Dao系统及Dinpy语言起步 ********************** 下载、解压和安装 ================= 看到本文,说明你很可能已经下载了dao语言。如果没有,你可以用以下方法从pypi下载。 * 你可以进入python的pypi,找到存放daot的位置,下载daot的压缩包,然后将下载的压缩包解压到一个单独的文件夹,进入该文件夹,在系统终端输入如下shell命令:: python setup.py install * 使用easy_install直接连网安装,在系统终端输入如下shell命令:: easy_install daot * 使用pip直接连网安装,在系统终端输入如下shell命令:: pip install daot 交互方式执行Dinpy ================= 安装成功后,就可以使用dinpy了。dinpy可以在程序中调用,也可以交互方式执行。我们先来介绍交互方式。 dinpy是嵌入在python内部的,所以我们先进入python的交互模式。在系统终端输入如下shell命令:: $ python 然后导入与dinpy相关的定义:: >>> from dao.dinpy import * >>> python没有报告异常。恭喜你,你已经在python的lib\site-packages目录成功安装了dinpy的程序包。现在我们就来体验它! 首先,看看名声卓著的Hello world! >>> prin("Hello world") Hello world! OK!欢迎进入Dinpy的世界,一个镶嵌在python内部的奇异小宇宙。 prin是dinpy的打印语句。没有print最后的一个字母t。原谅我,Guido van Rossum不会允许我用他喜欢的关键字。 赋值 ----- 万物皆有其名。我们来为dinpy小宇宙中的物体命名吧。 在dinpy中赋值,要用dinpy的赋值符号<<,而不能直接用python的赋值符号。 如果我们这样写: >>> i = 1 那么i只是一个python的变量,不是dinpy的变量。dinpy见到它和见到1没有区别。 我们要这样写: >>> i << 1 1 这样写dinpy才知道i是它的变量,而且现在这个变量在dinpy中已经和1这个值绑定在一起。我们以后会进一步介绍dinpy中变量和绑定的概念。 我们要有一个概念:dinpy的所有语句实际上都是python的表达式,它们可以作为python的list的元素,或者是函数参数。只有这样,你才可以开始理解dinpy在python中的执行原理。 但是为什么不用==呢?本来是可以用==的,不过我发现如果用==有些不便之处。比如,受python限制,没办法象这样通过i=j==1表示级联赋值。实话实说,搞明白这一点还费了我不少周折。 如下语句是dinpy中级联赋值的例子: >>> i << j << 1 1 也可以同时给多个变量赋值: >>> i/j << (1,2) >>> i 1 >>> j 2 OMG!为什么不是i,j << 1,2? 或者是(i,j) << (i,2) ?相信我,我曾经试验过,但是这些试验始终在依照Guido的愿望,而不是依照我的想象工作。这让我明白:在Guido的地盘上,我必须遵守他的法则。也许以后我能找到一个更漂亮的妥协方案。 还有一种方式,你可以这样写: >>> put.i.j << (1,2) 本来我曾经实现了形如put.i==1, put.i.j==(1,2)赋值的形式。有了put在前面,甚至可以级联。但是,如果每次赋值都必须加个put未免太不方便。考虑到形式的一致以其诸多其它因素,我最终放弃了使用==的方案。也许你还可以在代码仓库中找出废弃的实现代码。 算术运算 -------- >>> 1+2 3 >>> 3*4+5 17 如果你能明白,上面两次计算和dinpy没有任何关系,说明你已经开始理解dinpy。我们在和dinpy交互的时候,随时都可以执行任何python的命令。 但是,下面的语句就不一样了: >>> j*2 4 前面我们给j赋值为2,因此j*2的结果是4。因为j是dinpy的变量,所以这次计算才进入了dinpy的领地。 * 左移运算 >>> lshift(j, 1) 8 左移的运算符已经被用作dinpy的赋值符,因此,就只能直接调用内建左移函数进行左移计算了。 * ++和-- >>> i << 0 # 给i赋值0 i >>> ++i # 增一 1 >>> i 1 >>> --i # 减一 0 >>> i 0 C和C++语言中,++和---运算符一直是我的最爱,我觉得这两个运算符体现了C语言的精髓:指针运算,效率,简洁,紧致。 我在编python程序时,经常会怀念这两个运算符,有时候不由自主的把 i += 1 写成 i++。因此,能够在dinpy中实现++和---,我觉得非常愉快。 当然,因为dinpy是在python之中,永远不会有i++或i---这样的算式。 不过,在dinpy中,我无法阻止你把++i 写成 + +i,甚至+(+i), 语法上都没有错,而且结果都和++i一样。毕竟,dinpy还是在python的地盘上。 用quote表示引用值 ----------------- lisp爱好者都知道quote的用法和意义。根据lisp的sexpression的记法, quote的作用是阻止对其参数进行求值,因此(quote x)总是直接返回x自身。不管x是数,变量,或者是个列表。 >>> quote(i) i >>> quote(i+j) i+j 依据dinpy的规则,quote(x)应该直接返回x自身。如果x是变量,返回的是变量本身而不是变量的值,如果是表达式,返回的就是表达式本身,而不是表达式的计算值。或者是其它任何对象。 真是这样吗?Guido说:No! 再看看下面的例子:: >>> quote(1+2) 3 哇!怎么回事?不是1+2?对不起,请不要忘了,dinpy是在python中运行的。python提前已经计算了1+2,dinpy能见到的只有python的计算结果。 以Guido的名义,让python的归于python,dinpy的归于dinpy! dinpy的变量 ----------- 我们要回过头来看一看关于dinpy的变量。我们没有用到python的赋值语句,那么我们上面用到的变量是从哪里来的呢?注意最开始的这条语句: >>> from oad.dinpy import * 这条命令导入你需要用到的dinpy中的一切内容。为了方便,dinpy预先也为你定义了很多形如i, j, x, y, A1, b2, _1, _a的变量。如果你不希望导入如此多的变量,你可以只导入其中的一部分。 >>> from oad.dinpy.vars.lower import * 上述语句导入所有小写字母变量。 >>> from oad.dinpy.vars.lower import i,j,k 上述语句导入变量i,j,k。 你可以看看子包dao.dinpy.vars是如何组织这些变量的,这会给你的理解和应用带来帮助。 除此以外,你也可以不通过导入,而是直接以v.name的形式直接使用单个变量,或者是var.name1.name2的形式使用多个变量。 >>> v.name1 << 1 1 或者这样: >>> name1 = v.name1 >>> name1 << 1 1 这样你可以在后面的使用中省略前缀v.name1的前缀v.喽。 定义多个变量还有另外的方式: >>> i, j, k = var.i.j.k 或者: >>> i, j, k = symbols('i, j, k') let和letr语句 ------------- 可以用let语句和letr语句扩展语句的求值环境并绑定变量。环境是函数式编程语言的重要概念。我们将在后面的章节进行专门的讨论。现在我们先通过例子来体验一下它们的用法。 >>> let(i<<1).do[ prin(i) ] 1 >>> let(i<<1).do[let(i<<2).do[ prin(i) ], prin(i)] 2 1 我们看到利用了let语句,我们可以控制访问变量的层次。求值器总是按照由内到外的层次查找环境。因此,第二个例子中,内层的let语句do块中的prin语句打印的是内层let给i绑定的值,而第二个prin语句,也就是外层let语句的do块的第二条语句,打印的是外层let给i绑定的值。因此,上述代码先打印2,然后打印1。 熟悉lisp的程序员必然也熟悉letrec,letr基本上类似于letrec。letr语句可以用来进行递归定义,特别是可以用来定义递归函数。函数和递归函数的概念,是函数式编程语言的基础。后面的章节会作重点地介绍。 控制语句 -------- 实际的程序很少会始终以一条直线执行下去。因此控制语句总是编程语言不可或缺的成分。我们来看看dinpy中的控制语句。 * 分支语句 可以用iff语句先测试条件决定执行不同分支。 >>> i << 1 >>> iff(i==1).do[prin('i is 1.')].els[prin('i is not 1.' i is 1. 也可以用case语句根据表达式的值选择执行分支。 >>> case(i).of(1)[prin('i is 1.')].of(2)[prin('i is 2.')].els[prin('i is not 1, 2.')] i is 1. >>> i << 3 >>> case(i).of(1)[prin('i is 1.')].of(2)[prin('i is 2.')].els[prin('i is not 1, 2.')] i is not 1, 2. 在控制语句一章中,我们将对循环语句作更加详细的介绍。 * 循环语句 dinpy提供了几种循环语句。看看例子: >>> loop(3)[ prin(i), ++i ] 0 1 2 >>> loop [ prin(i), --i]. until(i==0) 3 2 1 >>> loop [ prin(i), ++i].when(i<3) 0 1 2 >>> when(i!=0).loop[ prin(i), --i] 3 2 1 >>> loop [ prin(i), ++i, iff(i==3) .do[exit] ] 0 1 2 >>> each(i)[0:3].loop[ prin(i) ] 0 1 2 在控制语句一章中,我们将对循环语句作更加详细的介绍。 函数和宏 -------- 在dinpy中,程序员可以自由的使用内建的函数和宏,也可以自己定义新的的函数和宏。函数和宏都是dinpy的一级对象。也就是说,它们和其它类型的数据一样,可以作为参数,操作数,也可以作为返回值,如此等等。 >>> fun.f1(x) == [x+x] f1 >>> f1(2) 4 如“let和letr语句”一节所述,可以用letr定义递归函数。我们看一个实例: >>> letr(f2 << fun(x)[ iff(x<1) .do[ x ] .els[ f2(x-1) ] ]) .do [f2(3)] 0 上述语句用letr定义了函数f2。因为函数体中又调用了f2,所以它是一个递归函数。递归函数必须用letr定义。 现在我们已经简单地介绍了dinpy的一些基本用法。在后续章节中我们将详细讨论dinpy的各种功能特性。下一章将介绍dinpy的常用语句。