Lisp是REPL唯一的语言吗?

时间:2011-04-15 00:30:54

标签: scala programming-languages lisp read-eval-print-loop

Lisp(ruby,scala)以外的语言表示他们使用REPL(Read,Eval,Print,Loop),但不清楚REPL的含义是否与Lisp相同。 Lisp REPL与非Lisp REPL有何不同?

7 个答案:

答案 0 :(得分:31)

REPL 的想法来自Lisp社区。还有其他形式的文本交互式界面,例如命令行界面。一些文本接口还允许执行某种编程语言的子集。

REPL代表READ EVAL PRINT LOOP :(循环(打印(eval(读取))))。

在Lisp中,REPL不是命令行解释器(CLI)。 READ不读取命令,REPL不执行命令。 READ读取输入并将其转换为数据。因此READ函数可以读取所有类型的s表达式 - 而不仅仅是Lisp代码。

READ读取s表达式。这是一种也支持编码源代码的数据格式。 READ返回Lisp数据。

EVAL以Lisp数据的形式获取Lisp源代码并对其进行评估。可能发生副作用,EVAL返回一个或多个值。未定义如何使用解释器或编译器实现EVAL。实现使用不同的策略。

PRINT获取Lisp数据并将其打印为s表达式。

LOOP只围绕这个循环。在现实生活中,REPL更复杂,包括错误处理和子循环,即所谓的中断循环。如果出现错误,则会在错误的上下文中添加另一个REPL,并添加调试命令。在一次迭代中生成的值也可以重新用作下一次评估的输入。

由于Lisp都使用代码作为数据和功能元素,因此与其他编程语言略有不同。

类似的语言也提供类似的交互式界面。例如,Smalltalk也允许交互式执行,但它不像Lisp那样使用I / O的数据格式。对于任何Ruby / Python / ...交互式界面也是如此。

<强>问题:

那么阅读EXPRESSIONS,评估它们和打印它们的价值观的最初想法有多重要?这对于其他语言的作用是否重要:阅读文本,解析文本,执行文章,打印内容以及可选择打印返回值?通常不会真正使用返回值。

