Jan*_*bel 61 lisp python functional-programming read-eval-print-loop
我遇到Richard Stallman的以下声明:
'当你启动一个Lisp系统时,它会进入一个read-eval-print循环.大多数其他语言没有什么可比阅读,没有什么可比得上eval,没有什么比得上印刷.有什么差距不足!"
现在,我在Lisp中做了很少的编程,但我在Python中编写了大量代码,最近在Erlang中编写了一些代码.我的印象是这些语言也提供了read-eval-print循环,但Stallman不同意(至少关于Python):
"在人们告诉我它与Lisp基本相似之后,我浏览了Python的文档.我的结论是,情况并非如此.当你启动Lisp时,它会"读取","eval"和"print",所有这些都在Python中缺失.
Lisp和Python的read-eval-print循环之间真的存在根本的技术差异吗?你能举例说明Lisp REPL易于使用并且在Python中很难做到的事情吗?
Gre*_*ill 57
为了支持Stallman的立场,Python在以下方面与典型的Lisp系统不同:
readLisp中的函数读取一个S表达式,它表示可以被视为数据或被评估为代码的任意数据结构.Python中最接近的东西是读取一个字符串,如果你想要它意味着任何东西,你必须自己解析它.
evalLisp中的函数可以执行任何Lisp代码.evalPython中的函数仅评估表达式,并且需要exec语句来运行语句.但这两种方法都可以用Python源代码表示为文本,你必须跳过一堆箍来"评估"Python AST.
printLisp中的函数以完全相同的形式写出一个S表达式read.print在Python中打印出由您尝试打印的数据定义的内容,这当然不总是可逆的.
Stallman的说法是有点虚伪,因为明确的Python 也有完全相同的命名函数eval和print,但他们做的,他希望有什么不同的东西(和下).
在我看来,Python 确实有一些类似于Lisp的方面,我可以理解为什么人们可能会建议Stallman看看Python.但是,正如Paul Graham在What Lisp中所说的那样,任何包含Lisp所有功能的编程语言都必须是 Lisp.
use*_*342 30
Stallman的观点是,没有实现一个明确的"读者"使得Python的REPL与Lisps相比显得瘫痪,因为它从REPL过程中删除了一个关键步骤.Reader是将文本输入流转换为内存的组件 - 想象一下像语言中内置的XML解析器,用于源代码和数据.这不仅适用于编写宏(理论上可以在Python中使用ast模块),也适用于调试和内省.
假设您对incf特殊表单的实现方式感兴趣.你可以像这样测试它:
[4]> (macroexpand '(incf a))
(SETQ A (+ A 1)) ;
Run Code Online (Sandbox Code Playgroud)
但是incf除了递增符号值之外,还可以做更多的事情.当被要求增加哈希表条目时它究竟做了什么?让我们来看看:
[2]> (macroexpand '(incf (gethash htable key)))
(LET* ((#:G3069 HTABLE) (#:G3070 KEY) (#:G3071 (+ (GETHASH #:G3069 #:G3070) 1)))
(SYSTEM::PUTHASH #:G3069 #:G3070 #:G3071)) ;
Run Code Online (Sandbox Code Playgroud)
在这里,我们学习incf调用特定于系统的puthash函数,这是此Common Lisp系统的实现细节.请注意"打印机"如何利用"阅读器"已知的功能,例如使用#:语法引入匿名符号,以及引用扩展表达式范围内的相同符号.在Python中模拟这种检查会更冗长,更难以访问.
除了REPL的明显用途之外,经验丰富的Lispers 在代码中使用print和read作为一个简单易用的序列化工具,与XML或json相当.虽然Python具有str相当于Lisp的功能print,但它缺少等价的read,最接近的等价物eval.eval当然会混淆两个不同的概念,解析和评估,这会导致像这样的问题和类似的解决方案,并且是Python论坛上反复出现的主题.这在Lisp中不是一个问题,因为读者和评估者是完全分开的.
最后,阅读器工具的高级功能使程序员能够以甚至宏无法提供的方式扩展语言.马克·坎特罗维茨(Mark Kantrowitz )的infix软件包实现了功能强大的完美示例,将全功能的中缀语法实现为阅读器宏.
Rai*_*wig 20
在基于Lisp的系统中,通常在从REPL(读取评估打印循环)运行时开发程序.所以它集成了一堆工具:完成,编辑器,命令行解释器,调试器......默认情况下就是这样.键入带有错误的表达式 - 您处于另一个REPL级别,并启用了一些调试命令.你实际上必须做一些事情来摆脱这种行为.
您可以对REPL概念有两种不同的含义:
像Lisp(或其他一些类似的语言)中的Read Eval Print循环.它读取程序和数据,评估并打印结果数据.Python无法以这种方式工作.Lisp的REPL允许您直接以元编程方式工作,编写生成(代码)的代码,检查扩展,转换实际代码等.Lisp将read/eval/print作为顶部循环.Python有一些像readstring/evaluate/printstring作为顶部循环.
默认模式下Python的默认shell对于交互式使用来说并不是那么强大:
Python 2.7.2 (default, Jun 20 2012, 16:23:33)
[GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a+2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>>
Run Code Online (Sandbox Code Playgroud)
您收到错误消息,就是这样.
将其与CLISP REPL进行比较:
rjmba:~ joswig$ clisp
i i i i i i i ooooo o ooooooo ooooo ooooo
I I I I I I I 8 8 8 8 8 o 8 8
I \ `+' / I 8 8 8 8 8 8
\ `-+-' / 8 8 8 ooooo 8oooo
`-__|__-' 8 8 8 8 8
| 8 o 8 8 o 8 8
------+------ ooooo 8oooooo ooo8ooo ooooo 8
Welcome to GNU CLISP 2.49 (2010-07-07) <http://clisp.cons.org/>
Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2010
Type :h and hit Enter for context help.
[1]> (+ a 2)
*** - SYSTEM::READ-EVAL-PRINT: variable A has no value
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead of A.
STORE-VALUE :R2 Input a new value for A.
ABORT :R3 Abort main loop
Break 1 [2]>
Run Code Online (Sandbox Code Playgroud)
CLISP使用Lisp的条件系统来破解调试器REPL.它提出了一些重启.在错误上下文中,新的REPL提供扩展命令.
让我们使用:R1重启:
Break 1 [2]> :r1
Use instead of A> 2
4
[3]>
Run Code Online (Sandbox Code Playgroud)
因此,您可以获得程序和执行运行的交互式修复......
Python的交互模式与Python的"从文件读取代码"模式有几种不同的关键方式,可能是语言的文本表示所固有的.Python也不是homoiconic,这使我称之为"交互模式"而不是"read-eval-print loop".除此之外,我会说它更多的是等级差异而不是实物差异.
现在,在Python代码文件中,您可以轻松地插入空行:tatstactually接近"实物差异":
def foo(n):
m = n + 1
return m
Run Code Online (Sandbox Code Playgroud)
如果您尝试将相同的代码粘贴到解释器中,它会认为该函数是"已关闭"并抱怨您在错误的缩进处有一个裸返回语句.这不会发生在(Common)Lisp中.
此外,在Common Lisp(CL)中有一些相当方便的便利变量在Python中是不可用的(至少据我所知).既CL和Python有"最后一个表达式的值"(*在CL,_在Python),但CL还具有**(最后之前表达的值),并***与(在此之前的一个的值)+,++和+++(表达式本身).CL也不区分表达式和语句(实质上,一切都是表达式),所有这些都有助于构建更丰富的REPL体验.
正如我在开始时所说的,它在等级上的差异比在实物上的差异更大.但是如果差距只是他们之间的差距更大,那么它可能也会有所不同.