【问题标题】:Understanding block structure in MIT Scheme理解 MIT 方案中的块结构
【发布时间】:2016-11-20 02:43:47
【问题描述】:

以下面的函数定义为例:

(define (foo)
  (bar)
  (define (bar)
    (display "bar")))

这会产生错误:;Premature reference to reserved name: bar。相反,以下两个定义是合法的。请注意,我在他们两个中都使用了过早引用。

(define (foo)
  (bar))
(define (bar)
  (display "bar"))

(define (foo)
  (define (bar)
    (display "bar"))
  (bar))

我的问题是:为什么我不能在使用块结构时提前引用当前未定义的函数?为什么bar 是“保留名称”?

【问题讨论】:

  • 你不能,或者至少不应该能,因为内部defines 可能只出现在definelambda、@987654328 正文的开头@、let*letrec 等(根据 r5rs)。
  • @manualcrank 但是为什么呢?过早的引用可以使代码更全面,我认为这是一个很大的优势。允许define 出现在包含define 的正文中的任何地方有什么缺点?
  • 某些方案可能确实允许您的第一个定义,但在定义之前调用bar 时会引发错误。
  • 例如 Racket 将允许您定义(define (foo) (bar) (define (bar) (display "bar")) (bar))。但是请注意,在其定义之后额外的(bar); Racket 在一系列内部定义之后坚持至少一个表达式。 Chez Scheme,OTOH,在编译时拒绝这个和你原来的foo

标签: scheme lisp language-design sicp mit-scheme


【解决方案1】:

你可以引用它,但你不能使用什么还没有定义。

Racket 让你定义

(define (foo) 
  (bar)                    ; here the future error
  (define (bar) 
      (display "bar")) 
  (bar))

但是当您尝试调用 (foo) 时,消息会出错

    bar: undefined;
cannot use before initialization

所以,这里有一个特定的时间线,特定的序列;根据它们在定义中的文本位置,有些事情在其他事情之前完成。考虑这个进一步的 Racket REPL 交互:

> (define (foo) (baz) (define (bar) (display "bar")) (bar))
> (foo)
. . baz: undefined;
 cannot reference an identifier before its definition
> (define (baz) '())
> (foo)
bar

REPL 有自己的时间表。不过,每个 Scheme 实现都可以以自己的方式处理 REPL 的这一方面。

【讨论】:

  • (define (foo) (bar)) (define (bar) (display "bar")) 在这段代码中,时间线似乎有点混乱:(bar) 被引用并在其定义之前调用 。你能解释一下为什么会这样吗?
  • 那里有两个定义。第一个将foo 定义为调用(bar)——它还没有调用它,只是被定义为这样做,whenif 在将来调用。如果接下来确实调用了(foo),则会出现undefined: bar 消息出错。相反,第二个定义定义了bar,所以之后如果调用(foo),就不会出错。 --- 重要提示:一些 REPL 仍然会使其成为错误 - 它们没有定义 foo 看到的相同名称 bar,而是另一个 较新 bar。这就是我在最后一句话中暗示的。
  • so bar 在定义之前被引用,但没有被调用(使用)。
猜你喜欢
  • 1970-01-01
  • 2014-04-03
  • 2019-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-19
  • 2015-06-08
  • 2015-12-15
相关资源
最近更新 更多