Lisp*****python(以后的方向)

人工智能AI程序设计语言简介 本文将介绍10多种不同的程序设计语言,以及如何下载它们的免费的编译器,(注意,是免费,而不是盗版),如果下载或者安装有问题,可以给我发邮件:ruoyu0088@sohu.com20 年后,我们用什么样的语言?也许现在谁都不好回答,20年前的程序员们会告诉你用汇编吧,今天人们也许会告诉你用c语言(c++?)吧。那么20年后呢, 也许不是c语言了吧。下面都是所谓的非主流语言,不过它们的设计思想 阅读详情

这是一篇为Lisp程序员写的Python简介(一些Python程序员告诉我,这篇文章对他们学习Lisp也有帮助,尽管这不是我的本意)。基本上,Python可以看作一个拥有“传统”语法(Lisp社区称之为“中缀”或者“m-lisp”语法)的Lisp方言。一个来自comp.lang.python的帖子说到“我一直不明白为什么LISP是一个不错的想法,直到我开始玩上了Python”。Python支持除了宏(macros)之外的所有Lisp的关键特征,并且你不会太想念宏因为Python拥有求值,运算符重载,和正则表达式解析,所以一些(不是所有)宏的使用场景都被涵盖了。

我深入学习Python的原因是,我正在考虑把Russel & Norvig的人工智能教材配套的Lisp代码转化成Java代码。一些教师和学生想要Java代码,因为:

  1. 那是他们在其他课程中最熟悉的语言。
  2. 他们希望有图形应用程序(graphical applications)。
  3. 少数人想要能在浏览器中运行的applets。
  4. 一些人只是因为在他们可以投入的有限的上课时间内,不能习惯于Lisp的语法。
然而,我们编写Java版本的第一次尝试很不成功。Java太罗嗦了,并且书中的伪代码和java代码之间的差异太大。我环顾四周,寻找一种和书中伪代码相近的语言,并最终发现 Python 是最接近的。此外,有了 Jython ,我可以定位于Java的JVM。

我的结论

对于我的使用目的而言,Python是一个非常优秀的语言。它使用简单(交互式的,没有“编译-链接-加载-运行”循环),这点对于我的教学目的是很重要的。虽然Python不满足被拼写为J-A-V-A的前提条件,但是Jython已经很接近了。对于没有其他语言经验的人而言,Python似乎比Lisp更容易阅读。我开发的 Python代码 看起来比 Lisp代码 更像书中(独立开发)的 伪代码 。这点是很重要的,因为一些同学抱怨说,他们在把书中的伪代码和网上的Lisp代码对应起来的过程中,有一段困难的时间(即使这个过程对Lisp程序员来说是显然的)。

从我的观点来看,Python的两个主要的缺点是:1)只有很少的编译时的错误分析(compile-time error analysis)和类型声明(type declaration),甚至比Lisp还少。2)运行时间比Lisp慢很多,通常相差10倍(有时100倍,有时1倍)。定性地说,Python和解释型的Lisp的速度差不多,但是很明显地慢于编译型的Lisp。基于这个原因,对于那些(或者会逐渐变为)计算密集性的应用(除非你愿意把速度瓶颈部分用c重写),我不会推荐使用Python。但是我的目的是面向教育的,而不是产品,所以速度不是问题。

Python 简介

Python既可以看作一个实用(更好的库)版本的Scheme,也可以看作一个净化(没有了“$@&%”符号)版本的Perl。然而Perl的哲学是TIMTIWTDI(there's more than one way to do it,即都种方法解决同意问题),Python试图提供一个最小子集,使得人们以同样的方式使用它(即TOOWTDI,there's only one way to do it,但是如果你努力尝试的话,通常会有多种方式去解决同一问题)。其中一个具有争议的Python特征是,用缩进层次代替begin/end或者花括号,启发它的哲学思想是:因为这里没有括号,所以也就没有了因为如何放置括号而引起的风格战争(style wars)。有趣的是,Lisp在这点上有同样的哲学思想:每个人都用emacs去缩进代码,所以他们没有去争论缩进方式。拿到一个Lisp代码,对它进行合理的缩进,删除行首的左括号(opening parens),以及与之匹配的闭括号(close parens),这样我们就得到看起来类似Python的程序。

Python有做出明智妥协的哲学思想,使得容易的事情更容易,并且不排除太多困难的事情。在我看来Python做的不错。简单的事情简单,困难的事情逐渐困难,并且你甚至注意不到这种不一致。Lisp有做出较少妥协的思想:提供一个强大并且完全一致的核心。这让Lisp很难去学,因为你从一开始就操作在一个较高的抽象层次上,而且你需要理解你正在做什么,而不是仅仅依靠感觉活着看起来漂亮。但是它同样意味着更容易为Lisp添加抽象层次和复杂性;Lisp优化的目标是使得很困难的事情不太困难,而Python优化的目标是使得中等难度的事情更简单。

这里我从Python.org摘了一段对Python的简介,并我创造了两个版本:一个是用蓝色斜体显示的Python,另一个是用绿色粗体显示的Lisp。其他大部分,两个语言的共同部分,是黑色的。

