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的libsite-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的常用语句。

Table Of Contents

Previous topic

致谢

Next topic

常用语句

This Page