Lisp的read eval打印循环与Python有什么不同?

2024-05-10 14:33:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我遇到了一个statement by Richard Stallman

'When you start a Lisp system, it enters a read-eval-print loop. Most other languages have nothing comparable to read, nothing comparable to eval, and nothing comparable to print. What gaping deficiencies! '

现在,我很少用Lisp编程,但我用Python编写了大量代码,最近用Erlang编写了一些代码。我的印象是,这些语言还提供read eval print循环,但Stallman不同意(至少关于Python):

'I skimmed documentation of Python after people told me it was fundamentally similar to Lisp. My conclusion is that that is not so. When you start Lisp, it does 'read', 'eval', and 'print', all of which are missing in Python.'

Lisp和Python的read eval print循环之间真的有根本的技术区别吗?您能举例说明Lisp REPL使之变得容易,而在Python中又很难做到的事情吗?


Tags: andofto代码youreadevalit
3条回答

在基于Lisp的系统中,通常在程序从REPL(read eval print loop)运行时开发程序。所以它集成了很多工具:完成、编辑器、命令行解释器、调试器。。。默认情况是这样。键入表达式时出错-您处于另一个REPL级别,并启用了一些调试命令。你必须做点什么来摆脱这种行为。

REPL概念有两种不同的含义:

  • 类似于Lisp(或其他一些类似语言)中的Read Eval Print循环。它读取程序和数据,评估并打印结果数据。Python不是这样工作的。Lisp的REPL允许您以元编程的方式直接工作,编写生成(代码)的代码,检查扩展,转换实际代码等。。Lisp将read/eval/print作为顶层循环。Python的顶部循环类似于readstring/evaluate/printstring。

  • 命令行界面。交互式外壳。有关IPython的示例,请参见。将其与常见的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
>>> 

你收到一条错误信息就这样。

将其与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]> 

CLISP使用Lisp的条件系统进入调试器REPL。它显示了一些重启。在错误上下文中,新的REPL提供了扩展命令。

让我们使用:R1重新启动:

Break 1 [2]> :r1
Use instead of A> 2
4
[3]> 

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

Stallman的观点是,与Lisps相比,没有实现显式的“reader”会使Python的REPL看起来很糟糕,因为它从REPL进程中删除了一个关键步骤。Reader是将文本输入流转换为内存的组件,可以想象一个内置于语言中的XML解析器,用于数据的源代码。这不仅对编写宏(理论上在Python中可以使用ast模块)有用,而且对调试和内省也很有用。

假设您对incf特殊表单的实现方式感兴趣。你可以这样测试:

[4]> (macroexpand '(incf a))
(SETQ A (+ A 1)) ;

但是incf可以做的远不止增加符号值。当要求增加哈希表条目时,它到底做了什么?让我们看看:

[2]> (macroexpand '(incf (gethash htable key)))
(LET* ((#:G3069 HTABLE) (#:G3070 KEY) (#:G3071 (+ (GETHASH #:G3069 #:G3070) 1)))
 (SYSTEM::PUTHASH #:G3069 #:G3070 #:G3071)) ;

这里我们了解到incf调用系统特定的puthash函数,这是这个常见Lisp系统的实现细节。请注意“printer”是如何利用“reader”已知的特性的,例如使用#:语法引入匿名符号,以及在扩展表达式的范围内引用相同的符号。在Python中模拟这种检查将更加冗长,而且不易访问。

除了在REPL中的明显使用之外,有经验的Lispers在代码中使用printread作为一个简单且易于使用的序列化工具,与XML或json类似。虽然Python具有str函数,相当于Lisp的print,但它缺少read的等价物,最接近的等价物是evaleval当然把两个不同的概念,解析和求值结合在一起,这导致了problems like thissolutions like this这是Python论坛上经常出现的话题。这在Lisp中并不是一个问题,因为读取器和求值器是完全分离的。

最后,reader工具的高级特性使程序员能够以甚至宏都无法提供的方式扩展语言。这种使困难成为可能的完美例子是Mark Kantrowitz的the ^{} package,他作为reader宏实现了一个功能齐全的中缀语法。

为了支持Stallman的立场,Python在以下方面与典型的Lisp系统不同:

  • Lisp中的read函数读取一个S表达式,该表达式表示可以作为数据处理或作为代码计算的任意数据结构。Python中最接近的东西是读取一个字符串,如果您想让它有任何意义,就必须自己解析它。

  • Lisp中的eval函数可以执行任何Lisp代码。Python中的eval函数只计算表达式,需要exec语句来运行语句。但这两种方法都适用于表示为文本的Python源代码,您必须跳过一堆箍来“评估”Python AST。

  • Lisp中的print函数写出的S表达式与read接受的格式完全相同。print在Python中,打印出由您试图打印的数据定义的内容,这当然不总是可逆的。

Stallman的声明有点不真诚,因为很明显Python确实有一个名为evalprint的函数,但是它们所做的事情与他所期望的不同(并且次于预期)。

在我看来,Python确实有一些类似于Lisp的方面,我可以理解为什么人们会建议Stallman研究Python。然而,作为Paul Graham argues in What Made Lisp Different,任何包含Lisp所有功能的编程语言也必须Lisp。

相关问题 更多 >