Scheme/Racket:做循环评估顺序

Cam*_*Cam 3 operator-precedence racket do-loops

以下过程在方案r6rs和Racket中都有效:

;; create a list of all the numbers from 1 to n
(define (make-nums n)
  (do [(x n (- x 1)) (lst (list) (cons x lst))]
    ((= x 0)
     lst)))
Run Code Online (Sandbox Code Playgroud)

我已经为r6rs和Racket测试了它并且它确实正常工作,但我只知道DrRacket肯定.

我的问题是,如果保证了一步表达式((- x 1)(cons x lst)在这种情况下)将按顺序进行评估.如果不能保证,那么我的程序不是很稳定.

我没有在任何一种语言的标准中看到任何指定这一点的东西,但我在这里问,因为当我测试时它按顺序进行了评估.

Eli*_*lay 8

它们通常不保证按顺序进行评估,但结果仍然相同.这是因为这里没有副作用 - 循环没有改变,x或者lst它只是将它们重新绑定到新值,因此评估两个步骤表达式的顺序是无关紧要的.

要看到这一点,请从代码更清晰的版本开始:

(define (make-nums n)
  (do ([x n (- x 1)] [lst null (cons x lst)])
      [(zero? x) lst]))
Run Code Online (Sandbox Code Playgroud)

转换为命名 - let:

(define (make-nums n)
  (let loop ([x n] [lst null])
    (if (zero? x)
      lst
      (loop (- x 1) (cons x lst)))))
Run Code Online (Sandbox Code Playgroud)

并进一步将其转换为辅助函数(这就是命名 - let实际上是):

(define (make-nums n)
  (define (loop x lst)
    (if (zero? x)
      lst
      (loop (- x 1) (cons x lst))))
  (loop n null))
Run Code Online (Sandbox Code Playgroud)

现在应该清楚的是,在递归loop调用中计算两个表达式的顺序并没有使它做任何不同的事情.

最后,请注意,在Racket评估保证从左到右.当有副作用时这很重要--Backet喜欢可预测的行为,而其他人反对它,声称这会导致人们对隐含依赖于此的代码.一个显示差异的常见小例子是:

(list (read-line) (read-line))
Run Code Online (Sandbox Code Playgroud)

在Racket中保证返回第一行读取的列表,然后是第二行.其他实现可能以不同的顺序返回两行.