当前位置:文档之家› 怎么写一个解释器

怎么写一个解释器

怎么写一个解释器
怎么写一个解释器

怎样写一个解释器

这是一篇解释器的入门教程。虽然我试图从最基本的原理讲起,尽量让这篇文章不依赖于其它知识,但是这篇教程并不是针对编程的入门知识,所以我假设你已经学会了最基本的Scheme 和函数式编程。我不是很推崇函数式编程,但它里面确实包含了很重要的一些方法。如果你完全不了解这些,可以读一下SICP 的第一,二章(或者接下去读The Little Schemer)。当然你也可以继续读这篇文章,有不懂的地方再去查资料。我在这里也会讲递归和模式匹配的原理。如果你已经了解这些东西,这里的内容也许可以加深你的理解。

解释器是一种简单却又深奥的东西,以至于好多人都不会写,或者自认为会写却又不真正的会写。在这个领域里有一些历史遗留下来的误解,以至于很少有人真正的知道如何写出正确的解释器。很多“语言专家”或者“逻辑学家”的解释器代码里面有各种各样的错误,却又以谬传谬,搞得无比复杂。这误解的渊源之深,真是一言难尽。

你必须从最简单的语言开始,逐步增加语言的复杂度,才能构造出正确的解释器。这篇文章就是告诉你如何写出一个最简单的语言(lambda calculus) 的解释器,并且带有基本的的算术功能,可以作为一个高级

计算器来使用。

一般的程序语言课程往往从语法分析(parsing)开始,折腾lex 和yacc 等麻烦却不中用的工具,解决一些根本不需要存在的问题。Parsing 的作用其实只是把字符串解码成程序的语法树(AST)结构。麻烦好久得到了AST 之后,真正的困难才开始!而很多人在写完parser 之后就已经倒下了。鉴于这个原因,这里我用所谓的“S-expression”来表示程序的语法树(AST)结构。S-expression (加上Lisp 对它发自“本能”的处理能力)让我们可以直接跳过parse 的步骤,进入关键的主题:语义(semantics)。

这里用的Scheme 实现是Racket。为了让程序简洁,我使用了Racket 的模式匹配(pattern matching)。我对Racket 没有特别的好感。但它安装比较方便,而且是免费的。如果你用其它的Scheme 实现的话,恐怕要自己做一些调整。

解释器是什么

首先我们来谈一下解释器是什么。说白了解释器跟计算器差不多。它们都接受一个“表达式”,输出一个“结果”。比如,得到'(+ 1 2) 之后就输出3。不过解释器的表达式要比计算器的表达式复杂一些。解释器接受的表达式叫做“程序”,而不只是简单的算术表达式。从本质上讲,每个程序都是一台机器的“描述”,而解释器就是在“模拟”这台机器的运转,也就是在进行“计算”。所以从某种意义上讲,解释器就是计算的本质。当然,不同的解释器就会带来不同的计算。

需要注意的是,我们的解释器接受的参数是一个表达式的“数据结构”,而不是一个字符串。这里我们用一种叫“S-exp ression”的数据结构来表示表达式。比如表达式'(+ 1 2) 里面的内容是三个符号:'+, '1 和'2,而不是字符串“(+ 1 2)”。从结构化的数据里面提取信息很方便,而从字符串里提取信息很麻烦,而且容易出错。

从广义上讲,解释器是一个通用的概念。计算器实际上是解释器的一种形式,只不过它处理的语言比程序的解释器简单很多。也许你会发现,CPU 和人脑,从本质上来讲也是解释器,因为解释器的本质实际上是“任何用于处理语言的机器”。

递归定义(recursive definition)

解释器一般都是“递归程序”。之所以是递归的原因,在于它处理的数据结构(程序)本身是“递归定义”的结构。算术表达式就是一个这样的结构,比如:'(* (+ 1 2) (* (- 9 6) 4))。每一个表达式里面可以含有子表达式,子表达式里面还可以有子表达式,如此无穷无尽的嵌套。看似很复杂,其实它的定义不过是:

“算术表达式”有两种形式:

1.一个数

2.一个'(op e1 e2)这样的结构(其中e1和e2是两个“算术表达

式”)

看出来哪里在“递归”了吗?我们本来在定义“算术表达式”这个概念,而

它的定义里面用到了“算术表达式”这个概念本身!这就构造了一个“回路”,让我们可以生成任意深度的表达式。

很多其它的数据,包括自然数,都是可以用递归来定义的。比如常见的对自然数的定义是:

“自然数”有两种形式:

1.零

2.某个“自然数”的后继

看到了吗?“自然数”的定义里面出现了它自己!这就是为什么我们有无穷多个自然数。

所以可以说递归是无所不在的,甚至有人说递归就是自然界的终极原理。递归的数据总是需要递归的程序来处理。虽然递归有时候表现为另外的形式,比如循环(loop),但是“递归”这个概念比“循环”更广泛一些。有很多递归程序不能用循环来表达,比如我们今天要写的解释器就是一个递归程序,它就不能用循环来表达。所以写出正确的递归程序,对于设计任何系统都是至关重要的。其实递归的概念不限于程序设计。在数学证明里面有个概念叫“归纳法”(induction),比如“数学归纳

法”(mathematical induction)。其实归纳法跟递归完全是一回事。

我们今天的解释器就是一个递归程序。它接受一个表达式,递归的调用它自己来处理各个子表达式,然后把各个递归的结果组合在一起,形成最后的结果。这有点像二叉树遍历,只不过我们的数据结构(程序)比二叉树复杂一些。

模式匹配和递归:一个简单的计算器

既然计算器是一种最简单的解释器,那么我们为何不从计算器开始写?下面就是一个计算器,它可以计算四则运算的表达式。这些表达式可以任意的嵌套,比如'(* (+ 1 2) (+ 3 4))。我想从这个简单的例子来讲一下模式匹配(pattern matching) 和递归(recursion) 的原理。

下面就是这个计算器的代码。它接受一个表达式,输出一个数字作为结果,正如上一节所示。

这里的match 语句是一个模式匹配。它的形式是这样:

它根据表达式exp 的“结构”来进行“分支”操作。每一个分支由两部分组成,左边的是一个“模式”,右边的是一个结果。左边的模式在匹配之后可能会绑定一些变量,它们可以在右边的表达式里面使用。

一般说来,数据的“定义”有多少种情况,用来处理它的“模式”就有多少情况。比如算术表达式有两种情况,数字或者(op e1 e2)。所以用来处理它的match语句就有两种模式。“你所有的情况,我都能处理”,这就是“穷举法”。穷举的思想非常重要,你漏掉的任何一种情况,都非常有可能带来麻烦。所谓的“数学归纳法”,就是这种穷举法在自然数的递归定义上面的表现。因为你穷举了所有的自然数可能被构造的两种形式,所以你能确保定理对“任意自然数”成立。

