方案中的let语法

时间:2017-06-18 12:27:36

标签: loops scheme let named chicken-scheme

在我找到的一个例子中,我无法理解let的这种用法。我正在使用鸡肉计划。

(let loop ()
    (print "hello world")
    (loop)
)

它是一个简单的无限循环,它以递归方式调用自身,我无法理解的是语法。我知道第一个参数必须是对((<var[1]> <value[1]>)...(<var[n]> <value[n]))的列表,其他参数是let的主体。 那么,为什么这个片段有用呢?

2 个答案:

答案 0 :(得分:3)

这是一个named let,它是辅助程序的简写,通常用于使用递归进行循环,参数随着递归的进行而提前(尽管在代码中没有使用参数)。例如,此过程:

(define (test)
  (let loop ((i 5))
    (cond ((<= i 0) 'ok)
          (else (print i)
                (loop (- i 1))))))

......相当于这一个:

(define (test)
  (define (loop i)
    (cond ((<= i 0) 'ok)
          (else (print i)
                (loop (- i 1)))))
  (loop 5))

现在你看到问题中的代码片段与写这个代码片段相同:

(define (loop)
  (print "hello world")
  (loop))

(loop)

另请注意,名称“循环”只是一种约定,您可以将其命名为“iter”或“helper”或您想要的任何其他东西,它并不重要。

答案 1 :(得分:2)

它的工作原理是因为你关于第一个“参数”的声明需要是一个绑定列表是错误的。 let的语法是这样的:

(let name ((binding expression) ...)
  body ...)

该名称是可选的。您可以拥有零个或多个绑定。宏如何看待它的名称是由于绑定需要是一个列表,而名称绝对需要是一个标识符。没有这个名字,它就像:

((lambda (binding ...)
   body ...)
 expression ...)

然而,名称变为:

((letrec ((name (lambda (binding ...)
                 body ...)))
   name)
 expression ...)

当然letrec是根据let

定义的
(letrec ((name expression) ...)
  body ...)
; ===
(let ((name 'undefined) ...)
  (let ((tmp expression) ...)
    (set! name tmp) ...)
  body ...)

因此,将上面命名的let转为:

(((lambda (name)
   ((lambda (tmp) (set! name tmp)) (lambda (binding ...) body ...))
   name)
  'undefined)
 expression ...)

注意这个名字没有绑定在它首次调用的帧上的技巧。它被返回并立即被调用。因此,你可以从自由变量中实际评估name的表达式,而不会引起名字let的干扰:

(let ((name "sylwester"))
  (let name ((cur (list name))
             (n 2))
    (if (zero? n)
        cur
        (name (append cur cur) (- n 1)))))
; ==> ("sylwester" "sylwester" "sylwester" "sylwester")

使用绑定的define阴影:

(let ((name "sylwester"))
  (define (name cur n) 
    (if (zero? n)
        cur
        (name (append cur cur) (- n 1))))

  (name (list name) 2))
; ==> (#<proc> #<proc> #<proc> #<proc>)

R6RS report syntax for letelsewhere in the report没有展示更复杂的名为let,但添加了对其描述enter image description here的引用。这可能是因为当你只需要本地绑定时,命名let可能会让人感到压力和困惑。这个和define是顶级的两个不同的东西,而不是Scheme中最令人困惑的部分,因此如果这些语言实际上有不同的名称而不是记录在不同的地方,那么对于初学者来说可能更容易掌握。