Python/ Lisp 是一个解释型的 和编译型的, 面向对象的,高级编程语言,并且拥有动态语义。它的高级的内部数据结构,以及动态类型和动态绑定,使得它对于快速应用开发特别有吸引力,同样地,Python作为脚本或者胶水语言去链接已存在的部分,也是非常有吸引力。 Python/ Lisp简单、易学的语法强调的是可读性,并因此降低程序维护的成本。 Python/ Lisp支持 模块(modules)和包(packages),这鼓励了程序的模块化和复用。 Python/ Lisp解释器和广泛的标准库在大多数平台上都是以源码或者二进制形式发布的,并且可以自由的散发。通常,程序员爱上 Python/ Lisp是因为它提供地不断增加的生产力。因为这里没有 分离的(separate)编译步骤,“编辑-测试-调试”循环难以置信的快。调试 Python/ Lisp程序很简单:一个bug或者坏输入不会造成段错误。相反,当解释器发现一个错误,它抛出一个异常。当程序没有抓获这个异常时,解释器将打印栈轨迹。代码级别的调试允许查看局部和全局变量,求值任意表达式,设置断点,单步执行,等等。调试器是由 Python/ Lisp自己写的,表明了 Python/ Lisp的自省能力。另一方面,通常最快的调试一个程序的方法是向源码中添加一些打印语句:快速的“编辑-测试-调试”循环使得这个简单的方法特别有效。
我只添加下面这句:
尽管有些人对于 缩进作为块结构/ 括号有原始的抵制,但是大多数人最后 喜欢/ 深深的感激 他们。

想学习更多关于Python的知识,如果你是一个有经验的程序员,我推荐你去Python.org的下载页面,下载文档包,并且特别要注意Python参考手册和Python库参考。这里有各种各样的辅导材料(tutorials)和出版的书籍,但是这些参考是你真正需要的。

下面的表作为一个Lisp/Python转化的指南。红色的表项标记了这个位置中的语言,明显的比较糟糕(我自己的观点)。粗体标记了这个位置上,两个语言明显不同,但是没有一个方式明显的好于另一个。用正常字体显示的表项意味着两个语言是相似的;语法可能稍有不同,但是概念是相同的,或者非常相近。表后面是一个要点列表和一些Python的示例程序.

