你如何 运行 等价于 scheme 中的嵌套 for 循环
How do you run equivalent of nested for loops in scheme
我正在研究如何将此伪代码转换为惯用方案:
for c in range(1, 1000):
for b in range(1, c):
for a in range(1, b):
do something if condition is true
在 Scheme 中表达任何类型的循环(以及更通用的控制结构)都非常容易。例如,如果你想要一个简单计数的循环,你可以这样做:
(let loop ([i 0])
(if (>= i 10)
(values)
(begin
(display i)
(loop (+ i 1)))))
你显然可以嵌套这些。
但这远不如您在 Python:
中所写的那样容易阅读
for i in range(10):
print(i)
嗯,好的。但是 Lisp-family 语言是 构建语言 :如果你想要的语法不存在,你 让它存在 。这是一个这样做的例子:
(define-syntax nloop*
;; Nested numerical loop
(syntax-rules ()
[(_ () form ...)
(begin form ...
(values))]
[(_ ((variable lower-inclusive upper-exclusive) more ...) form ...)
(let loop ([variable lower-inclusive])
(if (< variable upper-exclusive)
(begin
(nloop* (more ...) form ...)
(loop (+ variable 1)))
(values)))]
[(_ ((variable start-inclusive end-exclusive step) more ...) form ...)
(let ([cmp? (if (>= step 0) < >)])
(let loop ([variable start-inclusive])
(if (cmp? variable end-exclusive)
(begin
(nloop* (more ...) form ...)
(loop (+ variable step)))
(values))))]))
现在:
> (nloop* ((i 0 10))
(print i))
0123456789
而且
> (nloop* ((i 0 10)
(j 20 0 -4))
(displayln (list i j)))
(0 20)
(0 16)
(0 12)
(0 8)
(0 4)
...
(9 20)
(9 16)
(9 12)
(9 8)
(9 4)
所以我刚刚发明的这个 nloop*
构造将完美地完成一般数字循环,包括嵌套循环(这就是为什么 *
: nloop
(不存在)会并行循环)。
当然,在 industrial-strength scheme-derived 语言中,比如 Racket,已经有这样做的构造:
(for ([i (in-range 10)])
(for ([j (in-range 20 0 -4)])
(displayln (list i j))))
将是惯用的球拍方式。但是 for
及其所有变体和基础结构只是语言结构,原则上您可以 自己 在非常 bare-bones 的方案中编写。
这些语言专为构建语言而设计。
你的伪代码被直接翻译成 Scheme 作为嵌套的命名 let
s 的序列,如
(let loopc ((c 1)) ; start the top loop with c = 1,
(if (> c 1000) ; if `c > 1000`,
#f ; exit the top loop ; or else,
(let loopb ((b 1)) ; start the first-nested loop with b = 1,
(if (> b c) ; if `b > c`,
(loopc (+ c 1)) ; end the first-nested loop, and
; continue the top loop with c := c+1 ; or else,
(let loopa ((a 1)) ; start the second-nested loop with a = 1,
(if (> a b) ; if `a > b`,
(loopb (+ b 1)) ; end the second-nested loop, and
; continue the first-nested loop with b := b+1
(begin ; or else,
(if condition ; if condition holds,
(do_some a b c) ; do something with a, b, c,
#f) ; and then,
(loopa (+ a 1)) ; continue the second-nested loop with a := a+1
)))))))
当然有点复杂,容易出错。另一方面,通过从 loopa
中调用 loopc
可以很容易地缩短最内层循环(在 a
s 上)并直接重新启动顶部循环(在 c
s 上) ](总是在尾部位置,注意!),如果需要的话。
例如,上面的代码直接适用于您在评论中提出的问题,即找到总和为 1000 的毕达哥拉斯三元组。此外,当您找到解决方案时,您可以直接调用 (loopc 1001)
立即退出整个三重嵌套循环构造。
作为旁注,
for c in range(3, 1000):
for b in range(2, c):
for a in range(1, b):
if a**2 + b**2 == c**2 and a + b + c == 1000:
print(a * b * c)
不是最有效的解决方案。至少,首先,
for c in range(3, 1000):
for b in range(2, 1000-c-1):
for a in range(1, 1000-c-b):
if a**2 + b**2 == c**2 and a + b + c == 1000:
print(a * b * c)
此外,
for c in range(3, 1000):
c2 = c**2
for b in range(2, 1000-c-1):
a = 1000-c-b
if a**2 + b**2 == c2:
print(a * b * c)
# exit the loops
有很多方法可以解决您的问题。这是我现在想到的最简单的:
(define iota
(lambda (n k)
((lambda (s) (s s 1))
(lambda (s x)
(k x)
(or (= n x)
(s s (+ x 1)))))))
(iota 1000
(lambda (c)
(iota c
(lambda (b)
(iota b
(lambda (a)
(newline)
(display a) (display " ")
(display b) (display " ")
(display c) (display " ")))))))
如果 c
的初始区间是 1..3
而不是 1..1000
,则嵌套代码将包含 (a b c)
:
的这些值
a b c
1 1 1
1 1 2
1 2 2
2 2 2
1 1 3
1 2 3
2 2 3
1 3 3
2 3 3
3 3 3
我正在研究如何将此伪代码转换为惯用方案:
for c in range(1, 1000):
for b in range(1, c):
for a in range(1, b):
do something if condition is true
在 Scheme 中表达任何类型的循环(以及更通用的控制结构)都非常容易。例如,如果你想要一个简单计数的循环,你可以这样做:
(let loop ([i 0])
(if (>= i 10)
(values)
(begin
(display i)
(loop (+ i 1)))))
你显然可以嵌套这些。
但这远不如您在 Python:
中所写的那样容易阅读for i in range(10):
print(i)
嗯,好的。但是 Lisp-family 语言是 构建语言 :如果你想要的语法不存在,你 让它存在 。这是一个这样做的例子:
(define-syntax nloop*
;; Nested numerical loop
(syntax-rules ()
[(_ () form ...)
(begin form ...
(values))]
[(_ ((variable lower-inclusive upper-exclusive) more ...) form ...)
(let loop ([variable lower-inclusive])
(if (< variable upper-exclusive)
(begin
(nloop* (more ...) form ...)
(loop (+ variable 1)))
(values)))]
[(_ ((variable start-inclusive end-exclusive step) more ...) form ...)
(let ([cmp? (if (>= step 0) < >)])
(let loop ([variable start-inclusive])
(if (cmp? variable end-exclusive)
(begin
(nloop* (more ...) form ...)
(loop (+ variable step)))
(values))))]))
现在:
> (nloop* ((i 0 10))
(print i))
0123456789
而且
> (nloop* ((i 0 10)
(j 20 0 -4))
(displayln (list i j)))
(0 20)
(0 16)
(0 12)
(0 8)
(0 4)
...
(9 20)
(9 16)
(9 12)
(9 8)
(9 4)
所以我刚刚发明的这个 nloop*
构造将完美地完成一般数字循环,包括嵌套循环(这就是为什么 *
: nloop
(不存在)会并行循环)。
当然,在 industrial-strength scheme-derived 语言中,比如 Racket,已经有这样做的构造:
(for ([i (in-range 10)])
(for ([j (in-range 20 0 -4)])
(displayln (list i j))))
将是惯用的球拍方式。但是 for
及其所有变体和基础结构只是语言结构,原则上您可以 自己 在非常 bare-bones 的方案中编写。
这些语言专为构建语言而设计。
你的伪代码被直接翻译成 Scheme 作为嵌套的命名 let
s 的序列,如
(let loopc ((c 1)) ; start the top loop with c = 1,
(if (> c 1000) ; if `c > 1000`,
#f ; exit the top loop ; or else,
(let loopb ((b 1)) ; start the first-nested loop with b = 1,
(if (> b c) ; if `b > c`,
(loopc (+ c 1)) ; end the first-nested loop, and
; continue the top loop with c := c+1 ; or else,
(let loopa ((a 1)) ; start the second-nested loop with a = 1,
(if (> a b) ; if `a > b`,
(loopb (+ b 1)) ; end the second-nested loop, and
; continue the first-nested loop with b := b+1
(begin ; or else,
(if condition ; if condition holds,
(do_some a b c) ; do something with a, b, c,
#f) ; and then,
(loopa (+ a 1)) ; continue the second-nested loop with a := a+1
)))))))
当然有点复杂,容易出错。另一方面,通过从 loopa
中调用 loopc
可以很容易地缩短最内层循环(在 a
s 上)并直接重新启动顶部循环(在 c
s 上) ](总是在尾部位置,注意!),如果需要的话。
例如,上面的代码直接适用于您在评论中提出的问题,即找到总和为 1000 的毕达哥拉斯三元组。此外,当您找到解决方案时,您可以直接调用 (loopc 1001)
立即退出整个三重嵌套循环构造。
作为旁注,
for c in range(3, 1000):
for b in range(2, c):
for a in range(1, b):
if a**2 + b**2 == c**2 and a + b + c == 1000:
print(a * b * c)
不是最有效的解决方案。至少,首先,
for c in range(3, 1000):
for b in range(2, 1000-c-1):
for a in range(1, 1000-c-b):
if a**2 + b**2 == c**2 and a + b + c == 1000:
print(a * b * c)
此外,
for c in range(3, 1000):
c2 = c**2
for b in range(2, 1000-c-1):
a = 1000-c-b
if a**2 + b**2 == c2:
print(a * b * c)
# exit the loops
有很多方法可以解决您的问题。这是我现在想到的最简单的:
(define iota
(lambda (n k)
((lambda (s) (s s 1))
(lambda (s x)
(k x)
(or (= n x)
(s s (+ x 1)))))))
(iota 1000
(lambda (c)
(iota c
(lambda (b)
(iota b
(lambda (a)
(newline)
(display a) (display " ")
(display b) (display " ")
(display c) (display " ")))))))
如果 c
的初始区间是 1..3
而不是 1..1000
,则嵌套代码将包含 (a b c)
:
a b c
1 1 1
1 1 2
1 2 2
2 2 2
1 1 3
1 2 3
2 2 3
1 3 3
2 3 3
3 3 3