【问题标题】:Invoking a continuation vs a function call调用延续与函数调用
【发布时间】:2016-12-11 11:37:50
【问题描述】:

让我们考虑一个简单的阶乘函数示例,用类似 CPS 的伪风格编写(省略了中间结果的枚举和排序,因为会很吵):

(def (fact n k) (if (eq? n 0) (k 1) (fact (- n 1) (\result (k (* n result))))))

调用延续(k 1)(即“返回”一个值)在技术上与“正常”函数调用有何不同,例如在 else-branch 中?我想到的一件事是,在这种情况下,延续是唯一在它的论点中没有另一个延续的东西:)

另外,你能说这个计算类似于动态构造树的 DFS 树遍历,当前计算是当前探索的节点,“其他未探索的分支”是调用堆栈/延续?

【问题讨论】:

  • 延续的意义在于它们不会返回。他们是Goto,而不是Call
  • 没什么区别,就是普通函数。

标签: functional-programming continuations


【解决方案1】:

好吧,你已经回答了你的第一个问题:技术上的区别是没有更多的传递延续:这是释放到这一点的张力的点,即实际执行累积计算的点 [由β 减少序列]。 当然从语言的语义来看,这是一个普通的函数应用。

至于第二个问题,我可能理解错了,但我会说每次计算都是叶向树遍历,但树不是动态构造的,而是一个静态(通常是无限)对象 [唯一地] 由程序定义。但是,调用堆栈[即使它很微不足道,因为尾调用]是你已经走过的(从根到当前节点),而延续是你未来的道路,从将延续应用于某事的点(即当你应用这个事实函数时,下一个节点又是事实,除非 n 为 0)。

  (fact _ id)
   /      \
(= _ 0)?   otherwise
  |          \
 (id 1)    (fact _ (λ (x) (id (* 2 x))))
  |          /          \
  1       (= _ 0)?      otherwise
            |              \
      (id (* 2 1)))    (fact _ (λ (x) (id (* 2 (* 1 x)))))
            |                    /        \
          (id 2)               (= _ 0)?  otherwise
            |                    |           \ 
            2           (id (* 2 (* 1 1)))    ...
                                 |
                           (id (* 2 1))
                                 |
                              (id 2)
                                 |
                                 2

如果您喜欢从这些方向思考,您可能想阅读流程树,例如 Hatcliff 的“在线和离线部分评估简介”http://repository.readscheme.org/ftp/papers/pe98-school/hatcliff-DIKU-PE-summerschool.pdf——顺便说一句,PE 的话题非常有趣), 也许您可能会喜欢 Scott 的“流程图网格”(至少前 20 页左右)(https://www.cs.ox.ac.uk/files/3223/PRG03.pdf -- 实际上,恕我直言,这篇论文将“更自然”翻译成应用函数式语言)。

希望能给你一些见解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-17
    • 2016-09-30
    相关资源
    最近更新 更多