关键特征Lisp 特征Python 特征
一切都是对象是是
对象有类型,变量没有是是
支持异构的(heterogenous)列表是 (linked list and array/vector)是 (array)
多范式语言是:函数式,命令式,OO,Generic是:函数式,命令式,OO
存储管理自动垃圾收集自动垃圾收集
包/模块使用困难使用简单
对象,类的自省强大强大
元编程的宏强大的宏没有宏
交互式的REPL(Read-eval-print loop)> (string-append "hello" " " "world")
"hello world"
>>> ' '.join(['hello', 'world'])
'hello world'
简介、富有表达力的语言(defun transpose (m)
   (apply #'mapcar #'list m))
> (transpose '((1 2 3) (4 5 6)))
((1 4) (2 5) (3 6))
def transpose (m): 
  return zip(*m)
>>> transpose([[1,2,3], [4,5,6]])
[(1, 4), (2, 5), (3, 6)]
跨平台可移植性Windows, Mac, Unix, Gnu/LinuxWindows, Mac, Unix, Gnu/Linux
实现的数量很多一个主要的,附加一些分支(例如:Jython, Stackless)
开发模式专有和开源开源
效率大约比C++慢1到2倍大约比C++慢2到100倍
GUI, Web 等库没有标准GUI, Web 标准库
方法
方法分派
动态, (meth obj arg) 语法
runtime-type, multi-methods
动态, obj.meth(arg) 语法 
runtime-type, single class-based
数据类型Lisp 数据类型Python 数据类型
Integer
Bignum 
Float
Complex
String
Symbol
Hashtable/Dictionary
Function
Class
Instance
Stream
Boolean
Empty Sequence
Missing Value
Lisp List (linked)
Python List (adjustable array)
Others
42
100000000000000000
12.34
#C(1, 2)
"hello"
hello
(make-hash-table)
(lambda (x) (+ x x))
(defclass stack ...)
(make 'stack)
(open "file")
t, nil
(), #() linked list, array
nil
(1 2.0 "three")
(make-arrary 3 :adjustable t
  :initial-contents '(1 2 3))

很多 (in core language)
42
100000000000000000
12.34
1 + 2J
"hello" or 'hello' ## 不可变的
'hello'
{}
lambda x: x + x
class Stack: ...
Stack()
open("file")
True, False
(), [] tuple, array
None
(1, (2.0, ("three", None)))
[1, 2.0, "three"]

很多 (in libraries)
控制结构Lisp 控制结构Python 控制结构
语句和表达式一切都是表达式区分语句和表达式
假值(False)nil是唯一的假值False, None, 0, '', [ ], {}都是假值
函数调用(func x y z)func(x,y,z)
条件测试(if x y z)if x: y
else: z
条件表达式(if x y z)y if x else z
While循环(loop while (test) do (f))while test(): f()
其他循环(dotimes (i n) (f i))
(loop for x in s do (f x))
(loop for (name addr salary) in db do ...)
for i in range(n): f(i)
for x in s: f(x) ## s为任意序列
for (name, addr, salary) in db: ...
赋值(setq x y)
(psetq x 1 y 2)
(rotatef x y)
(setf (slot x) y)
(values 1 2 3) 在栈上
(multiple-value-setq (x y) (values 1 2))
x = y
x, y = 1, 2
x, y = y, x
x.slot = y 
(1, 2, 3) 在堆中
x, y = 1, 2
异常(assert (/= denom 0))
(unwind-protect (attempt) (recovery))
 
(catch 'ball ... (throw 'ball))
assert denom != 0, "denom != 0"
try: attempt()
finally: recovery()
try: ...; raise 'ball'
except 'ball': ...
其他控制结构case, etypecase, cond, with-open-file, etc.扩展的with语句
没有其他控制结构
词法结构Lisp 词法结构Python 词法结构
注释;; 分号直到行尾## 井号直到行尾
界定符(Delimiters)用括号来界定表达式
(defun fact (n)
  (if (<= n 1) 1 
      (* n (fact (- n 1)))))
用缩进来界定语句
def fact (n):
    if n <= 1: return 1
    else: return n * fact(n — 1)
高阶函数Lisp 高阶函数Python 高阶函数
应用函数
执行一个表达式
执行一个语句
加载文件
(apply fn args)
(eval '(+ 2 2)) => 4
(eval '(dolist (x list) (f x)))
(load "file.lisp") or (require 'file)
apply(fn, args) or fn(*args)
eval("2+2") => 4
exec("for x in list: f(x)")
execfile("file.py") or import file
序列函数(mapcar length '("one" (2 3))) => (3 2)
(reduce #'+ numbers)
(every #'oddp '(1 3 5)) => T
(some #'oddp '(1 2 3)) => 1
(remove-if-not #'evenp numbers)

(reduce #'min numbers)
map(len, ["one", [2, 3]]) => [3, 2]
or [len(x) for x in ["one", [2, 3]]]
reduce(operator.add, numbers) 
all(x%2 for x in [1,3,5]) => True
any(x%2 for x in [1,2,3]) => True
filter(lambda x: x%2 == 0, numbers)
or [x for x in numbers if x%2 == 0]
min(numbers)
其他高阶函数count-if 等
:test, :key 等关键字参数
没有其他内置的高阶函数
map/reduce/filter函数没有关键字参数
Close over read-only var
Close over writable var
(lambda (x) (f x y))
(lambda (x) (incf y x))
lambda x: f(x, y)
Can't be done; use objects
参数列表Lisp 参数列表Python 参数列表
可选参数 

变长参数
未确定的关键字参数

调用约定
(defun f (&optional (arg val) ...)
(defun f (&rest arg) ...)
(defun f (&allow-other-keys &rest arg) ...)
只有明确声明时,才可以用关键词方式调用:
(defun f (&key x y) ...)
(f :y 1 :x 2)
def f (arg=val): ... 

def f (*arg): ...
def f (**arg): ...

用关键字方式调用任何函数:
def f (x,y): ...
f(y=1, x=2)
效率Lisp 效率问题Python 效率问题
编译
函数引用解析
声明
编译到本机代码
大多数“函数/方法”的查找很快
为了效率可以进行声明
编译到字节码
大多数“函数/方法”的查找比较慢
没有声明
特征Lisp 特征和函数Python 特征和函数
引用(Quotation)引用整个列表结构:
'hello
'(this is a test)
'(hello world (+ 2 2))
引用单个的字符串或者.split():
'hello'
'this is a test'.split()
['hello', 'world', [2, "+", 2]]
自省(Introspectible)文档字符串(defun f (x)
  "compute f value"
  ...)
> (documentation 'f 'function)
"compute f value"
def f(x):
  "compute f value"
  ...
>>> f.__doc__
"compute f value"
列表访问通过函数:
(first list)
(setf (elt list n) val)
(first (last list))
(subseq list start end)
(subseq list start)
通过语法:
list[0]
list[n] = val
list[-1]
list[start:end]
list[start:]
哈希表访问通过函数:
(setq h (make-hash-table))
(setf (gethash "one" h) 1.0)
(gethash "one" h)
(let ((h (make-hash-table)))
  (setf (gethash "one" h) 1)
  (setf (gethash "two" h) 2)
  h)
通过语法:
h = {}
h["one"] = 1.0
h["one"] or h.get("one")
h = {"one": 1, "two": 2}
列表上的操作(cons x y)
(car x)
(cdr x)
(equal x y)
(eq x y)
nil
(length seq)
(vector 1 2 3)
[x] + y but O(n); also y.append(x)
x[0]
x[1:] but O(n)
x == y
x is y
() or [ ]
len(seq)
(1, 2, 3)
数组上的操作(make-array 10 :initial-element 42)
(aref x i)
(incf (aref x i))
(setf (aref x i) 0)
(length x)
#(10 20 30) 如果大小不变的话
10 * [42] 

x[i]
x[i] += 1
x[i] = 0
len(x)
[10, 20, 30]

对大多数人来说,一个重要的方面就是Python和Lisp相对于其他语言而言的执行速度如何。我很难得到和你的应用相关的基准测试数据,但是下面这个表可能对你有用:

5种语言在基准测试集 The GreatComputer Language Shootout上的相对速度.
测试LispJavaPythonPerlC++
哈希访问1.063.234.011.851.00
异常处理0.010.901.541.731.00 图例说明
对文件中的数字进行求和7.542.638.342.491.00 > 100 x C++
反转行1.611.221.381.251.00 50-100 x C++
矩阵相乘3.308.90278.00226.001.00 10-50 x C++
堆排序1.677.0084.4275.671.00 5-10 xC++
数组访问1.756.83141.08127.251.00 2-5 x C++
列表处理0.9320.4720.3311.271.00 1-2 x C++
对象实例化1.322.3949.1189.211.00 < 1 x C++
单词计数0.734.612.571.641.00
中位数1.674.6120.3311.271.00
25% to 75%0.93 to 1.672.63 to 7.002.57 to 84.421.73 to 89.211.00 to 1.00
范围0.01 to 7.540.90 to 20.471.38 to 2781.25 to 2261.00 to 1.00

对速度进行了规格化处理,以使得利用g++编译器编译过的c++代码的速度是1.00,所以2.00意味着比c++慢2倍;0.01意味着比c++快100倍。对于Lisp而言,使用的是CMUCL编译器。背景颜色是按照右边图例说明进行绘制的。最后的3行给出了中位数值,25%到75%的quartile值(即去掉两个最低值和去掉两个最高值),以及整体范围。在去掉两个最低的和两个最高的之后,比较Lisp和Python,我们发现Python比Lisp要慢3到85倍,Perl和Python差不多,但是比Java和Lisp都慢。Lisp大约比Java要快2倍。

给Lisp程序员的Python要点

在这里我列出了一些概念上的问题,是为像我一样转到Python的Lisp程序员准备的:
  1. Lists are not Conses。 Python的列表其实比较像Lisp的可变数组,或者Java的向量(Vector)。这意味着列表的存取是O(1)的,但是与cons和cdr等价的操作却产生了O(n)的新空间。你真的应该使用map或者for e in x:,而不是基于car/cdr的递归。注意Python里有多个空列表,而不是一个。这修正了Lisp一个常见的bug,即用户调用(nconc old new),并期待old被修改了,但是当old是nil的时候,它并没有被修改。在Python里,即使old是[ ],old.extend(new)也可以正常工作。但是这将意味着你必须用==测试列表是否为[];而不是用is,同时这也意味着,如果你把一个默认参数的值设置为[],你最好不要修改这个值。
  2. Python is less functional。 部分原因是Python的列表不是conses,相比于Lisp,Python使用了更多方法去改变列表的结构,并且为了强调它们的改变,它们通常返回None值。例如像list.sort,list.reverse, 和list.remove这样的方法都是,但是Python的新版本中引入了函数式的版本,即作为一个函数而不是方法,我们现在有了sorted andreversed(但是没有removed)。
  3. Python classes are more functional.在Lisp(CLOS)中,当你重定义一个类C时,表示类C的对象也相应地得到修改。已经存在的C的实例和子类也因此重新指向新的类。这有时候会引起一些问题,但是在交互式的调试中,这却是很有用的。在Python中,当你重定义一个类时,你会得到一个新的类对象,但是实例和子类还是指向旧类。这就意味着大多数时候你必须重新载入你的子类,并且重建你的数据结构。如果你忘记了,将会引起混淆。
  4. Python is more dynamic, does less error-checking.在Python中,对于未定义的函数或者域,或者传给了函数错误的参数个数,或者其他载入阶段的其他大多数问题,你都不会得到任何警告信息;你必须等到运行时,才能得到错误信息。商业的Lisp实现将会把这些大多数问题标识为警告;简单的Lisp实现(如clisp)不会。一个演示Python危险性的地方是,当你想写self.field = 0时,却敲入了self.feild = 0,后者将会动态的创建一个新的域。与之相对的Lisp等价物为,(setf (feild self) 0),它将给你一个错误。另一方面,访问你一个未定义的域时,两个语言都会报告一个错误。
  5. Don't forget self.这点更应该引起Java程序员的注意:在一个方法中,确保你写的是self.field,而不是field。这里没有隐式的作用域(scope)。通常这会引起一个运行时错误。这很令人讨厌,但是我认为过一段时间后,人们将学会不这么干。
  6. 不要忘记return.写函数def twice(x): x+x是很诱人的,并且这不会发出任何警告或者异常信号,但是你可能真正想要的是def twice(x): return x+x。这点特别令人厌烦,因为在一个lambda表达式中,return语句是被禁止的,但是它的语义却是执行return。
  7. 注意单元素元组(tuple)。一个元组是一个不可变的列表,并且用圆括号围起来,而不是用方括号。()是空元组,(1, 2)是一个含有两个元素的元组,但是(1)表示的是1。而一个元素的元组却要用(1,)表示,我擦!Damian Morton指出,如果你把元组看成是由逗号(,)产生的,打印时带有圆括号的数据,这样就好理解了。圆括号只是起了消除歧义的作用,在这个解释下,1, 2是含有两个元素的元组,1,是含有一个元素的元组,圆括号有时是需要的,这主要取决于元组出现的位置。例如,虽然2, + 2,是一个合法的表达式,但是更加清晰的方式是(2,) + (2,)或者(2, 2)。
  8. 注意特定的异常 小心: 当key不存在时,dict[key]会抛出KeyError; lisp哈希表的用户则期望得到nil。你应该捕获这个异常或者用key in dict测试。
  9. Python是Lisp-1的。 我的意思是Python只有一个命名空间,里面包含了变量和函数,就像scheme一样,而不是想Common Lisp那样,有两个命名空间。例如:
    def f(list, len): return list((len, len(list)))      ## bad Python
    (define (f list length) (list length (length list))) ;; bad Scheme
    (defun f (list length) (list length (length list)))  ;; legal Common Lisp
    
    对于域和方法也是一样:你不能提供一个和域名相同的方法名:
    class C:
        def f(self): return self.f  ## bad Python
        ...
    
  10. Python字符串不同于Lisp的符号(symbol)。Python通过把字符串内化到模块或类的哈希表中,然后进行符号查找。也就是说,当你写obj.slot时,Python在运行阶段会去obj类的哈希表中查找字符串"slot"。Python也会内化一些用户代码中的字符串,例如,当你写x = "str"时。但是Python并不内化那些看起来不像变量的字符串,例如x = "a str"(感谢Brian Spilsbur指出这点)。
  11. Python没有宏(macro)。 Python可以访问一个程序的抽象语法树,但是这不是适合“心脏不好”的人。从积极的方面来看,该模块容易理解,并且在5分钟内,我用5行代码就可以得到:
    >>> parse("2 + 2")
    ['eval_input', ['testlist', ['test', ['and_test', ['not_test', ['comparison',
     ['expr', ['xor_expr', ['and_expr', ['shift_expr', ['arith_expr', ['term', 
      ['factor', ['power', ['atom', [2, '2']]]]], [14, '+'], ['term', ['factor',
       ['power', ['atom', [2, '2']]]]]]]]]]]]]]], [4, ''], [0, '']]
    
    这令我相当失望,同样的表达式在Lisp中的解析结果是(+ 2 2)。看来,只有真正的专家才会想去操纵Python的解析树,相反,Lisp的解析树对任何人来说都是简单可用。我们任然可以在Python中,通过连接字符串,来创建一些类似于宏的东西,但是它不能和其他语言集成,所以在实践中不这样做。在Lisp中,两个使用宏的主要目的是:新的控制结构和定制针对特定问题的语言。前者没有在Python中实现。后者可以通过在Python中,用适合特定问题的数据格式来做到:下面我在Python中定义了一个上下文无关语法,分别通过1)组合字典的内置语法,2)解析字符串的预处理过程完成的。第一种方式和Lisp的宏一样优雅。但是对于复杂的任务,例如为逻辑编程语言写一个编译器这样的事,在Lisp中很容易,但是在Python将很困难。

比较Lisp和Python程序

我从 《Paradigms of Artificial Intelligence Programming》 一书中取了一个简单的 随机句子生产器 程序,并把它翻译成Python。结论:简介性相当;Python因为 grammar[phrase] 比 (rule-rhs (assoc phrase *grammar*)) 简单,而获得一分,但是Lisp因为 '(NP VP) 比 ['NP', 'VP'] 更简介而扳平比分。Python程序很可能比较低效,但是这不是我们关注的点。两个语言看起来都很适合这样的程序。 调整浏览器窗口到合适的宽度以便阅读代码。

Lisp程序 simple.lispPython程序 simple.py
(defparameter *grammar*
  '((sentence -> (noun-phrase verb-phrase))
    (noun-phrase -> (Article Noun))
    (verb-phrase -> (Verb noun-phrase))
    (Article -> the a)
    (Noun -> man ball woman table)
    (Verb -> hit took saw liked))
  "A grammar for a trivial subset of English.")

(defun generate (phrase)
  "Generate a random sentence or phrase"
  (cond ((listp phrase)
         (mappend #'generate phrase))
        ((rewrites phrase)
         (generate (random-elt (rewrites phrase))))
        (t (list phrase))))

(defun generate-tree (phrase)
  "Generate a random sentence or phrase,
  with a complete parse tree."
  (cond ((listp phrase)
         (mapcar #'generate-tree phrase))
        ((rewrites phrase)
         (cons phrase
               (generate-tree (random-elt (rewrites phrase)))))
        (t (list phrase))))

(defun mappend (fn list)
  "Append the results of calling fn on each element of list.
  Like mapcon, but uses append instead of nconc."
  (apply #'append (mapcar fn list)))

(defun rule-rhs (rule)
  "The right hand side of a rule."
  (rest (rest rule)))

(defun rewrites (category)
  "Return a list of the possible rewrites for this category."
  (rule-rhs (assoc category *grammar*)))
from random import choice

grammar = dict(
        S = [['NP','VP']],
        NP = [['Art', 'N']],
        VP = [['V', 'NP']],
        Art = ['the', 'a'],
        N = ['man', 'ball', 'woman', 'table'],
        V = ['hit', 'took', 'saw', 'liked']
        )

def generate(phrase):
    "Generate a random sentence or phrase"
    if isinstance(phrase, list): 
        return mappend(generate, phrase)
    elif phrase in grammar:
        return generate(choice(grammar[phrase]))
    else: return [phrase]
    
def generate_tree(phrase):
    """Generate a random sentence or phrase,
     with a complete parse tree."""
    if isinstance(phrase, list): 
        return map(generate_tree, phrase)
    elif phrase in grammar:
        return [phrase] + generate_tree(choice(grammar[phrase]))
    else: return [phrase]

def mappend(fn, list):
    "Append the results of calling fn on each element of list."
    return reduce(lambda x,y: x+y, map(fn, list))
Running the Lisp ProgramRunning the Python Program
> (generate 'S)
(the man saw the table)
>>> generate('S')
['the', 'man', 'saw', 'the', 'table']

>>> ' '.join(generate('S'))
'the man saw the table'

Python中的grammer比Lisp中的丑陋,这让我很担心,所以我考虑在Python中写一个解析器(后来发现已经有一些写好的,并且可以免费获得的),以及重载一些内置的操作符。第二种方法在一些应用中是可行的,例如我写Expr class,这是用来表现和操纵逻辑表达式的。但是对于这个应用而言,一个简单、定制的语法规则解析器就够了:一个语法规则是一个用“|”分开的,可选部分的列表,每个可选部分都是由空格(" ")分隔的单词列表。把grammar程序重写为一个更加符合Python惯用法的程序,而不是Lisp程序的翻译,下面就是该程序:

Python Program simple.py (idiomatic version)
"""Generate random sentences from a grammar.  The grammar
consists of entries that can be written as S = 'NP VP | S and S',
which gets translated to {'S': [['NP', 'VP'], ['S', 'and', 'S']]}, and
means that one of the top-level lists will be chosen at random, and
then each element of the second-level list will be rewritten; if it is
not in the grammar it rewrites as itself.  The functions rewrite and
rewrite_tree take as input a list of symbols.  The functions generate and
generate_tree are convenient interfaces to rewrite and rewrite_tree
that accept a string (which defaults to 'S') as input."""

import random

def make_grammar(**grammar):
  "Create a dictionary mapping symbols to alternatives."
  for (cat, rhs) in grammar.items():
    grammar[cat] = [alt.split() for alt in rhs.split('|')]
  return grammar
  
grammar = make_grammar(
  S = 'NP VP',
  NP = 'Art N',
  VP = 'V NP',
  Art = 'the | a',
  N = 'man | ball | woman | table',
  V = 'hit | took | saw | liked'
  )

def rewrite(symbols):
  "Replace each non-terminal symbol in the list with a random entry in grammar (recursively)."
  return [terminal for symbol in symbols
          for terminal in (rewrite(random.choice(grammar[symbol])) 
                           if symbol in grammar else [symbol])]

def rewrite_tree(symbols):
  "Replace the list of symbols into a random tree, chosen from grammar."
  return [{symbol: rewrite_tree(random.choice(grammar[symbol]))} 
          if symbol in grammar else symbol
          for symbol in symbols]

def generate(symbols='S'):
  """Replace symbol(s) in the space-delimited input string by a random entry
  in grammar (recursively until terminals); join back into a string."""
  return ' '.join(rewrite(symbols.split()))

def generate_tree(symbols='S'):
  "Replace symbol(s) in the space-delimited input string by a random entry
  in grammar (recursively until terminals); return a tree."""
  return rewrite_tree(symbols.split())

编程语言这么多,偏偏C语言成了大学的必修课!就因为二级吗? “我不是学计算机的,为啥要学C语言?”如果把学C语言的精力用来学学其它与专业相关度更高的东西,是不是会更好呢?既然如此,干嘛还学C语言呢? 大学里除了要拿到“毕业证”和“学位证”以外,还要过“四级”和“二级”,否则可能会被扣发两证,或找工作受阻,难以“落户”等。这里的二级指的是“全国计算机等级考试二级”,是一个面向“程序员”,“考核计算机基础知识和使用一种高级计算机语言编写程序以及上机调试的基本技能”的考试。虽然社会只需要少量的人会编程序,但莫名其妙的是几乎所有的大学生都想(或被要求)过二级,导致程序 阅读详情

相关推荐

Python实现Lisp风格的函数式列表操作

本文还有配套的精品资源,点击获取 简介:Python虽以面向对象设计为主,但支持函数式编程,尤其适合处理数据分析任务。文章介绍如何在Python中使用函数式编程的Lisp风格列表表示,通过元组和自定义函数模拟Lisp中的列表操作如cons、car和cdr,并展示了在数据分析中的应用。这种风格简化了代码,提高了可读性和可维护性。 1. Python中的函数式编程基础...

weixin_42418754的博客 1188

给Lisp程序员的Python简介

*本篇译文还在初始阶段,希望各位提出反馈意见,另外一个更适合浏览的版本在这里* ============================================================== 给Lisp程序员的Python简介 作者:Peter Norvig,译者:jineslong 这是一篇为Lisp程序员写的Python简介(一些Python程序员告诉我,这篇文章

zzljlu的专栏 1万+

Lispy:Python编写的类似Lisp的语言

丽皮 描述 用Python编写的类似Lisp的语言 文档: : 依存关系 Python 3.8以上 安装 下载最新版本或主要版本(不稳定) 使用python .\Lispy.py <file>.lpy [--debug]启动任何lispy文件,或使用python .\Lispy.py [--debug]启动解释器 享受 特征 基本数学运算符:+,-,*,/,//,% 基本逻辑运算符:=,!=,<=,> =,<,> 、! 基本功能: 使用def,set,del进行变量管理 显示以打印东西 输入以获取用户输入 如果条件 与for和while循环 使用类型获取对象的类型 断言断言 导入以导入其他lispy文件或本机模块或python模块 本机模块(导入<名称>): str:管理字符串(9个常量,22个函数) list:管理列表(23个功能) dict:管理字典(7个功能) m

Lisp-Stat翻译 —— 第五章 统计函数

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33862188的博客 540

(How to Write a (Lisp) Interpreter (in Python))中文版(包括上下篇)

(How to Write a (Lisp) Interpreter (in Python))和(An ((Even Better) Lisp) Interpreter (in Python))的翻译,对解释器实现原理和函数式编程敢兴趣的可以下载看看!

从‘炼丹’到‘讲理’:物理信息神经网络(PINN)的硬约束 vs 软约束,我该选哪个?

本文深入探讨了物理信息神经网络(PINN)中硬约束与软约束的选择策略,帮助研究者在深度学习中有效融入物理先验知识。通过对比两种约束方法的实现复杂度、数据效率和计算性能,提供了针对材料科学和流体动力学等领域的实用决策指南,并介绍了自适应约束和混合约束等前沿技术。

weixin_42535703的博客 526

python lisp_给 Lisp 程序员的 Python 简介

这是一篇为Lisp程序员写的Python简介(一些Python程序员告诉我,这篇文章对他们学习Lisp也有帮助,尽管这不是我的本意)。基本上,Python可以看作一个拥有“传统”语法(Lisp社区称之为“中缀”或者“m-lisp”语法)的Lisp方言。一个来自comp.lang.python的帖子说到“我一直不明白为什么LISP是一个不错的想法,直到我开始玩上了Python”。Python支持除了...

weixin_39767887的博客 351

别再让C盘爆红了!手把手教你将Anaconda虚拟环境迁移到其他盘(附权限问题解决)

本文详细指导如何将Anaconda虚拟环境从C盘迁移到其他驱动器,解决C盘空间不足问题。通过conda config命令和直接修改.condarc配置文件两种方法,确保环境迁移顺利进行,同时提供权限问题解决方案和长期维护技巧,帮助用户高效管理虚拟环境。

weixin_42525289的博客 420

python lisp_给Lisp程序员的Python简介

作者:Peter Norvig,译者:jineslong这是一篇为Lisp程序员写的Python简介(一些Python程序员告诉我,这篇文章对他们学习Lisp也有帮助,尽管这不是我的本意)。基本上,Python可以看作一个拥有“传统”语法(Lisp社区称之为“中缀”或者“m-lisp”语法)的Lisp方言。一个来自comp.lang.python的帖子说到“我一直不明白为什么LISP是一个不错的想...

weixin_39618597的博客 404

Plant Simulation新手入门:从零开始搭建工厂布局仿真模型(附完整代码)

本文为Plant Simulation新手提供了一个从零开始搭建工厂布局仿真模型的完整实战教程。通过一个玩具工厂车间布局优化的具体案例,详细讲解了从数据准备、模型构建、核心逻辑编程到连接遗传算法进行自动优化的全流程,并附有完整代码示例,帮助读者快速掌握这一工业仿真软件的核心应用。

metal的博客 553

告别有线:用PVE直通无线网卡,打造一台真正的‘无线’Win10软路由/测试机

本文详细介绍了如何通过PVE直通无线网卡技术,构建全无线Win10软路由或测试机。从硬件选型、PVE环境配置到PCIe和USB双模直通技术,逐步指导实现Wi-Fi AP和蓝牙连接功能,为家庭实验室和小型办公环境提供灵活高效的网络解决方案。

weixin_28683689的博客 586

python lisp_Hy: 在Python上运行的Lisp

如果想要体验下古老的Lisp语言,你可以下载同样古老的emacs,然后配上SBCL/CLISP,再加上SLIME。等你差不多配好这个环境的时候,也差不多想要放弃体验Lisp了。不过我在网上找到了这个比较有意思的Lisp项目,HyHy的吉祥物这是一个在Python上运行的Lisp语言,和Clojure运行在Java上类似。如果对Python语言比较熟悉,上手会很快。安装超级简单$pipinsta...

weixin_39993623的博客 456

Stata实战:5步搞定PSM倾向得分匹配(附完整代码与常见报错解决)

本文详细介绍了使用Stata进行PSM(倾向得分匹配)的5步实战流程,包括数据预处理、模型构建、匹配算法选择、匹配质量诊断和结果解释。提供完整代码示例和常见报错解决方案,帮助研究者高效完成实证分析,提升匹配成功率。

weixin_29214559的博客 590

一分钟学Python| Python的函数(下)

点击上方“Python进击者”,选择“星标”公众号超级无敌干货每日18:00推送给你!!!默认参数调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入 age 参数,则...

Kuls 368

代谢组数据分析(一):生成代谢组数据SummarizedExperiment对象

本文介绍了基于Zeybel 2022年发布的多组学数据集的分析脚本,该数据集包含微生物组、宿主人体学指标、临床学指标、血浆代谢组和免疫因子等多维度数据,涵盖56位患者的横截面数据和22位患者的纵向时间序列数据。脚本旨在生成适用于下游分析的数据对象,主要包括phyloseq和SummarizedExperiment两类。phyloseq对象用于整合微生物群落数据,支持多数据整合、可视化、丰富分析功能和便捷的数据处理;SummarizedExperiment对象则广泛应用于基因组学和转录组学研究,便于数据整合、

专注生信领域 2988

YOLOv8交通监控全方案:精准车辆计数+违章(闯红灯/压线)识别,拿来就落地!

作为深耕计算机视觉落地的开发者,我见过太多交通监控的“伪智能”——传统系统要么车辆计数不准(漏计/重复计数),要么违章识别滞后(靠人工回看录像),甚至连“闯红灯、压线”这类常见违章都无法实时判定,完全满足不了实际交通管理需求。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 646

人工智能编程语言介绍

本文将介绍10多种不同的程序设计语言,以及如何下载它们的免费的编译器(注意:不是盗版) 20 年后,我们用什么样的语言?也许现在谁都不好回答,20年前的程序员们会告诉你用汇编吧,今天人们也许会告诉你用c语言(c++?)吧。那么20年后呢, 也许不是c语言了吧。下面都是所谓的非主流语言,不过它们的设计思想、所能够完成的功能,绝对不比c语言差。也许将来它们会成为主流,也许不会,可是不管 怎样,它

Rana专栏 4849

【华为OD技术面-手撕算法代码真题】- 最长公共前缀

🌟 强烈推荐:华为OD技术面试手撕算法代码真题 🌟大家好!今天我给大家推荐一份备受赞誉的华为OD技术面试手撕算法代码真题。所有题目均为华为od实际面试过程中出现的算法代码真题。

算法大师! 4884

面向对象——进阶

面向对象--初级:重点知识回顾 面向对象是一种编程方式,此编程方式的实现是基于对类和对象的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数 面向对象三大特性:封装、继承和多态 三个装饰器 @property **** #@name.se...

weixin_30608131的博客 100

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

python lisp_在Python中实现lisp

第一:是的,我已经花了很长时间看看Norvig的lispy.第二:我重用了部分代码.关于我的代码和我的问题.我正在用Python写一个非常惯用的lisp解释器,我很好奇我将如何编写嵌套函数定义(例如(定义square(lambda(x)(* xx)))然后(定义SoS(lambda( xy)((方x)(方y))))),目前不起作用.我有点卡住了.我能做什么?编辑:任何有关我的编码风格或我可以做的改...

weixin_39561673的博客 547

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

用python和HY(lisp)代码为例学习什么是递归?

递归就是行为规则,是一种处理过程,递归也是最符合人类原生行为的编程方法,所以即使很复杂的问题,也比较容易用递归来实现算法。比如汉诺塔,使用递归代码非常简洁,使用循环则代码复杂很多。关于递归的资源消耗,确实要比普通函数多,不过很多编译器专门对递归进行了优化,许多Common Lisp 编译器(比如sbcl)都可以把尾递归转化成循环函数,这样就没有额外的资源消耗了。再回过头来看看ANSI Common Lisp关于递归的描述,加强一下记忆起初,许多人觉得递归函数很难理解。

skywalk8163的专栏 1102

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

lisp语言与python_Lisp 语言优点那么多,为什么国内很少运用?

为什么Lisp没有流行起来本文探讨的是为什么Lisp语言不再被广泛使用的。很久以前,这种语言站在计算机科学研究的前沿,特别是人工智能的研究方面。现在,它很少被用到,这一切并不是因为古老,类似古老的语言却被广泛应用.其他类似的古老的语言有 FORTRAN, COBOL, LISP, BASIC, 和ALGOL 家族,这些语言的唯一不同之处在于,他们为谁设计,FORTRAN是为科学家和工程师设计的,他...

weixin_31411315的博客 1101

探索编程新维度:Hy——Lisp与Python的完美融合

在编程世界中,Python以其简洁明了的语法和强大的生态系统赢得了广泛赞誉,而Lisp作为元编程的先驱,其独特的表达式结构和宏系统则为程序员提供了无尽的可能性。现在,我们很高兴地向您推荐一个将两者巧妙结合的开源项目——Hy。 ## 项目介绍 Hy是一个嵌入于Python中的Lisp方言。它的核心思想是让Lisp代码能够直接转换成Python抽象语法树(AST),从而使你在享受Lisp的优雅的同...

gitblog_00041的博客 600

编程奇妙之旅:使用Python写一个Lisp语言的解释器

Python号称最接近Lisp的语言,但它终究不是。但是因为几乎所有语言都是图灵完备的,所以即使Python无法实现Lisp的某个功能,也可以通过在Python中写一个Lisp解释器来实现那个功能。

skywalk8163的专栏 772

用Python实现一个Lisp解析器

基于Python正则表达式的Lisp解析器。

硫酸锌01的博客 664
上一篇: 如何成为一名黑客!
下一篇: Lisp语言学习计划
freemindhack
博客等级 码龄13年 53粉丝 182原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值