那么模式是如何工作的呢?比如'(,op ,e1 ,e2)就是一个模式(pattern),它被用来匹配输入的exp。模式匹配基本的原理就是匹配与它“结构相同”的数据。比如,如果exp是'(+ 1 2),那

么'(,op ,e1 ,e2)就会把op绑定到'+,把e1绑定到'1,把e2绑定到'2。这是因为它们结构相同:

说白了,模式就是一个可以含有“名字”(像op, e1和e2)的“数据结构”,像'(,op ,e1 ,e2)。我们拿这个带有名字的结构去“匹配”实际的数据(像'(+ 1 2))。当它们一一对应之后,这些名字就自动被绑定到实际数据里相应位置的值。模式里面不但可以含有名字,也可以含有具体的数据。比如你可以构造一个模式'(,op ,e1 42),用来匹配第二个操作数固定为42 的那些表达式。

看见左边的模式,你就像直接“看见”了输入数据的形态,然后对里面的元素进行操作。它可以让我们一次性的“拆散”(destruct) 数据结构,把各个部件(域)的值绑定到多个变量,而不需要使用多个访问函数。所以模式匹配是非常直观的编程方式,值得每种语言借鉴。很多函数式语言里都有类似的功能,比如ML 和Haskell。

注意这里e1和e2里面的操作数还不是值,它们是表达式。我们递归的调用interp1自己,分别得到e1和e2的值v1和v2。它们应该是数字。你注意到我们在什么地方使用了递归吗?如果你再看一下“算术表达式”的定义:

“算术表达式”有两种形式:

1.一个数

2.一个'(op e1 e2)这样的结构(其中e1和e2是两个“算术表达

式”)

你就会发现这个定义里面“递归”的地方就是e1和e2,所

以calc在e1和e2上面递归的调用自己。如果你在数据定义的每个递归处都进行递归,那么你的递归程序就会穷举所有的情况。

之后,我们根据操作符op的不同,对这两个值v1和v2分别进行操作。如果op是加号'+,我们就调用Scheme 的加法操作,作用于v1和v2,并且返回运算所得的值。如果是减号,乘号,除号,我们也进行相应的操作,返回它们的值。

所以你就可以得到如下的测试结果:

一个计算器就是这么简单。你可以试试这些例子,然后自己再做一些新的例子。

什么是lambda calculus?

现在让我们过渡到一种更强大的语言:lambda calculus。它虽然名字看起来很吓人,但是其实非常简单。它的三个元素分别是是:变量,函数,调用。用传统的表达法,它们看起来就是:

?变量:x

?函数:λx.t

?调用:t1 t2

每个程序语言里面都有这三个元素,只不过具体的语法不同,所以你其实每天都在使用lambda calculus。用Scheme 作为例子,这三个元

素看起来就像:

?变量:x

?函数:(lambda (x) e)

?调用:(e1 e2)

一般的程序语言还有很多其它的结构,可是这三个元素却是缺一不可的。所以构建解释器的最关键步骤就是把这三个东西搞清楚。构造任何一个语言的解释器一般都是从这三个元素开始,在确保它们完全正确之后才慢慢加入其它的元素。

有一个很简单的思维方式可以让你直接看到这三元素的本质。记得我说过,每个程序都是一个“机器的描述”吗?所以每个lambda calculus 的表达式也是一个机器的描述。这种机器跟电子线路非常相似。lambda calculus 的程序和机器有这样的一一对应关系:一个变量就是一根导线。一个函数就是某种电子器件的“样板”,有它自己的输入和输出端子,自己的逻辑。一个调用都是在设计中插入一个电子器件的“实例”,把它的输入端子连接到某些已有的导线,这些导线被叫做“参数”。所以一个

lambda calculus 的解释器实际上就是一个电子线路的模拟器。所以如果你听说有些芯片公司开始用类似Haskell 的语言(比如Bluespec System Verilog)来设计硬件,也就不奇怪了。

需要注意的是,跟一般语言不同,lambda calculus 的函数只有一个参数。这其实不是一个严重的限制,因为lambda calculus 的函数可以被作为值传递(这叫first-class function),所以你可以用嵌套的函数定义来表示两个以上参数的函数。比如,(lambda (x) (lambda (y) y))就

可以表示一个两个参数的函数,它返回第二个参数。不过当它被调用的时候,你需要两层调用,就像这样:

虽然看起来丑一点,但是它让我们的解释器达到终极的简单。简单对于设计程序语言的人是至关重要的。一开头就追求复杂的设计,往往导致一堆纠缠不清的问题。

lambda calculus 不同于普通语言的另外一个特点就是它没有数字等基本的数据类型,所以你不能直接用lambda calculus 来计算像(+ 1 2)这样的表达式。但是有意思的是,数字却可以被lambda calculus 的三个基本元素“编码”(encoding) 出来。这种编码可以用来表示自然数,布尔类型,pair,list,以至于所有的数据结构。它还可以表示if 条件

语句等复杂的语法结构。常见的一种这样的编码叫做Church encoding。

所以lambda calculus 其实可以产生出几乎所有程序语言的功能。中国的古话“三生万物”,也许就是这个意思。

求值顺序,call-by-name, call-by-value

当解释一个程序的时候,我们可以有好几种不同的“求值顺

序”(evaluation order)。这有点像遍历二叉树有好几种不同的顺序一样(中序,前序,后序)。只不过这里的顺序更加复杂一些。比如下面的程序:

我们可以先执行最外层的调用,把(+ 1 2) 传递进入函数,得到(* (+ 1 2) (+ 1 2))。所以求值顺序是:

但是我们也可以先算出(+ 1 2) 的结果,然后再把它传进这个函数。所以求值顺序是:

我们把第一种方式叫做call-by-name (CBN),因为它把参数的“名字”(也就是表达式自己)传进函数。我们把第二种方式叫做call-by-value (CBV),因为它先把参数的名字进行解释,得到它们的“值”之后,才把它们传进函数。

这两种解释方式的效率是不一样的。从上面的例子,你可以看出CBN 比CBV 多出了一步。为什么呢?因为函数(lambda (x) (* x x))里面有两个x,所以(+ 1 2)被传进函数的时候被复制了一份。之后我们需要对它的每一拷贝都进行一次解释,所以(+ 1 2)被计算了两次!鉴于这个原因,几乎所有的程序语言都采用CBV,而不是CBN。CBV 常常被叫做“strict”或者“applicative order”。虽然CBN 效率低下,与它等价的一种顺序call-by-need 却没有这个问题。call-by-need 的基本原理是对CBN 中被拷贝的表达式进行“共享”和“记忆”。当一个表达式的一个拷贝被计算过了之后,其它的拷贝自动得到它的值,从而避免重复求值。call-by-need 也叫“lazy evaluation”,它是Haskell 语言所用的语义。

求值顺序不只停留于call-by-name, call-by-value, call-by-need。人们还设计了很多种其它的求值顺序,虽然它们大部分都不能像

call-by-value 和call-by-need 这么实用。

完整的lambda calculus 解释器

