Scheme “及早计划”;短路返回“;?

Scheme “及早计划”;短路返回“;?,scheme,continuations,short-circuiting,Scheme,Continuations,Short Circuiting,我试图找出如何在方案过程中进行“提前返回”,而不使用顶级if或cond类构造 (define (win b) (let* ((test (first (first b))) (result (every (lambda (i) (= (list-ref (list-ref b i) i) test)) (enumerate (length b))))) (when (and (not (= test 0)) result) te

我试图找出如何在方案过程中进行“提前返回”,而不使用顶级
if
cond
类构造

(define (win b)
 (let* ((test (first (first b)))
        (result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
                       (enumerate (length b)))))
  (when (and (not (= test 0)) result) test))
 0)
例如,在上面的代码中,我希望
win
返回
test
,如果满足
when
条件,否则返回0。但是,不管
when
条件的结果如何,过程始终返回0


我以这种方式构造代码的原因是,在这个过程中,我需要进行大量复杂的检查(多个块类似于示例中的
let*
),将所有内容放在一个大的
cond
中会非常笨拙。

一种方法是使用递归而不是循环,然后通过不进一步递归来实现提前退出。

您可以使用“调用当前延续”支持来模拟返回。上面有一个例子。该函数被称为当前延续的call,尽管通常有一个别名称为call/cc,这与此完全相同。还有一个稍微干净一点的例子


注意:这是一种相当先进的Scheme编程技术,一开始可能有点令人费解

以下是如何使用call/cc构建
return
您自己

(define (example x)
  (call/cc (lambda (return)
    (when (< x 0) (return #f))
    ; more code, including possible more calls to return
    0)))
(定义(示例x)
(呼叫/抄送(lambda(返回)
(当(
一些方案定义了一个名为let/cc的宏,该宏允许您消除lambda的一些噪声:

(define (example x)
  (let/cc return
    (when (< x 0) (return #f))
    0))
(定义(示例x)
(let/cc返回)
(当(
当然,如果您的方案没有这样做,那么let/cc编写起来就微不足道了


这是因为call/cc将调用它的点保存为延续。它将继续传递给它的函数参数。当函数调用该延续时,Scheme放弃它迄今为止建立的任何调用堆栈,并从调用/cc调用结束时继续。当然,如果函数从不调用continuation,那么它只会正常返回

直到您开始从该函数返回它们,或者可能将它们存储在全局数据结构中并在以后调用它们时,Continuations才变得真正令人费解。否则,它们就像任何其他语言的结构化goto语句(while/for/break/return/continue/exceptions/conditions)



我不知道您的完整代码是什么样子的,但最好使用cond并将复杂的检查分解为单独的函数。需要
return
let*
通常是过度命令式代码的症状。但是,call/cc方法现在应该可以让您的代码正常工作。

在这种情况下,您不需要when,而是需要if,尽管不是顶级的

(define (win b)
  (let* ((test (first (first b)))
         (result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
                        (enumerate (length b)))))
    (if (and (not (= test 0)) result) 
        test
        0)))
它总是返回零的原因是,无论执行时的主体是否被执行,其结果都会被扔到地板上。你看,函数define form中的lambda隐式也创建了一个隐式begin块,所以

(define foo 
  (lambda (b)
     (begin
       (let ...)
       0)))
begin的工作方式是,它返回内部最后一个表单的结果,同时将所有中间结果放到地板上。这些中间结果旨在产生副作用。您没有使用任何形式,这很好(!),但是您必须小心,在函数定义中只有一种形式(您真正想要的结果)


格雷姆

谢谢你的解释。我最终使用了顶级cond,并按照您的建议创建了单独的函数。是的。在Scheme中,如果在(lambda()stmt1 stmt2 stmt3)中有多个表达式,则只返回最后一条语句的结果。在asker的例子中,如果在末尾有一个if语句就可以工作,那么continuations可能会有点过头。是的,但是如果你做了一个大的cond,并用一个精心选择的名称将每个let块放在一个单独的函数中,你会得到更好、更可读的代码。