【问题标题】:Unusual Scheme `let` binding, what is `f`?不寻常的方案`let`绑定,什么是`f`?
【发布时间】:2020-02-16 00:52:15
【问题描述】:

“The Scheme Programming Language 4th Edition” 3.3 节Continuations 中给出了以下示例:

(define product
  (lambda (ls)
    (call/cc
      (lambda (break)
        (let f ([ls ls])
          (cond
            [(null? ls) 1]
            [(= (car ls) 0) (break 0)]
            [else (* (car ls) (f (cdr ls)))]))))))

我可以确认它在 chezscheme 中有效:

> (product '(1 2 3 4 5))
120

上面let中的'f'是什么?为什么给定的ls 被分配给它自己?它似乎与我对 (let ...) 的理解不符,如 4.4 local binding 中所述:

syntax: (let ((var expr) ...) body1 body2 ...)

如果这里定义了“f”,我希望它在括号/方括号内:

(let ([f some-value]) ...)

【问题讨论】:

    标签: syntax binding scheme lisp let


    【解决方案1】:

    f 绑定到以let 的主体作为主体,ls 作为参数的过程。

    http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_sec_11.16

    【讨论】:

      【解决方案2】:

      这是 'named let',语法方便。

      (let f ([x y] ...)
        ...
        (f ...)
        ...)
      

      或多或少等同于

      (letrec ([f (λ (x ...)
                    ...
                    (f ...)
                    ...)])
        (f y ...))
      

      或者,在适当的情况下,到本地define,然后调用:

      (define (outer ...)
        (let inner ([x y] ...)
          ...
          (inner ...)
          ...))
      

      或多或少等同于

      (define (outer ...)
        (define (inner x ...)
          ...
          (inner ...)
          ...)
        (inner y ...))
      

      命名为let 的好处在于它将本地函数的定义和初始调用放在同一个地方。

      像我这样使用 CL 的穴居人有时会使用下面的 binding 之类的宏来实现这一点(注意这不是生产代码:它的所有错误消息都是晦涩的笑话):

      (defmacro binding (name/bindings &body bindings/decls/forms)
        ;; let / named let
        (typecase name/bindings
          (list
           `(let ,name/bindings ,@bindings/decls/forms))
          (symbol
           (unless (not (null bindings/decls/forms))
             (error "a syntax"))
           (destructuring-bind (bindings . decls/forms) bindings/decls/forms
             (unless (listp bindings)
               (error "another syntax"))
             (unless (listp decls/forms)
               (error "yet another syntax"))
             (multiple-value-bind (args inits)
                 (loop for binding in bindings
                       do (unless (and (listp binding)
                                       (= (length binding) 2)
                                       (symbolp (first binding)))
                            (error "a more subtle syntax"))
                       collect (first binding) into args
                       collect (second binding) into inits
                       finally (return (values args inits)))
               `(labels ((,name/bindings ,args
                           ,@decls/forms))
                  (,name/bindings ,@inits)))))
          (t
           (error "yet a different syntax"))))
      

      【讨论】:

      • TWIMC: "CL" == "Common Lisp"。我们都知道,Lisp 没有语法! ;) Common Lisp 更是如此。它的所有构造,以prog 开头,都只是 S 表达式! ;)
      • @WillNess:Lisp 有很多语法。事实上,由于它的语法是用户可扩展的,它的数量是无限的。它通常没有有很多词法语法。
      • @tfb 谢谢,您的回答很好地解决了这个问题。因为我充其量只是一个方案爱好者,所以这个特殊的形式没有引起我的注意,因为我没有阅读所有递归的东西。我希望它要么在普通的 let 部分进行解释,要么在该部分中明确引用它实际定义的部分的名称,或者有一个替代名称,如“letrec-and-execute”
      【解决方案3】:

      想想这个过程:

      (define (sum lst)
        (define (helper lst acc)
          (if (null? lst)
              acc
              (helper (cdr lst) 
                      (+ (car lst) acc))))
        (helper lst 0))
      
      (sum '(1 2 3)) ; ==> 6
      

      我们可以使用命名let 而不是定义一个本地过程,然后像这样使用它:

      (define (sum lst-arg)
        (let helper ((lst lst-arg) (acc 0))
          (if (null? lst)
              acc
              (helper (cdr lst) 
                      (+ (car lst) acc)))))
      

      除了一些重复的命名情况外,这些代码完全相同。 lst-arg 可以具有相同的名称 lst,并且它从不let 中的 lst 相同。

      命名为let 很容易掌握。 call/cc通常需要一些成熟。在我开始创建自己的实现之前,我没有得到call/cc

      【讨论】:

        猜你喜欢
        • 2021-05-03
        • 2015-10-08
        • 2012-08-06
        • 1970-01-01
        • 2014-08-13
        • 1970-01-01
        • 1970-01-01
        • 2011-09-23
        • 2023-03-26
        相关资源
        最近更新 更多