下面是我们今天要完成的解释器,它只有39行(不包括空行和注释)。你可以先留意一下各个部分的注释,它们标注各个部件的名称,并且有少许讲解。这个解释器实现的是CBV 顺序的lambda calculus,外加基本的算术。加入基本算术的原因是为了可以让初学者写出比较有趣一点的程序,不至于一开头就被迫去学Church encoding。

测试例子

这里有一些测试的例子。你最好先玩一下再继续往下看,或者自己写一些新的例子。学习程序的最好办法就是玩弄这个程序,给它一些输入,观察它的行为。有时候这比任何语言的描述都要直观和清晰。

在接下来的几节,我们来看看这个解释器里主要的分支(match)表达式的各种情况。

对基本算术操作的解释

算术操作在解释器里是最简单也是最“基础”的东西,因为它们不能再被细分为更小的元素了。所以在接触函数,调用等复杂的结构之前,我们来看一看对算术操作的处理。以下就是这个解释器里处理基本算术的部分,它是interp1的最后一个分支。

你可以看到它几乎跟刚才写的计算器一模一样,不过现在interp1的调用多了一个参数env而已。这个env是什么,我们下面很快就讲。

变量和函数

我想用两个小节来简单介绍一下变量,函数和环境。稍后的几节我们再来看它们是如何实现的。

变量(variable)的产生是数学史上的最大突破之一。因为变量可以被绑定到不同的值,从而使得函数的实现成为可能。比如数学函数f(x) = x*2,其中x是一个变量,它把输入的值传递到函数的主体x*2里面。如果没有变量,函数就不可能实现。

对变量的最基本的操作是对它的“绑定”(binding)和“取值”(evaluate)。什么是绑定呢?拿上面的函数f(x)作为例子吧。当x等于 1 的时候,f(x)的值是2,而当x等于2 的时候,f(x)的值是4。在上面的句子里,我们对x进行了两次绑定。第一次x被绑定到了1,第二次被绑定到了2。你可以把“绑定”理解成这样一个动作,就像当你把插头插进电源插座的那一瞬间。插头的插脚就是f(x)里面的那个x,而x*2里面的x,则是电线的另外一端。所以当你把插头插进插座,电流就通过这根电线到达另外一端。如果电线导电性能良好,两头的电压应该几乎相等。有点跑题了…… 反正只要记住一点:绑定就是插进插座的那个“动作”。

那么“取值”呢?再想一下前面的例子,当我们用伏特表测电线另外一端的电压的时候,我们就是在对这个变量进行取值。有时候这种取值的过程不是那么明显,比如电流如果驱动了风扇的电动机。虽然电线的另外一头没有显示电压,其实电流已经作用于电动机的输入端子,进入线圈。所以你也可以说其实是电动机在对变量进行取值。

环境

我们的解释器是一个挺笨的程序,它只能一步一步的做事情。比如,当它需要求f(1)的值的时候,它做以下两步操作:1) 把x绑定到1; 2) 进入f的函数体对x*2进行求值。这就像一个人做出这两个动作:1)

把插头插进插座,2) 走到电线的另外一头测量它的电压,并且把结果乘以2。在第一步和第二步之间,我们如何记住x的值呢?它必须被传递到那个用来处理函数体的递归解释器里面。这就是为什么我们需要“环境”,也就是interp1的第二个参数env。

环境记录变量的值,并且把它们传递到它们的“可见区域”,用术语说就叫做“作用域”(scope)。通常作用域是整个函数体,但是有一个例外,就是当函数体内有嵌套的函数定义的时候,内部的那个函数如果有同样的参数名,那么外层的参数名就会被“屏蔽”(shadow)掉。这样内部的函数体就看不到外层的参数了,只看到它自己的。比如(lambda (x) (lambda (x) (* x 2))),里面的那个x看到的就是内层函数的x,而不是外层的。

在我们的解释器里,用于处理环境的主要部件如下:

这里我们用的是Scheme 的association list 来表示环境。Association list 看起来像这个样子:((x . 1) (y . 2) (z . 5))。也就是一个两元组(pair)的链表,左边的元素是key,右边的元素是value。写的直观一点就是:

查表操作就是从头到尾搜索,如果左边的key 是要找的变量,就返回整个pair。简单吧?

ext-env扩展一个环境。比如,如果原来的环境是((y . 2) (z . 5))那么(ext-env x 1 ((y . 2) (z . 5))),就会得到((x . 1) (y . 2) (z .

5))。也就是把(x . 1)放到最前面去。值得注意的一点是,环境被扩

展以后其实是形成了一个新的环境,原来的环境并没有被“改变”。比如上面红色的部分就是原来的数据结构,只不过它被放到另一个更大的结构里面了。这叫做“函数式数据结构”。这个性质在我们的解释器里是至关重要的,因为当我们扩展了一个环境之后,其它部分的代码仍然可以原封不动的访问扩展前的那个旧的环境。当我们讲到调用的时候也许你就会发现这个性质的用处。

你也可以用另外的,更高效的数据结构(比如splay tree)来表示环境。你甚至可以用函数来表示环境。唯一的要求就是,它是变量到值的“映射”(map)。你把x映射到1,待会儿查询x的值,它应该仍然是1,而不会消失掉或者别的值。也就是说,这几个函数要满足这样的一种“界面约定”:如果e是(ext-env 'x 1 env)返回的环境,那么(lookup 'x e)应该返回1。只要满足这样的界面约定的函数都可以被叫

做ext-env和lookup,以至于可以它们用来完全替代这里的函数而不会导致其它代码的修改。这叫做“抽象”,也就是“面向对象语言”的精髓所在。

对变量的解释

了解了变量,函数和环境,让我们来看看解释器对变量的操作,也就

是interp1的match的第一种情况。它非常简单,就是在环境中查找变量的值。这里的(? symbol? x)是一个特殊的模式,它使用Scheme 函

数symbol?来判断输入是否匹配,如果是的就把它绑定到x,查找它的值,然后返回这个值。

注意由于我们的解释器是递归的,所以这个值也许会被返回到更高层的表达式,比如(* x 2)。

对数字的解释

对数字的解释也很简单。由于在Scheme 里面名字'2 就是数字2(我认为这是Scheme 设计上的一个小错误),所以我们不需要对数字的名字做特殊的处理,把它们原封不动的返回。

对函数的解释

对函数的解释是一个比较难说清楚的问题。由于函数体内也许会含有外层函数的参数,比如(lambda (y) (lambda (x) (* y 2)))里面的y是外层函数的参数,却出现在内层函数定义中。如果内层函数被作为值返回,那么(* y 2)就会跑到y的作用域以外。所以我们必须把函数做成“闭包”(closure)。闭包是一种特殊的数据结构,它由两个元素组成:函数的定义和当前的环境。所以我们对(lambda (x) e)这样一个函数的解释就是这样:

习题07_Shell编程

