经验丰富的策划者,letcc和guile

10

在这里有几个关于 letcc 的问题,它是在《通晓Scheme的厨师》中使用的。

(define (intersect-all sets)
  (letcc hop
    (letrec
      ((A (lambda (sets)
            (cond
              ((null? (car sets)) (hop '())
              ((null? (cdr sets)) (car sets))
              (else
                (intersect (car sets)
                           (A (cdr sets)))))))
       ; definition of intersect removed for brevity
      (cond
        ((null? sets) '())
        (else (A sets))))))
  1. 我认为我理解了letcc的作用,它基本上和Ruby(似乎是Common Lisp也是)中的catchthrow类似,这意味着通过调用命名的letcc可以缩短一整个代码块。在这个简短的书系列中,这感觉像是我遇到过的最不“函数式”的事情,这让我有点不敢使用它,因为我想学习一种良好的函数式风格。我是不是对letcc有什么误解,或者它不是真正的函数式编程概念,只存在于提高性能方面?整个想法是我可以在某个例程中间,然后突然到达代码的另一个点,感觉有点不对劲...就像在Java中滥用try/catch以控制程序流。

  2. letcc在我安装在OS X上的guile版本(1.8.7)中似乎不存在。我应该在guile中寻找另一个名称吗?

  3. 如果我通过将其与Java中的try/catch或Ruby中的catch/throw进行比较来误解letcc(需要明确的是,对于非Ruby程序员来说,这并不是异常处理),那么在函数级别上它实际上是如何工作的?它能否以更复杂的方式表达,让我相信它实际上是函数式的?


1
如果您的实现支持“普通”续体和逃逸续体,通常使用后者更便宜。您示例中的模式自然适用于逃逸续体。标准缩写是 let/ec - soegaard
FYI,此页面展示了如何使用简单的宏在call/cc的基础上定义letcc - user725091
@JonO,我之前看过并尝试了这个方法,但是Guile只是报错ERROR: invalid syntax () ABORT: (misc-error)。我已经按照页面上的方式操作了,但仍然无法正常工作。也许等我学习宏后就能找到问题所在了。 - d11wtq
1
谁知道这个在哪里被记录了,但是在定义宏之前需要(use-syntax (ice-9 syncase))(至少是按照这种方式定义的) :) - d11wtq
1个回答

13
  1. "Functional"有几种含义,但没有一种流行的含义与延续存在冲突。但是,它们可能被滥用以创建难以阅读的代码。它们不是可以“滥用程序流程”的工具 - 它们是程序流程工具。

  2. 我无法帮助你那里。我知道最近在Guile中有过有关延续的讨论,但我不知道情况如何。它肯定应该有call-with-current-continuation,通常也称为更友好的call/cc,而let/cc是一个简单的宏,可以通过call/cc来构建。

    我可以告诉你,在Racket中有一个let/cc内置函数,还有一堆其他内置函数属于同一族,此外还有一个完整的库,包含各种控制运算符(并附有广泛的参考文献列表)。

  3. let/cc的简单用法确实类似于捕捉/抛出的方式 - 更具体地说,这些延续通常被称为“逃逸延续”(有时也称为“向上”)。这就是你在代码中使用的那种用法,通常用于实现abortreturn

    但是,在Scheme中,延续是可以在任何地方使用的东西。为了展示这种差异的非常简单的示例,请尝试执行以下操作:

  4. (define (foo f) (f 100))
    (let/cc k (+ (foo k) "junk") (more junk))
    
  5. 最后,如果你想了解更多关于continuations的信息,你可以查看PLAI相关部分,还有Matthew Might写的更为简明扼要的基于示例的概述,你也可以查看我写的一些基于PLAI的课堂笔记,其中包含一些受后者文章启发的示例。


2
哇,读了Matthew Right的那篇文章之后,我的看法改变了。我认为实际理解call/cc的工作方式使它看起来比我最初想象的要干净得多。非常感谢解释。事实证明,我的guile版本具有call/cccall-with-current-continuation,但没有letcc。此外,与ruby中的catch/throw相比,call/cc更加有趣,因为它可以让你做更酷的事情。我猜《The Seasoned Schemer》会在后面的章节中更详细地探讨这个话题。 - d11wtq
1
如果你已经看完了他的文本,那么也可以看看我的课堂笔记:有几个更加详细的例子,包括生成器和amb的完整开发,并且它以CPS的描述开始——这是在其他语言中实现某些内容的方式。 - Eli Barzilay
4
顺便说一下,他的名字是Matthew Might。(他也许是正确的,但他的名字就是Might。) - Eli Barzilay
let/cc 实际上是比 goto 更强大的跳转语句。如果滥用,它们会比 goto 更糟糕。当然,像 switch 一样,let/cc 也有其合法的使用情况。 - BlackVegetable
一个简单的Haskell中letcc和throw的实现,可能会帮助某些人理解letcc的工作原理。 - Dwayne Crooks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接