【问题标题】:Scheme early "short circuit return"?计划提前“短路退货”?
【发布时间】:2011-01-26 22:01:47
【问题描述】:

我正在尝试找出如何在方案过程中实现“提前返回”,而不使用顶级 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 中会非常笨拙.

【问题讨论】:

  • 是的,但是如果你做了一个很大的条件并将每个 let-block 放在一个单独的函数中,并且名称选择得当,你最终会得到更好、更易读的代码。

标签: scheme continuations short-circuiting


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    您可以使用“当前继续调用”支持来模拟返回。 wikipedia 上有一个例子。该函数称为 call-with-current-continuation,尽管通常有一个别名称为 call/cc,这完全一样。还有一个稍微干净一点的例子here

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

    【讨论】:

      【解决方案3】:

      这里是如何使用 call/cc 自己构建return

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

      一些方案定义了一个名为 let/cc 的宏,它可以让您消除 lambda 的一些噪音:

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

      当然,如果你的 Scheme 没有,let/cc 写起来很简单。


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

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


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

      【讨论】:

      • 感谢您的解释。我最终使用了顶级 cond 并按照您的建议创建了单独的函数。
      【解决方案4】:

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

      它总是返回零的原因是,无论何时执行的主体,其结果都会掉在地板上。你看,函数定义形式中隐含的 lambda 也创建了一个隐含的开始块,所以

      (define foo 
        (lambda (b)
           (begin
             (let ...)
             0)))
      

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

      格雷姆

      【讨论】:

      • 是的。在 Scheme 中,如果一个 (lambda () stmt1 stmt2 stmt3) 中有多个表达式,则只返回最后一条语句的结果。在提问者的示例中,如果在末尾有 if 语句可以工作,则延续可能是矫枉过正。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-31
      • 1970-01-01
      • 2015-11-02
      • 1970-01-01
      相关资源
      最近更新 更多