习题07_Shell编程 一、单选 1. 下面哪个命令是用来定义shell的全局变量( D )。 A. exportfs B. alias C. exports D. export 2.在Shell脚本中,用来读取文件内各个域的内容并将其赋值Shell变量的命令是(D )。 A. fold B. join C. tr D. read 3. 当字符串用双引号(’’)括起来时,SHELL将(C )。 A. 解释引号内的特殊字符 B. 执行引号中的命令 C. 不解释引号内的特殊字符 D. 结束进程 4. 以下哪个环境变量表示当前路径( B )。 A. PATH B. PWD C. HOME D. ROOT 5. shell不仅是(用户命令的解释器),它同时也是一种功能强大的编程语言,(bash )是Linux的缺省shell。 6. 编写的Shell程序运行前必须赋予该脚本文件(执行)权限。 7. 对于Shell脚本程序,若输入参数数量多于9个,则程序遍历每个参数可通过使用(shift )命令实现。 8. 为脚本程序指定执行权的命令及参数是( chmod a+x filename )。 9. 在shell编程时,使用方括号表示测试条件的规则是:方括号两边必有(空格)。 10. 输入了Shell命令的部分字符后按( tab )键可补全其余部分。 11.改变命令提示符的环境变量是(PS1 )。 11. grep -E '[Hh]enr(y|ietta)' file的功能是:(在文件File中查找Henry、henry、Henrietta或henrietta)。 12. 如下为命令终端下的一个截图: 则,以下两句的执行结果是:(the home path is $HOME )。 path=‘the home path is $HOME’ echo $path 13. 以下为程序名为prog的程序内容。则sh prog a b c d的执行结果为( C )。 set A B C D shift shift echo $1 三、简答 1. 下面给出了一个SHELL程序,试对其行后有#(n)形式的语句进行解释,并说明程序完成的功能。

第七章 图形化编程软件平台

举手之劳发信人: ilyfe (伊犁*飞), 信区: LabVIEW 标题: 第七章图形化编程软件平台 发信站: 饮水思源(2003年05月04日13:24:43 星期天), 站内信件 第七章图形化编程软件平台 图形化编程软件平台作为虚拟仪器应用程序的开发环境,具有编程简单、使用方便等优势,本章介绍了图形化编程软件平台的特点,重点介绍了浙江大学数字技术及仪器研究所自主开发的图形化编程软件平台VPP的设计思路与应用。 7.1 图形化编程软件平台的特点 自动测试系统应用程序开发环境可以选择传统文本形式的语言环境,如BC、VC、VB、Delphi 或LabWindows/CVI等,也可以选择图形化的软件开发环境。被称为快速应用程序开发环境(RAD)的VC、VB、Delphi等开发平台具有可视化界面与已经以类形式封装的可视化控件,在很大程度上编程也不需要从头开始,但是,它们的源代码从本质上还是一行行的文本代码,编程还是包含了许多技巧。对于一个仪器工程师而言,在进行测试系统应用程序设计与调试时,往往会将精力与时间过多地停留于语言调试本身,而很难集中时间与精力在真正系统集成任务上,工作量大,难度也大。为了让仪器工程师从繁重的编程任务中走出来,回归到他们本职工作上去,业界推出了图形化语言的概念。在现实的生活中和科学研究过程中,人们常常以框图来描述事物。框图不仅可以描述事物的位置、大小,更为重要的是,它可以描述事物的运行过程。在计算机上以二维平台上的框图描述一个程序的结构和运行过程,就形成了图形化语言。简略地说,一个图形化语言是指一个计算机系统,其执行过程可由二维平台上的框图来描述。与一些框图绘制系统不同的是,图形化语言需要满足两个规范: 1、图形化语言中的框图必须是可运行的。就是说,与那些静态的流程框图绘制软件和图像处理软件不同,图形化语言中的框图描述的是系统的运行过程,并且每个部分在运行中会执行一定的操作。 2、框图可以动态修改,这里的修改不只是设定一些参数或变量值,框图编辑环境可以修改程序的结构和运行的流程。因此,那些基于文本的具有一定图形化特性的编程语言如VB、VC、Delphi等,都不是图形化语言,它们只是文本语言的图形化环境或者称为支持可视化的环境。那些用于描述图形结构和内容的语言如Postscript也不是图形化语言。绘图软件也不是图形化语言,因为它们并不解释图形所代表的意义。图形化语言是图形化计算机领域中的一个重要分支。为了与其它一些图形化系统相区别,在一些文献中称图形化语言为图形化

24种设计模式的定义和使用场合

