【问题标题】:SICP example doesn't work on RacketSICP 示例不适用于 Racket
【发布时间】:2013-05-14 09:20:04
【问题描述】:

我正在尝试 SICP 第 4 章的示例(编写 LISP 解释器的一部分)

(define (definition-value exp)
    (if (symbol? (cadr exp))
        (caddr exp)
        (make-lambda 
                    (cdadr exp) ; formal parameters
                    (cddr exp)  ; body
        )              
    )
) 


(define (make-lambda parameters body)
    (cons 'lambda (cons parameters body))
)

我测试过了,'(define (double x) (+ x x))) 上的定义值应该返回一个 lambda 函数

( (definition-value '(define (double x) (+ x x))) 10)

球拍输出

procedure application: expected procedure, given: (lambda (x) (+ x x)); arguments were: 10

“(lambda (x) (+ x x))”不是一个过程吗?或者它是一个参考?如果是引用,有什么方法可以“取消引用”它?

【问题讨论】:

    标签: functional-programming lisp scheme racket


    【解决方案1】:

    definition-value 返回作为参数提供给它的定义表达式中的值:

    (definition-value '(define x 42))  
    => 42
    
    (definition-value '(define (qq x) (+ x y 42))) 
    => (make-lambda '(x) '((+ x y 42)))
    => '(lambda (x) (+ x y 42))
    

    您不能像您那样将引用列表作为函数调用:( '(lambda (x) (+ x y 42)) 10) 无效。它不是一个函数,它只是一个s-expression

    definition-valuean interpreter 的一部分。这个解释器是“取消引用”的方式,即解释函数定义。不同的解释器可以有不同的方式来解释相同的函数定义,从而为生成的语言提供不同的语义。

    表达式的评估必须在上下文中进行 - 它们出现在特定的 lexical scope(代码中变量可见的区域)内,这会产生 environments(也就是 this )。在上面的示例中,y 被定义在某个封闭范围内在被解释的程序中。试图通过调用Racket's eval 来解释REPL 中的那个表达式,y 有什么值?

    【讨论】:

    • 这是我的第一个猜测,定义值返回一个引用。但是,我尝试了 ((eval '(lambda (x) (+ x x))) 10) => 20,这个 eval 是内置的 eval。但是 ( (eval (definition-value '(define (double x) (+ x x))) ) 10) => compile: unbound identifier (and no #%app syntax transformer is bound) at: lambda in: (lambda (x ) (+ x x))。我正在努力学习这一章,我知道最后我应该有一个 eval 的工作版本。但是,这真的很长,我想分别学习和测试每个功能。
    • 每个函数都做它该做的事。 definition-value 操作 s 表达式。评估它们不是其讨论范围的一部分。
    • 我很困惑,如果definition-value真的返回一个s-expression '(lambda (x) (+ x x))) 10),内置的eval函数(与定义无关-value 一旦它返回一个值)应该能够评估它,不是吗?
    • 谁知道 Racket 在其 REPL 中做了什么?您可以尝试将其分为两个步骤:(setq x (definition-value '(define (double x) (+ x x))))。然后,((eval x) 10)。那会发生什么? (但是这个测试eval 和它测试definition-value 一样多;在这里只检查x 的值就足够了)。
    • 它实际上是这样工作的,(define x (definition-value '(define (double x) (+ x x)))), ((eval f) 10) =>20 并且没有错误
    【解决方案2】:

    我想出了答案,如果在文件中执行 Racket 脚本,racket 解释器不知道命名空间,但是,REPL 知道它。解决方法是在文件开头添加这一行

    (define ns (make-base-namespace))
    

    然后在使用的时候把ns传给eval

    (eval <what ever code reference here> ns)
    

    这将使我上面提到的示例有效。

    【讨论】:

    • 最初的问题是““(lambda (x) (+ x x)) 不是一个过程吗?”,而不是“我如何评估引用的 lambda 表达式?”。当你改变主意时,你应该就 SO 提出一个新的问题。
    猜你喜欢
    • 2023-03-26
    • 2015-12-13
    • 1970-01-01
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    • 2015-03-07
    • 2021-10-27
    • 1970-01-01
    相关资源
    最近更新 更多