algorithm - "do"控制结构在Scheme 中起什么作用?

标签 algorithm scheme procedure

我遇到过一个Scheme 过程,它使用所谓的do 过程来工作,但我不知道它是如何工作的,或者它是如何实现的。如果有人可以提供帮助,我将不胜感激。 代码如下:

(define (storage-move-right vector i j)
  (do ((idx j (- idx 1)))
    O(n)
      ((< idx i))
        (vector-set! vector (+ idx 1) (vector-ref vector idx))))

最佳答案

“缺失”的循环构造 while、repeat-until 和 for

问题

控件构造while , repeat-untilfor存在于大多数主流编程语言中,但在Scheme 标准中却没有找到。方案中使用什么?

解决方案

这些构造都可以通过使用正常(尾递归)函数调用来“模拟”。然而,由于这些结构被如此频繁地使用,因此有一些更方便的语法糖可用。最常用的两个构造是使用 named letdo构造。

最好想到 do构造为一个美化的“do-until”循环。最简单的形式 do是:

(do ()
  [test return-exp]
  exp
  ...)

如果return-exp省略则返回值为 void ,“不可见的值”(如此命名是因为它不会打印在交互窗口中)。

在每个循环开始时,都会评估测试表达式。如果它不为假,则 return-exp被求值,其返回值是整个 do 的值-环形。如果测试为假,则按顺序计算主体表达式。在计算完最后一个主体表达式后,将开始一个新的循环。

; Example 1
; WARNING: This is not good style.
;          A better solution is found below.

                                  ; Pseudo Code
(define i 0)                      ; i := 0
(do ()                            ; do 
  [(= i 5)]                       ;   until i=5
  (display i)                     ;       display i
  (set! i (+ i 1)))               ;       i := i +1

; displays: 01234

do 的替代方案即“named let”是 let 语法的变体。它提供了比 do 更通用的循环结构,也可以用来表达递归。它的语法与普通 let 几乎相同。 ,但变量名可以在主体中使用,以使用 var1 的新值重复执行主体。 ...通过使用新值调用名称。

(let name ([var1 exp1]
           ...)
   body
   ...)

变量列表可以为空。 重写普通的 while 循环现在很容易:

while test           (do ()                 (let while ()
  begin                [(not test)]           (when test
    exp1               exp1                      exp1
    exp2               exp2                      exp2
    ...                ...)                      ...
  end                                            (while)))

(void)表达式的计算结果为“不可见”值,表示不打算使用返回值。它不是由交互窗口(REPL)打印的。

写一个for -循环 do要求我们看看 do 的下一个最简单的形式:

(do ([var start-exp next-exp])
  [test return-exp]
  exp
  ...)

do的评价-表达式以 start-exp 的计算开始。结果绑定(bind)到变量 i这在 next-exp 中都可见以及 test , return-expbody表达式。当循环重新启动时next-exp被求值并且变量var就是反弹到结果,之前test表达和(可能) body 被重新评估。

; Example 2
(do ([i 0 (+ i 1)])           (let loop ([i 0])
  [(= i 5)]                     (unless (= i 5)
  (display i))                     (display i)
                                   (loop (+ i 1))))
; displays: 01234

现在很清楚如何使用 do 编写普通的 for 循环:

for i=a to b step d         (do ([i a (+ i d)])          (let for ([i a])
  begin                       [(> i b)]                    (unless (> i b)
    exp1                      exp1                            exp1
    exp2                      exp2                            exp2
    ...                       ...)                            ...
  end                                                         (for (+ i d))))

repeat-until使用 do 编写循环很尴尬,但是使用命名的let我们可以执行如下操作:

repeat                
  begin        (let loop ()         (let loop ()
    exp1         exp1                 exp1
    exp2         exp2                 exp2
    ...          ...                  ...
  end            (if (not test)       (unless test (loop)))
until test           (loop)))

这个答案基于SchemeCookbook 中的一个条目。查看文件:https://web.archive.org/web/20131004034526/http://schemecookbook.org/Cookbook/IdiomLoopingConstructs

关于algorithm - "do"控制结构在Scheme 中起什么作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41086158/

相关文章:

list - 使用 Scheme 递归添加到列表

functional-programming - 是否可以使用call/cc实现递归?

java - 在检索输出参数的值之前应该调用 commit() 吗?

google-cloud-platform - 如何从 bq 命令行工具调用 BigQuery 过程?

c - 查询后在每个索引处查找最小值

javascript - 理解使用对象作为散列的 JavaScript 算法的复杂性

java - java中没有重复数字的反向整数

java - 优化算法以在堆栈中找到最大值

recursion - 埃拉托色尼筛法

mysql - 从 mssql 服务器更新链接服务器上的 mysql 表时出错