一.创建型模式(Creational): 简单工厂模式(simpleFactory)发音:['simpl] ['f?kt(?)r?] 定义: 提供一个创建对象实例的功能,而无须关心其具体实现.被创建实例的类型可以是接口,抽象类,也可以是具体的类. 1.抽象工厂(AbstractFactory)发音: ['?bstr?kt] 定义: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类. 使用场合: 1.如果希望一个系统独立于它的产品的创建,组合和表示的时候,换句话书,希望一个系统只是知道产品的接口,而不关心实现的时候. 2.如果一个系统要由多个产品系列中的一个来配置的时候.换句话说,就是可以,就是可以动态地切换产品簇的时候. 3.如果强调一系列相关产品的接口,以便联合使用他们的时候 2.建造者模式(Builder)发音: ['b?ld?] 定义: 将复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 使用场合: 1.如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时 2.如果同一个构建过程有着不同的表示时 3.工厂方法模式(Factory Method) 定义: 为创建对象定义一个接口,让子类决定实例化哪个类.工厂方法让一个类的实例化延迟到了子类. 使用场景: 1.客户类不关心使用哪个具体类,只关心该接口所提供的功能. 2.创建过程比较复杂,例如需要初始化其他关联的资源类,读取配置文件等. 3.接口有很多具体实现或者抽象类有很多具体子类时, 4.不希望给客户程序暴露过多的此类的内部结构,隐藏这些细节可以降低耦合度. 5.优化性能,比如缓存大对象或者初始化比较耗时的对象. 4.原型模式(Prototype Method)发音: ['pr??t?ta?p] 定义: 使用原形实例指定将要创建的对象类型,通过复制这个实例创建新的对象. 应用场合: 1.如果一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程,在系统需要新的对象的时候,可以通过克隆原型来得到. 2.如果需要实例化的类是在运行时刻动态指定时,可以使用原型模式,通过克隆原型来得到需要的实例.

interp__linux命令中文解释.html

器。还有,没有通过它可以引用在应用中建立的第一个解释器的全局名字。这两种限制的目的都是为了安全。 INTERP 命令 COMMAND COMMAND COMMAND 使用 interp interp interp 命令建立、删除、和操纵从解释器,并在解释器之间共享或转换通道。依赖于 option 参 数,它可以有下列一些形式: interp interp alias alias alias srcPath srcCmd 返回一个 Tcl 列表,它的元素是与叫做 srcCmd 的别名有关的 targetCmd 和 arg s(在建立别名时指定所有这些值;在从解释器中实际的源命令如果被重命名的话可能与 srcCmd 不同)。 interp interp alias alias alias srcPath srcCmd {}{}{} 删除在从解释器中用 srcPath 标识的给 srcCmd 的别名。srcCmd 引用在其下建立别名的名字;如果 源命令已经被重命名,则删除重命名后的命令。 interp interp alias alias alias srcPath srcCmd targetPath targetCmd ?arg arg ...? 这个命令在一个从解释器和其他解释器之间建立一个别名(关于在一个从解释器和它的主解释器之间建立别名请参见下面的 alias alias alias 从命令)。在这个命令中,两个从解释器可以在调用这个命令的解释器底下的解释器层次中的任何位置。SrcPath 和 srcCmd 标识这个别名的来源。SrcPath 是一个 Tcl 列表,它的元素选择一个特定的解释器。例如,“a b ”标识一个解释器 b b ,它是解释器 a a 的一个从解释器,a 解释器是调用(命令)的解释器的一个从解释器。一个空列表指定调用这个命令的解释器。srcCmd 给出一个新命令的名字,将在源解释器中建立它。TargetPath 和 targetCmd 指定一个目标解释器和命令,和 arg 参数,如果有的话,给 targetCmd 指定增补的参数,它们在 srcCmd 调用中指定的所有参数的前面。TargetCmd 在这个调用的时候可以被取消定义(undefine)了,或者它已经存在了;它不由这个命令来建立。别名安排在源解释器中调用给定源命令的时候在目标解释器中调用给定目标命令。详情参见下面的 ALIAS INVOCATION (别名调用)章节。 interp interp aliases aliases aliases ?path ? 这个命令返回给在用 path 表示的解释器中定义的别名的所有源命令的名字一个 Tcl 列表。 interp interp create create create ?--safe safe? ?------? ?path ? 建立用 path 标识的一个从解释器和叫做从命令(slave command )的一个新命令。从命令的名字是 path 的最后一个成员。在其中建立新的从解释器和从命令的解释器由从 path 中去除最后一个成员所获得的路径来标识。例如,如果 path 是 a b c a b c 则一个新的从解释器和叫做 c c 的从命令建立在用路径 a b a b 标识的从解释器中。可以使用从命令先下面描述的那样操纵新解释器。如果省略了 path , Tcl 建立 interp interp x 形式的一个唯一的名字,这里的 x 是一个整数,并用于解释器和从命令。如果指定了 --safe 开关(或者主解释器是一个安全解释器),新的从解释器将建立成功能有限的一个安全解释器;否则从解释器将包含 Tcl 内置命令和变量的全集。使用 ------ 开关来标记开关的结束;如果路径 是象 --safe safe 这样的一个特殊的值的时候需要这个开关。这个命令的结果是新解释器的名字。一个从解释器的名字在它的主解释器的所有从解释器中必须是唯一的;如果在这个主解释器中用给定名字(标识)的一个从解释器已经存在则发生一个错误。 interp interp delete delete delete ?path ...? 删除用可选的 path 参数给出的零个或多个解释器,并且对于每个解释器,它还删除它的所有从解释器。这个命令还删除给每个被删除的解释器的从命令。对于每个 path 参数,如果叫这个名字的解释器不存在,这个名字将引发一个错误。 interp interp eval eval eval path arg ?arg ...? 这个命令用与 concat concat 命令相同的方式串联所有的 arg 参数,接着在用 path 标识的解释器中把结果字符串作为一个 Tcl 脚本来求值。把这个求值的结果(如果发生错误的话,包括象 errorInfo errorInfo 和 errorCode errorCode 变量这样的错误信息)返回给调用(命令)的解释器。 interp exists path 如果在这个主解释器中存在用 path 指定的从解释器则返回 11,否则返回 00。如果省略了 path ,使用调用(命令)的解释器。 interp expose path hiddenName ?exposedCmdName ? 在用 path 表示(denote)的解释器中,使隐藏的命令 hiddenName 暴露(expose),最终把它带回在一个新的exposedCmdName 名字之下(目前只接受没有任何:: 的一个全局名字空间名字)。如果有目标名字的一个暴露的命令已经存在,这个命令失败。隐藏命令的详情参见下面的HIDDEN COMMANDS (隐藏命令)章节。 interp interp hide hide hide path exposedCmdName ?hiddenCmdName ? 在用 path 表示(denote)的解释器中,使暴露的命令 exposedCmdName 隐藏,并把它重命名成隐藏命令 hiddenCmdName ,如果未给出 hiddenCmdName 则保持相同的名字。如果有目标名字的一个隐藏的命令已经存在,这个命令失败。目前 exposedCmdName 和 hiddenCmdName 二者不能不能包含名字空间限

最全批处理命令学习资料【完美版】

一:批处理文件 批处理文件是扩展名为·bat或·cmd的文本文件,包含一条或多条命令,由DOS或Windows系统内嵌的命令解释器来解释运行。 批处理的本质,是一堆DOS命令按一定顺序排列而形成的集合。 二:常用命令: 1.echo 和 @: @ #关闭单行回显 echo off #从下一行开始关闭回显 @echooff #从本行开始关闭回显,一般批处理第一行都是这个echo on #从下一行开始打开回显 echo #显示当前是echo off 状态还是 echo on 状态echo. #输出一个”回车换行”,空白行 #(同echo, echo; echo+ echo[ echo]echo /echo") 2.errorlevel echo???%errorlevel% 每个命令运行结束,可以用这个命令行格式查看返回码 默认值为0,一般命令执行出错会设 errorlevel 为1 目录操作类命令: 3.dir?显示磁盘目录命令 dir #显示当前目录中的文件和子目录 dir /a #显示当前目录中的文件和子目录,包括隐藏文件和系统文件 dirc:/a:d #显示 C 盘当前目录中的目录 dir c: /a:-d #显示 C 盘根目录中的文件 dir c:" /b/p #/b只显示文件名,/p分页显示 dir c:" /b/p#/w不显示详细信息 dir *.exe /s #显示当前目录和子目录里所有的.exe文件 4.以树形显示当前路径文件夹结构 tree /f #显示每个文件夹中文件的名字。 5.md 建立子目录 md d:"a"b"c#如果 d:"a 不存在,将会自动创建中级目录 #如果命令扩展名被停用,则需要键入mkdir "a" b"c 6.cd?改变当前目录(进入) cd \#进入根目录 cd#显示当前目录 cd /d d:\sdk #可以同时更改盘符和目录 cd .. #返回上一级目录

基于FlexBison的高级解释器设计及实现

课程设计3 基于Flex/Bison的高级解释器设计及实现 3.1 需求分析 3.1.1 问题定义 1.使用flex和bison开发了一个具有全部功能的桌面计算器,能够支持变量,过程, 循环和条件表达式,使它成为一个虽然短小但是具有现实意义的编译器。 2.重点学习抽象语法树的用法,它具有强大而简单的数据结构来表示分析。 3.1.2 功能描述 1.计算器具体需要实现的功能: a)变量命名; b)实现赋值功能; c)实现比较表达式(大于、小于、等于等等) d)实现if/then/else和do/while的流程控制; e)用户可以自定义函数; f) 简单的错误恢复机制。 2. 编写 Flex/Bison源文件,实现C 语言的语法分析功能,最后上机调试。 3. 要求编写一个测试程序: 首先自定义两个函数sq和avg,sq函数使用Newton方法来迭代计算平方根;avg函数计算两个数值的平均值。 利用定义好的函数进行计算,得到计算结果并显示出来。 4.根据习题1的要求,修改fb3-2相关代码; 实现实现以下自定义函数,并保存为fb3-3。 函数示例: let sq(n){e=1; while (|((t=n/e)-e)>.001) do {e=avg(e,t);}} let avg(a,b){(a+b)/2;} let max(a,b) { if(a>b) then a; else b; } let max3(a,b,c) { if(a>b) then { if(a>c) then a; else c; }

else { if(b>c) then b; else c; } } 3.1.3 开发环境及工具介绍 1、Window环境下载Visual Studio之后,利用其命令提示窗口进行操作。下载并安装Flex。 2、vs2010的编译器cl.exe。 3、flex:词法分析器 Flex是用来生成程序的工具,他们所生成的程序能够处理结构化输入,最初的Flex是用来生成编译器的,但是后来他们被证明在其他领域也非常有效。Flex是一个SourceForge项目。其依赖于GNU m4宏处理器。Linux和BSD都应该有m4,对于Windos用户来说,Flex被包含在Cygein Linux模拟环境中。 什么是FLEX?它是一个自动化工具,可以按照定义好的规则自动生成一个C 函数yylex(),也成为扫描器(Scanner)。这个C函数把文本串作为输入,按照定义好的规则分析文本串中的字符,找到符合规则的一些字符序列后,就执行在规则中定义好的动作(Action)。例如在规则中可以这样定义:如果遇到一个换行字符\n,那么就把行计数器的值加一。Flex文件就是一个文本文件,内容包括定义好的一系列词法规则。 4、bison:语法分析器 GNU bison 是属于 GNU 项目的一个语法分析器生成器。Bison 把一个关于“向前查看从左到右最右”(LALR) 上下文无关文法的描述转化成可以分析该文法的 C 或 C++ 程序。它也可以为二义文法生成“通用的从左到右最右”(GLR)语法分析器。 Bison是一种通用目的的分析器生成器。它将LALR(1)上下文无关文法的描述转化成分析该文法的C程序。一旦你精通Bison,你可以用它生成从简单的桌面计算器到复杂的程序设计语言等等许多语言的分析器。 Bison 基本上与 Yacc 兼容,并且在 Yacc 之上进行了改进。它经常和Flex (一个自动的词法分析器生成器)一起使用。 此软件的源代码是可自由获得的,在 GPL 下发布。

