Scheme中的“短路返回”是什么意思?

5

我正在尝试找出如何在Scheme过程中进行“早期返回”,而不使用顶层的ifcond结构。

我想知道如何在Scheme过程中实现“早期返回”,但不想使用顶层的ifcond结构。
(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)

例如,在上面的代码中,如果满足when条件,我希望win返回test,否则返回0。然而,实际发生的是,无论when条件的结果如何,该过程都将始终返回0。
我之所以以这种方式构造我的代码,是因为在这个过程中,我需要执行许多复杂的检查(类似于示例中的多个let*块),把所有内容放在一个大的cond中会非常笨拙。

1
是的,但如果你创建了一个大的条件语句,并将每个let块放在一个单独的函数中,并选择一个合适的名称,那么你最终会得到更好、更易读的代码。 - John Clements
4个回答

7

以下是使用call/cc构建return的方法。

(define (example x)
  (call/cc (lambda (return)
    (when (< x 0) (return #f))
    ; more code, including possible more calls to return
    0)))

一些 Scheme 定义了一个名为 let/cc 的宏,可以让你省略 lambda 中的一些噪音:

(define (example x)
  (let/cc return
    (when (< x 0) (return #f))
    0))

当然,如果你的Scheme不支持,let/cc很容易编写。
这是因为call/cc保存了调用它的点作为一个continuation。它将该continuation传递给它的函数参数。当函数调用该continuation时,Scheme放弃了它已经建立的任何调用栈,并从call/cc调用的结尾继续执行。当然,如果函数从未调用continuation,那么它就会正常返回。
只有当你开始从该函数返回它们或者将它们存储在全局数据结构中并稍后调用它们时,continuations才会变得真正令人费解。否则,它们就像其他任何语言的结构化goto语句(while/for/break/return/continue/exceptions/conditions)一样。
我不知道你的完整代码是什么样子的,但最好使用cond并将复杂的检查分解成单独的函数。需要return和let*通常是过度命令式代码的症状。然而,现在使用call/cc方法应该可以让你的代码运行起来。

感谢您的解释。最终我使用了顶层的cond,并按照您的建议创建了单独的函数。 - Suan

1
一种方法是使用递归而不是循环,这样可以通过不再递归来实现早期退出。

0
在这种情况下,您不需要一个 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)))

它总是返回零的原因是,无论 when 的主体是否被执行,其结果都会被丢弃。你看,函数定义形式中的 lambda 隐式地创建了一个隐式的 begin 块,因此
(define foo 
  (lambda (b)
     (begin
       (let ...)
       0)))

而 begin 的工作方式是返回内部最后一个表单的结果,同时将所有中间结果丢弃在地上。这些中间结果旨在具有副作用。你没有使用其中任何一个,这很棒 (!),但你必须小心,只在函数定义内部有一个需要的结果。

Grem


是的。在Scheme中,如果您在(lambda() stmt1 stmt2 stmt3)中有多个表达式,则仅返回最后一个语句的结果。在提问者的示例中,当在结尾处使用if语句时,延续可能过度。 - erjiang

0

您可以使用“调用当前延续”支持来模拟返回。在wikipedia上有一个示例。该函数称为call-with-current-continuation,尽管通常有一个别名称为call/cc,它完全相同。这里还有一个稍微更清晰的示例here

注意:这是一种相当高级的Scheme编程技术,一开始可能会有点费解...!!!!


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