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/Linux | Windows, 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] |
| 测试 | Lisp | Java | Python | Perl | C++ | ||
|---|---|---|---|---|---|---|---|
| 哈希访问 | 1.06 | 3.23 | 4.01 | 1.85 | 1.00 | ||
| 异常处理 | 0.01 | 0.90 | 1.54 | 1.73 | 1.00 | 图例说明 | |
| 对文件中的数字进行求和 | 7.54 | 2.63 | 8.34 | 2.49 | 1.00 | > 100 x C++ | |
| 反转行 | 1.61 | 1.22 | 1.38 | 1.25 | 1.00 | 50-100 x C++ | |
| 矩阵相乘 | 3.30 | 8.90 | 278.00 | 226.00 | 1.00 | 10-50 x C++ | |
| 堆排序 | 1.67 | 7.00 | 84.42 | 75.67 | 1.00 | 5-10 x C++ | |
| 数组访问 | 1.75 | 6.83 | 141.08 | 127.25 | 1.00 | 2-5 x C++ | |
| 列表处理 | 0.93 | 20.47 | 20.33 | 11.27 | 1.00 | 1-2 x C++ | |
| 对象实例化 | 1.32 | 2.39 | 49.11 | 89.21 | 1.00 | < 1 x C++ | |
| 单词计数 | 0.73 | 4.61 | 2.57 | 1.64 | 1.00 | ||
| 中位数 | 1.67 | 4.61 | 20.33 | 11.27 | 1.00 | ||
| 25% to 75% | 0.93 to 1.67 | 2.63 to 7.00 | 2.57 to 84.42 | 1.73 to 89.21 | 1.00 to 1.00 | ||
| 范围 | 0.01 to 7.54 | 0.90 to 20.47 | 1.38 to 278 | 1.25 to 226 | 1.00 to 1.00 |
| 123 | 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 |
| 123 | class C: def f(self): return self.f ## bad Python ... |
| 12345 | >>> 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程序 simple.lisp | Python程序 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 Program | Running 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 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 randomdef 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()) |