【模式】32设计模式

【关键字】模式 一、设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下: 二、设计模式的六大原则 总原则:开闭原则(Open Close Principle) 开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类等,后面的具体设计中我们会提到这点。 1、单一职责原则 不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不然,就应该把类拆分。 2、里氏替换原则(Liskov Substitution Principle) 里氏代换原则(Liskov Substitution Principle LSP)面向东西设计的基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科 历史替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。 3、依赖倒转原则(Dependence Inversion Principle) 这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。 4、接口隔离原则(Interface Segregation Principle) 这个原则的意思是:每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。 5、迪米特法则(最少知道原则)(Demeter Principle)

server常见命令

实验10 Windows 2003 server常见命令 【实验目的】 1.通过本实验,学生可以使用Windows 2003提供的命令实现网络故障的检测和日常管理。【实验内容】 1.利用Windows 2003提供的命令,进行IP地址配置 2.利用Windows 2003提供的命令,测试网络的连通性 3 利用Windows 2003提供的命令,设置文件夹共享 【实验器材】 1.硬件:服务器1台、工作站2台、交换机1台。 2.软件:windows 2003 server操作系统1套、其它Windows操作系统1套。 【预备知识】 在Windows 2003 server的网络环境下,出现网络故障时,我们通常要通过命令来进行故障检查,下面就介绍几个常见的命令。 1.CMD 说明:打开一个新的命令解释器。 执行方法:在点“开始”→“运行”,在命令行中输入“CMD”。 2.Hostname 说明:显示当前计算机的名称。 语法:hostname 3.Ipconfig 说明:显示所有的TCP/IP配置值,如本机IP地址、默认网关、子网掩码、DNS服务器等。 语法: Ipconfig [/all | /renew | /reelease] /All完整显示TCP/IP配置值 /Release释放DHCP配置参数 /Renew重建DHCP配置参数 4.Nbtstat 说明:显示协议统计和当前使用的NBT的TCP/IP连接,用此命令可以查看指定计算机的MAC

地址。 语法: nbtstat [-a 计算机名] [ -A 计算机的IP地址] 例如,命令: nbtstat –A 192.168.0.1 显示局域网内远端IP地址为192.168.0.1的计算机的网络信息,如MAC地址。 5.Net 说明:这条命令允许用户通过命令提示符对网络、用户、组和服务的很多方面进行监控、启动、停止和更改。如可以在网络中发送消息、查看本机的共享资源的详细资料。 1) Net send :向用户或计算机发送消息。 语法: Net send <计算机名/用户名>消息内容 2) Net share:创建/删除/显示共享 语法: Net share [共享名/d ] /[共享名=共享文件夹路径] 例如,命令: Net share mysharefile=d:\file 将本机目录“d:\file”设置共享,共享名为mysharefile。 6.Ping 说明:检验TCP/IP连接和显示连接统计。用此命令可以检测与目标计算机的TCP/IP的连通性,也可以在知道目标计算机的名称或域名的情况下,来查看其IP地址。 语法: Ping [-t ] [-a] 目标计算机名或IP地址。 -t Ping直到中断 -a 解析地址到计算机名。 7.Tracert 说明:通过TCP/IP跟踪目的地的路由。 语法: Tracert [-d] 目标计算机名 -d 不解析地址到计算机名。

[P语言] 普乐P语言入门及示例