所以有两个可能的答案

  1. 一个Lisp REPL与大多数其他文本交互界面不同,因为它基于s表达式的数据I / O并对其进行评估。

  2. REPL是描述编程语言实现或其子集的文本交互界面的通用术语。

  3. 在实际实现中,Lisp REPL具有复杂的实现并提供许多服务,直到输入和输出对象的可点击演示(Symbolics,CLIM,SLIME)。高级REPL实现例如在SLIME(一种流行的基于Emacs的Common Lisp IDE),LispWorksAllegro CL中可用。

    Lisp REPL交互的示例

    产品和价格清单:

    CL-USER 1 > (setf *products* '((shoe (100 euro))
                                   (shirt (20 euro))
                                   (cap (10 euro))))
    ((SHOE (100 EURO)) (SHIRT (20 EURO)) (CAP (10 EURO)))
    

    订单,产品清单和金额:

    CL-USER 2 > '((3 shoe) (4 cap))
    ((3 SHOE) (4 CAP))
    

    订单的价格*是包含最后一个REPL值的变量。它不包含此值作为字符串,而是包含真实的实际数据。

    CL-USER 3 > (loop for (n product) in *
                      sum (* n (first (second (find product *products*
                                                    :key 'first)))))
    340
    

    但你也可以计算Lisp代码:

    让我们采用一个函数来增加两个参数的平方:

    CL-USER 4 > '(defun foo (a b) (+ (* a a) (* b b))) 
    (DEFUN FOO (A B) (+ (* A A) (* B B)))
    

    第四个元素就是算术表达式。 *指的是最后一个值:

    CL-USER 5 > (fourth *)
    (+ (* A A) (* B B))
    

    现在我们在它周围添加一些代码,将变量ab绑定到某些数字。我们使用Lisp函数LIST来创建一个新列表。

    CL-USER 6 > (list 'let '((a 12) (b 10)) *)
    (LET ((A 12) (B 10)) (+ (* A A) (* B B)))
    

    然后我们评估上面的表达式。同样,*指的是最后一个值。

    CL-USER 7 > (eval *)
    244
    

    每个REPL互动都会更新几个变量。先前值的示例为******。以前的输入也有+。这些变量的值不是字符串,而是数据对象。 +将包含REPL的读操作的最后结果。例如:

    变量*print-length*的价值是多少?

    CL-USER 8 > *print-length*
    NIL
    

    让我们看看如何阅读和打印列表:

    CL-USER 9 > '(1 2 3 4 5)
    (1 2 3 4 5)
    

    现在让我们将上面的符号*print-length*设置为3. ++指的是第二个先前的输入读数,作为数据。 SET设置符号值。

    CL-USER 10 > (set ++ 3)
    3
    

    然后上面的列表打印方式不同。 **指的是前面的第二个结果 - 数据,而不是文本。

    CL-USER 11 > **
    (1 2 3 ...)
    

答案 1 :(得分:11)

将REPL的概念视为只读,Eval,Print&amp;循环使用多种语言的REPL并不令人惊讶:

C/C++

C#/LINQ

Erlang

Haskell(在Windows上)

Java

Javascript

Perl

Python

Ruby

Scala

Smalltalk - 我在REPL上学到了它!

编辑我忘记了Java!

答案 2 :(得分:5)

我认为比较两种方法很有意思。 Lisp系统中的裸骨REPL循环如下所示:

(loop (print (eval (read))))

以下是REPL循环的两个实际Forth实现。我在这里什么也没留下 - 这是这些循环的完整代码。

: DO-QUIT   ( -- )  ( R:  i*x -- )
    EMPTYR
    0 >IN CELL+ !   \ set SOURCE-ID to 0
    POSTPONE [
    BEGIN           \ The loop starts here
        REFILL      \ READ from standard input
    WHILE
        INTERPRET   \ EVALUATE  what was read
        STATE @ 0= IF ."  OK" THEN  \ PRINT
        CR
    REPEAT
;

: quit
  sp0 @ 'tib !
  blk off
  [compile] [
  begin
    rp0 @ rp!
    status
    query           \ READ
    run             \ EVALUATE
    state @ not
    if ." ok" then  \ PRINT
  again             \ LOOP
;

Lisp和Forth做了完全不同的事情,特别是在EVAL部分,但也在PRINT部分。然而,他们分享这样一个事实,即两种语言的程序都是通过将其源代码提供给它们各自的循环来运行的,并且在这两种情况下代码都只是数据(尽管在Forth情况下它更像数据也是代码)。

我怀疑任何人只说LISP有一个REPL是READ循环读取DATA,由EVAL解析,并创建一个程序,因为CODE也是DATA。这种区别在很多方面都很有趣,关于Lisp和其他语言之间的区别,但就REPL而言,它根本没有关系。

让我们从外面考虑这个:

  1. READ - 从stdin
  2. 返回输入
  3. EVAL - 将所述输入作为语言
  4. 的表达式处理
  5. 打印 - 打印EVAL的结果
  6. LOOP - 返回阅读
  7. 没有进入实现细节,人们无法区分Lisp REPL,例如Ruby REPL。作为功​​能,它们是相同的。

答案 3 :(得分:4)

我猜你可以说Scala的“REPL”是一个“RCRPL”:Read,Compile,Run,Print。但由于编译器在内存中保持“热”状态,因此对于正在进行的交互来说速度相当快 - 启动时只需几秒钟。

答案 4 :(得分:3)

有许多人认为REPL的行为与LISP中的行为完全相同,或者它不是真正的REPL。相反,他们认为它有所不同,比如CLI(命令行解释器)。老实说,我倾向于认为如果它遵循以下基本流程:

  • 读取用户的输入
  • 评估输入
  • 打印输出
  • 循环回读

然后它是一个REPL。如上所述,有很多语言具有上述功能。

有关此类讨论的示例,请参阅this reddit thread

答案 5 :(得分:2)

有一个名为multi-repl的好项目,通过Node.JS公开各种REPL:

https://github.com/evilhackerdude/multi-repl

如果查看支持的语言列表,很明显不仅Lisp具有REPL的概念。

  • clj(clojure)
  • ghci(ghc)
  • IPython的
  • irb(ruby)
  • js(spidermonkey)
  • 节点
  • SBCL
  • V8

事实上,在Ruby中实现一个简单的过程非常简单:

repl = -> prompt { print prompt; puts(" => %s" % eval(gets.chomp!)) }
loop { repl[">> "] }

答案 6 :(得分:1)

  

Lisp REPL与非Lisp REPL有何不同?

让我们将Common Lisp的REPL与Python的IPython进行比较。

主要两点是:

  • Lisp是一种基于图像的语言。更改后无需重新启动进程/ REPL /整个应用程序。我们按功能编译代码功能(带有编译器警告等)。
  • 我们不会放松状态。更重要的是,当我们更新类定义时,REPL中的对象也会按照我们控制的规则进行更新。这样,我们可以在正在运行的系统中热重载代码。

通常,在Python中,您启动IPython或放入ipdb。您定义一些数据,直到尝试新功能。您编辑了源代码,然后想再试一次,因此退出IPython,然后重新开始整个过程​​。在Lisp(主要是Common Lisp)中,根本没有互动性。