Lisp的read-eval-print循环与Python的不同之处是什么?

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 有完全相同的命名函数evalprint,但他们做的,他希望有什么不同的东西(和下).

在我看来,Python 确实有一些类似于Lisp的方面,我可以理解为什么人们可能会建议Stallman看看Python.但是,正如Paul Graham在What Lisp中所说的那样,任何包含Lisp所有功能的编程语言都必须 Lisp.

  • Python对数据和代码进行了区分.LISP没有.另见[格林斯普的第十条规则](http://en.wikipedia.org/wiki/Greenspun's_tenth_rule) (11认同)
  • 实际上,RMS可能更喜欢`print()`使用`repr()`而不是`str()`.也就是说,`print(repr(eval(raw_input(">"))))`非常接近REPL. (2认同)
  • @HenkLangeveld虽然这样吗?Python具有一流的代码表示; lisp以字符序列的形式进行文本输入. (2认同)
  • 但是 python 交互式提示不会显示“单个字符串”。它读取完整的表达式(通常跨越几行),该表达式可以计算为数据或代码(表达式或语句)。由于函数是Python中的一等对象,因此“eval”的等价物是简单地运行该对象:“name()”,如果“name”引用一个函数。只有“print”确实具有不同的属性:打印 python 表达式或函数通常不会给我们提供可以再次以相同方式解析的内容。 (2认同)

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 在代码中使用printread作为一个简单易用的序列化工具,与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作为顶部循环.

  • 命令行界面.交互式shell.例如,参见IPython.与Common Lisp的SLIME相比.

默认模式下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)

因此,您可以获得程序和执行运行的交互式修复......

  • @Marcin,确实如此.但默认的"解释器"提示对于交互式开发来说并不是非常强大.就像一个CLI,而不是一个读/读/打印的REPL.Stallman来自一个互动提示具有更多功能的世界.包括他自己的Emacs. (5认同)
  • 是的,但关于python的事情是解释器提示**促进了交互式开发.这听起来像是RMS看着python,正确地得出结论,它实际上并不是lisp,并宣称它是劣等的. (3认同)
  • @Marcin:我的印象是CLISP REPL比Python默认提供的功能强大得多. (3认同)
  • 在实践中,根据我自己的经验,我不会说Common Lisp repl本身更有帮助. (2认同)
  • 我正在考虑CLISP,CMUCL和SBCL的复制品.将IPython与SLIME进行比较就像将sed与Emacs进行比较一样.它们甚至不是同一个东西,无论如何,这个问题的主题都不是. (2认同)
  • 此外,可以通过执行“import pdb;”来访问 python 解释器中的错误根源。pdb.pm()`。不同之处在于,Python 默认情况下不会自动将您带到调试器(我更喜欢这种调试器)。 (2认同)

Vat*_*ine 6

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体验.

正如我在开始时所说的,它在等级上的差异比在实物上的差异更大.但是如果差距只是他们之间的差距更大,那么它可能也会有所不同.

  • @Marcin这不是一个严格的要求,但我唯一一次听说过read-eval-print循环这个术语一直是同性语言,其余的都倾向于指"交互模式"或"解释器"(基本上,如果Python有一个REPL,那么Sinclair Basic也是如此 (2认同)
  • @Vatine是的,这就是我们在这里讨论的重点:Python的交互式提示不是REPL。还要注意,Sinclair的提示离REPL更远:您不能重复使用它打印的任何内容。甚至没有TTY的概念,保留了输出历史记录,就像在连续进纸打印机(原始TTY)上一样。 (2认同)