[P语言] 普乐P语言入门及示例 如果把网页源代码想象成是妈妈织的毛衣,那么p语言就是钩针。只要我们懂一些语法代码或编程基础,那么就可以使用p语言。如其他编程语言一样,P语言也要遵循语言格式,否则写错了就获取不到结果。所以童鞋们学习p语言需要有点小耐心哦。:) p语言所获取的代码,如在正文代码条目中的p语言代码片段,保存后,经过正文代码过滤(过滤网页代码),经过过滤规则区,输出为最终结果。所以需要注意数据处理的顺序。 补充:普乐软件2354升级了并简化了采集规则p语言处理,可以对任意上一步获取内容进行p语言处理,返回值统一用$m_return表示,详情请见第六个示例,以前的用法仍然有效, 举个简单的例子,格式如下 1.void main(void) 2.{ 3.int tem; // 定义tem为整数型 4.cstr tep; //定义tep为字符型 5.tem = 10; //tem 赋值 10 6.tep = $m_url; //tep 赋值为当前网页的网址 7.$m_ncontent = tep + tem; // 获取正文为网址+10 8.} 复制代码 注意,因为$m_ncontent 是全局变量“文章内容”,所以这段代码一般应该写在规则编辑器--正文代码里面,如果写到其他地方“作者、类型、头像、自定义”会干预到正文的最终结果; 同理,如果写成下面这样,一般则表示获取获取地址,包括:下一页地址,下一正文页地址,登录地址,退出地址,注册地址,对应规则编辑器--地址关键词里面。 1.void main(void) 2.{ 3.int tem; 4.cstr tep; 5.tem = 10; 6.tep = $m_url; 7.$m_pageurl = tep + tem; 8.} 复制代码 以上2个例子,可以了解p语言的格式和适用范围,每句命令最后必须要有分号,目前暂不支持在同时混用规则代码和p命令,比如在下一页地址里面设置如下代码是错误的: 1.>下一页< //规则解释器命令,以下是p语言片段

软件体系结构与设计模式笔记

第1章软件体系结构概述 ?SEI软件体系结构讨论群定义如下:一个程序/系统构件的结构,它们之间的相互关系,以及在设计和交付的整个过程中的原则和指导方针。 ?Mary Shaw和David Garlan认为软件体系结构包括构成系统的设计元素的描述,设计元素的交互,设计元素组合的模式,以及在这些模式中的约束。 ?软件体系结构包括构件(Component)、连接件(Connector)和约束(Constrain)或配置(Configuration)三大要素。 ?国内普遍接受的定义:软件体系结构包括构件、连接件和约束,它是可预制和可重构的软件框架结构。 ?构件是可预制和可重用的软件部件,是组成体系结构的基本计算单 元或数据存储单元 ?连接件也是可预制和可重用的软件部件,是构件之间的连接单元 ?构件和连接件之间的关系用约束来描述 ?软件体系结构= 构件+ 连接件+ 约束 软件体系结构的优势容易理解、重用、控制成本、可分析性 第2章软件体系结构风格 ?软件体系结构风格是描述某一特定应用领域中系统组织方式的惯用模式。 ?体系结构风格定义了一个系统家族,即一个体系结构定义一个词汇表和一组约束。 词汇表中包含一些构件和连接件类型,而这组约束指出系统是如何将这些构件和连接件组合起来的。 ?体系结构风格反映了领域中众多系统所共有的结构和语义特性,并指导如何将各个模块和子系统有效地组织成一个完整的系统。 ?数据流风格: 批处理序列; 管道/过滤器。 ?调用/返回风格:主程序/子程序;面向对象风格;层次结构。 ?独立构件风格:进程通讯;事件系统。 ?虚拟机风格:解释器;基于规则的系统。 ?仓库风格:数据库系统;超文本系统;黑板系统。 ?过程控制环路 ?C/S风格体系结构有三个主要组成部分:数据库服务器、客户应用程序和网络。 ?B/S风格浏览器/Web服务器/数据库服务器。 优点:C/S体系结构具有强大的数据操作和事务处理能力,模型思想简单,易于人们理解和接受。将大的应用处理任务分布到许多通过网络连接的低成本计算机上,以节约大量费用。缺点:开发成本较高、客户端程序设计复杂、信息内容和形式单一、用户界面风格不一,使用繁杂不利于推广使用、软件移植困难、软件维护和升级困难、新技术不能轻易应用 优点:基于B/S体系结构的软件,系统安装、修改和维护全在服务器端解决。 缺点:B/S体系结构缺乏对动态页面的支持能力,没有集成有效的数据库处理功能。 B/S体系结构的系统扩展能力差,安全性难以控制。 采用B/S体系结构的应用系统,在数据查询等响应速度上,要远远低于C/S体系结构。 B/S体系结构的数据提交一般以页面为单位,数据的动态交互性不强,不利于在线事务处理(OLTP)应用。 第3章软件需求与架构 ?需求的基本概念 ?IEEE (1997) ?(1) 用户解决问题或达到目标所需的条件或能力

命令行解释器使用命令语法

使用命令语法 语法按必须键入命令及其带有参数的顺序出现。下面的xcopy 命令范例显示各种语法文本格式: xcopy Source [Destination] [/w] [/p] [/c] [/v] [/q] [/f] [/l] [/g] [/d[:mm-dd-yyyy]] [/u] [/i] [/s [/e]] [/t] [/k] [/r] [/h] [{/a|/m}] [/n] [/o] [/x] [/exclude:file1[+[file2]][+[file3]] [{/y|/-y}] [/z] 下表说明如何解释不同的文本格式。 格式化图例 格式含义 斜体用户必须提供的信息 粗体用户必须像显示的一样准确键入的元素 省略号(...) 可在命令行中重复多次的参数在括号([]) 之间可选项目 在大括号({}) 之间;将选项用竖线(|) 隔开。例如:{even|odd} 用户必须从中只选择一个选项的选项组 Courier 字体代码或程序输出 使用多个命令和条件处理符号 使用条件处理符号可以从单个命令行或脚本运行多个命令。通过条件处理符号运行多个命令时,条件处理符号右边的命令根据条件处理符号左边的命令结果来发挥作用。例如,只有在前一个命令失败的情况下,才可能需要运行一个新命令。或者,只有在前一个命令成功时才可能需要运行一个新命令。 可以使用下表列出的特殊字符来传递多个命令。 字符语法定义 & [...] command1 & command2 用来分隔一个命令行中的多个命令。Cmd.exe 运行第一个命令,然后运行第二个命令。 && [...] command1 && command2 只有在符号&& 前面的命令成功时,才用于运行该 符号后面的命令。Cmd.exe 运行第一个命令,然后 只有在第一个命令运行成功时才运行第二个命令。 || [...] command1 || command2 只有在符号|| 前面的命令失败时,才用于运行符号|| 后面的命令。Cmd.exe 运行第一个命令,然后只有在第一个命令未能运行成功(接收到大于零的错误代码)时才运行第二个命令。 ( ) [...] (command1 & command2) 用来分组或嵌套多个命令。 ; 或 者, command1 parameter1;parameter2 用来分隔命令参数。 注意 “与”符号(&)、管道符号(|) 以及括号() 是特殊字符,将它们作为参数传递时,必须在其前面加上转义字符(^) 或引号。 如果某个命令成功完成操作,则该命令就返回零(0) 退出代码或不返回任何退出代码。有关退出代码的详细信息,请参阅Microsoft Windows Resource Kit。 嵌套命令行解释器 通过在命令提示符打开Cmd.exe 新的实例,可以在Cmd.exe 内嵌套命令行解释器。默认

23种设计模式的通俗理解

23种设计模式的通俗理解【转】 1、FACTORY 工厂方法 追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory 工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。 2、BUILDER 抽象工厂 MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱 你”builder。(这一定比美军在伊拉克用的翻译机好卖)建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 3、FACTORY METHOD 建造者模式 请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。 4、PROTOTYPE 原型模式 跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要)原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 5、SINGLETON 单态模式 俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事) 单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。[b:9ceca65206]结构型模式[/b:9ceca65206] 6、ADAPTER 适配器模式 在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我) 适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,

