4

以下过程在方案 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)))

我已经对 r6rs 和 Racket 进行了测试,它确实可以正常工作,但我只知道 DrRacket 是肯定的。

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

我没有在这两种语言的标准中看到任何具体说明,但我在这里问是因为当我测试它时,它是按顺序评估的。

4

1 回答 1

10

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

要查看这一点,请从看起来更简洁的代码版本开始:

(define (make-nums n)
  (do ([x n (- x 1)] [lst null (cons x lst)])
      [(zero? x) lst]))

转换为命名let-:

(define (make-nums n)
  (let loop ([x n] [lst null])
    (if (zero? x)
      lst
      (loop (- x 1) (cons x lst)))))

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

(define (make-nums n)
  (define (loop x lst)
    (if (zero? x)
      lst
      (loop (- x 1) (cons x lst))))
  (loop n null))

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

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

(list (read-line) (read-line))

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

于 2010-09-15T03:23:17.417 回答