【问题标题】:The use of let in the divisors function除数函数中let的使用
【发布时间】:2018-03-28 20:31:34
【问题描述】:
(定义除数 (λ (n) (让 f((i 2)) (条件((> = i n)'()) ((整数?(/ n i))(缺点i(f(+ i 1)))) (否则(f(+i 1)))))))

我对@9​​87654323@ 部分以及使用(f (+ i 1)) 的递归调用有点困惑。

f 会变成全部((i 2)) 吗? (f (+ i 1)) 部分是如何工作的?我认为(+ i 1) 变成了3,但是会发生什么?

编辑:我不太确定let 是如何工作的。我不太确定如何表达我不明白的内容。如果有人可以解释 let 通常如何与该递归调用一起工作,那将很有帮助。我见过let 的其他示例,但我不明白它在这种方法中是如何工作的。

edit2:谢谢,我不知道它被称为namedlet。我正在查找let,我找到了let*,但他们没有重新命名为let

【问题讨论】:

标签: scheme racket


【解决方案1】:

作为let 的第一部分的符号称为命名let。你可能知道一个标准的let

(let ((i 2))
  body ...)

只是立即调用的匿名函数的缩写:

((lambda (i)
   body ...)
 2)

使用命名为let,您可以为该函数命名,以便您可以将其作为函数从主体中调用。除了阴影较少之外,它相当于:

(define (divisors n)
  (define (f i)
    (cond
 ((>= i n) '())
          ((integer? (/ n i))
     (cons i (f (+ i 1))))
          (else (f (+ i 1)))))
  (f 2))

所以要回答您的问题,最初的i2。然后递归调用将i 增加到3 等等,方式与上述代码相同。与所有调用一样,绑定变量是调用的本地变量,因此每一轮都有自己的i

现在,如果您查看此版本以及名为let 的版本,您会注意到let 中的参数和绑定更接近,因此代码的意图更容易理解。此外,当您知道命名let 允许您使用新绑定调用同一个块时,您会认为命名let 比替代方法更容易阅读。

【讨论】:

  • 关于i 如何变成3 的快速问题?在命名的 let 版本中,你将f 3 传递给f (i 2) 会不会变成f (3 2) 然后3 会变成2
  • @cicero866 (let f ((i 2)) ...) 表示函数f首先被调用,第一个参数i是值2。当您调用 (f 3) 时,f 将被调用,i3。您需要再次阅读我的答案,并注意我的等效代码与命名let 发生的事情完全相同。
  • @WillNess 但是,如果将其与 define + apply 等效项进行比较,它就是一种函数调用。我尽量避免说 goto,因为每次调用 f 都会返回,有时还会返回调用 f,这不是 goto-y。
  • @Sylwester 啊,是的,我说的不是非常精确,至少可以说......嘿嘿。 ((i 2)) 虽然仍然非常不是函数调用(只是为了让 OP 清楚)。顺便说一句,你有没有见过/写过 (loop ... (loop ...)) 那种命名为 let 的代码?它适合哪里,也许是某种树遍历?
  • @WillNess Tree traversal 适合,但您不会使用名称 loop,因为它暗示尾递归,并且最多只有一个后续分支是尾调用。
猜你喜欢
  • 1970-01-01
  • 2019-05-12
  • 2017-10-14
  • 2020-01-19
  • 2013-05-31
  • 2014-08-05
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多