操作系统shell命令解析器

一.程序概述 1.完成的任务 在Linux环境下编写一个简单的命令解释器即定义一个命令使它具有Linux中某些 命令的功能,可以实现获取用户输入的指令、可解析指令、可执行命令等功能,以 此来熟悉Linux编程环境,加强对Linux命令的理解及函数的运用。 2.遇到的困难及解决的问题 在程序设计过程中,遇到的困难主要有以下几个:一、系统函数调用问题;二、对 文件的操作;三、复制文件。 通过查阅Linux c函数库后,知道了如何调用系统函数,并解决了对文件的操作;而 文件的复制,则采用了如下思想:打开一个输入文件,创建一个输出文件,建立一 个BUF大小的缓冲区;然后在判断输入文件未完的循环中,每次读入多少就向输出 文件中写入多少,直到输入文件结束。 二.概念原理 1.基本概念 为用户提供了输入命令和参数,以此向Linux内核发送请求以便运行程序的界面系 统级程序。下图为Linux、UNIX系统层次结构图: 2.原理 解释用户输入的命令行,提交到系统内核处理,并将结果返回给用户。 三.详细设计 1.总体设计 运行程序后,首先初始化,显示系统有的命令,然后通过用while(1)循环,连续的 获取用户输入的命令,一直到用户输入quit退出循环,接着程序结束。 2.主要函数及功能 2.1 int init() 显示系统命令等信息。 int init()//初始化 { cout<<"*****************欢迎使用GPLinux*********************"<

shell命令解释器实验报告

实验报告实验名称:实现一个shell命令解释器 学员: 学号: 年级: 专业:所属学院:计算机学院指导教员:职称: 实验室:实验日期:

目录 1.功能描述 (3) 2.主要数据结构 (3) 3.主要程序流程图 (4) 4.主要功能实现方法和系统调用 (4) 4.1初始化环境 (4) 4.2打印提示符,获取用户输入 (5) 4.3解析命令 (5) 4.4执行命令 (5) 4.4.1内部命令 (5) 4.4.2外部命令 (5) 4.4.3重定向功能 (6) 4.4.4管道功能 (6) 5.测试结果 (7) 6.心得体会 (10)

1.功能描述 本实验完成了一个shell命令解释器,实现了shell的解释命令功能,实现了内部命令(包括自定义命令)、外部命令、重定向功能和多管道等功能。具体功能描述如下: 1)内部命令: ●可以使用常用的如cd、echo、history、exit等命令 ●自定义命令 1)smile命令:打印出笑脸 2)myinfo命令:打印出作者信息和版本信息 2)外部命令:可实现cp、rm等所有外部命令。 3)重定向:通过输入重定向符号’<’ 或输出重定向’>’ ,把一行命令分成 两部分,前者为需要执行的命令,后者为一个重定向文件。输入重定向 是把文件内容作为输入传到前面的命令中,而输出重定向则是把命令的 结果传入重定向文件中。 4)管道:通过管道符号’|’ 把一条命令分成两部分,前一部分命令运行后, 将结果放入管道,后一部分命令从管道中取出该结果,作为输入继续执 行。最多可以实现10个管道。 2.主要数据结构 本程序主要使用字符数组进行命令、路径的存储与分析。

最全批处理命令学习资料【完美版】

一:批处理文件 批处理文件是扩展名为·bat或·cmd的文本文件,包含一条或多条命令,由DOS或Windows系统内嵌的命令解释器来解释运行。 批处理的本质,是一堆DOS命令按一定顺序排列而形成的集合。 二:常用命令: 1.echo 和@: @ #关闭单行回显 echo off #从下一行开始关闭回显 @echo off #从本行开始关闭回显,一般批处理第一行都是这个 echo on #从下一行开始打开回显 echo #显示当前是echo off 状态还是echo on 状态 echo. #输出一个”回车换行”,空白行 #(同echo, echo; echo+ echo[ echo] echo/ echo") 2.errorlevel echo %errorlevel% 每个命令运行结束,可以用这个命令行格式查看返回码 默认值为0,一般命令执行出错会设errorlevel 为1 目录操作类命令: 3.dir 显示磁盘目录命令 dir #显示当前目录中的文件和子目录 dir /a #显示当前目录中的文件和子目录,包括隐藏文件和系统文件

dir c: /a:d #显示C 盘当前目录中的目录 dir c: /a:-d #显示C 盘根目录中的文件 dir c:" /b/p #/b只显示文件名,/p分页显示 dir c:" /b/p #/w不显示详细信息 dir *.exe /s #显示当前目录和子目录里所有的.exe文件 4.以树形显示当前路径文件夹结构 tree /f #显示每个文件夹中文件的名字。 5.md 建立子目录 md d:"a"b"c #如果d:"a 不存在,将会自动创建中级目录 #如果命令扩展名被停用,则需要键入mkdir "a"b"c 6.cd 改变当前目录(进入) cd \ #进入根目录 cd #显示当前目录 cd /d d:\sdk #可以同时更改盘符和目录 cd .. #返回上一级目录 7.rd 删除子目录命令 rd abc #删除当前目录里的abc 子目录,要求为空目录rd /s/q d:\temp #删除d:"temp 文件夹及其子文件夹和文件/q安静模式 磁盘操作类命令: 8.format 磁盘格式化命令 9.chkdsk 检查磁盘当前状态命令

shell命令解释器资料

#include #include #include #include #include #include #include char *stack[1000]={"/"} ; int num=1; extern char **environ; /* 内装式shell命令的函数声明: */ int lsh_cd(char **args); int lsh_help(char **args); int lsh_exit(char **args); int lsh_pushd(char **args); int lsh_popd(char **args); int lsh_dirs(char **args); int lsh_redirect1(char **args); int lsh_redirect2(char **args); int lsh_redirect3(char **args); int lsh_redirect4(char **args); int lsh_export(char **args); int lsh_echo(char **args); /* 装入的命令列表,紧随其后的是相应的功能。*/ char *builtin_str[] = { "cd", "help", "exit", "pushd", "popd", "dirs", "export", "echo" }; int (*builtin_func[]) (char **) = { &lsh_cd, &lsh_help, &lsh_exit, &lsh_pushd,

设计模式 文档

Factory Singleton(单态) Builder Prototype(原型) Flyweight Bridge Decorator(油漆工) Composite(组合) Adapter(适配器) Proxy(代理) Facade(外观总管Manager) Visitor Observer 1、FACTORY?追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory 工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。 2、BUILDER?MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM 我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖)建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 3、FACTORY METHOD?请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。 4、PROTOTYPE?跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要)原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 5、SINGLETON?俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事) 单

相关主题
文本预览
相关文档 最新文档