"do" 控制结构在 Scheme 中有什么作用?
What does the "do" control construct do in Scheme?
我 运行 反对 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))))
"missing" 循环构造 while、repeat-until 和 for
问题
控制结构 while
、repeat-until
和 for
存在于大多数主流编程语言中,但在 Scheme 标准中却找不到它们。 Scheme中用到了什么?
解决方案
这些结构都可以 "emulated" 通过使用正常的(尾递归)函数调用。然而,由于这些结构经常被使用,所以有一些更方便的语法糖可用。两个最常用的构造是使用 named let
和 do
构造。
最好将 do
构造视为美化的 "do-until" 循环。 do
的最简单形式是:
(do ()
[test return-exp]
exp
...)
如果省略 return-exp
则 returned 值为 void
、"the invisible value"(如此命名是因为它没有在交互中打印 window).
在每个循环开始时,都会计算测试表达式。如果它不为假,则计算 return-exp
并且其 return 值是整个 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)
表达式的计算结果为 "invisible" 值,表示不打算使用 return 值。它不是由交互 window(REPL)打印的。
要用 do
编写一个 for
循环需要我们看下一个最简单的 do 形式:
(do ([var start-exp next-exp])
[test return-exp]
exp
...)
do
表达式的计算从 start-exp
的计算开始。结果绑定到变量 i
,它在 next-exp
以及 test
、return-exp
和 body
表达式中都可见。当循环重新启动时,next-exp
被评估并且变量 var
被重新计算到结果,在 test
表达式和(可能)主体被重新评估之前。
; 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
写起来很尴尬,但是使用named 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
我 运行 反对 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))))
"missing" 循环构造 while、repeat-until 和 for
问题
控制结构 while
、repeat-until
和 for
存在于大多数主流编程语言中,但在 Scheme 标准中却找不到它们。 Scheme中用到了什么?
解决方案
这些结构都可以 "emulated" 通过使用正常的(尾递归)函数调用。然而,由于这些结构经常被使用,所以有一些更方便的语法糖可用。两个最常用的构造是使用 named let
和 do
构造。
最好将 do
构造视为美化的 "do-until" 循环。 do
的最简单形式是:
(do ()
[test return-exp]
exp
...)
如果省略 return-exp
则 returned 值为 void
、"the invisible value"(如此命名是因为它没有在交互中打印 window).
在每个循环开始时,都会计算测试表达式。如果它不为假,则计算 return-exp
并且其 return 值是整个 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)
表达式的计算结果为 "invisible" 值,表示不打算使用 return 值。它不是由交互 window(REPL)打印的。
要用 do
编写一个 for
循环需要我们看下一个最简单的 do 形式:
(do ([var start-exp next-exp])
[test return-exp]
exp
...)
do
表达式的计算从 start-exp
的计算开始。结果绑定到变量 i
,它在 next-exp
以及 test
、return-exp
和 body
表达式中都可见。当循环重新启动时,next-exp
被评估并且变量 var
被重新计算到结果,在 test
表达式和(可能)主体被重新评估之前。
; 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
写起来很尴尬,但是使用named 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