我正在尝试从Scheme中的文件中读取数据,我想知道为什么在使用(read)
或(read-line)
获取值之后为什么需要反转列表。示例:
]=> (list 1 (read) 2 (read) 3)
Hello
world
;Value 39: (1 world 2 hello 3)
为什么结果列表中“世界”在“你好”之前?干杯。
答案 0 :(得分:2)
参数的计算顺序按标准未定义。这是part from the R6RS report:
注意:与Lisp的其他方言相反,评估顺序 未指定,以及运算符表达式和操作数 表达式始终使用相同的求值规则求值。
尽管未指定评估顺序,但效果 运算符和操作数表达式的任何并发求值 被约束为与 评价。每种评估的顺序可以选择不同 过程调用。
这里是一个示例:
(define (debug x)
(display x)
x)
(define (add x y)
(debug (+ (debug x) (debug y))))
(add (add 3 4)
(add 5 6))
; ==> 18
以下是display
调用的可能结果:
347561171118 ; strictly left to right
651143771118 ; strictly right to left
561134771118 ; ltr in add, rtl in call to add (consistent)
437651111718 ; rtl in add, ltr in call to add (consistent)
作为示例,我知道MIT计划严格从右至左执行,而DrRacket严格从左至右执行。伊卡洛斯获得了第三名。
未充分说明这些原因是为了在编写规范时允许未知的优化。顺序并不总是很重要,特别是如果您习惯于写。如果您需要正确的订单,可以这样写:
(let ((r1 (read)))
(let ((r2 (read)))
(list 1 r1 2 r2 3)))
由于绑定总是在您安全之前进行评估。它有一个快捷键,因此您可以将其写为let*
:
(let* ((r1 (read))
(r2 (read)))
(list 1 r1 2 r2 3))
答案 1 :(得分:0)
Scheme语言未定义函数自变量的求值顺序。也就是说,在评估(f (g) (h))
时,未定义对g
的调用是发生在对h
的调用之前还是之后(只有这两者都在对f
的调用之前发生) )。
对于您的代码,这意味着未定义对read
的两次调用中的哪一个首先发生。在某些实现中,您可能会获得期望的订单,而在其他实现中,您将获得您在此处看到的订单。
为了强制按照您想要的顺序进行调用,应在调用{{1之前使用let
或define
将read
的结果存储在变量中}}。
答案 2 :(得分:-2)
我想我明白了。由于Scheme中的每个列表实际上都是(cons ...)
的递归,因此上述列表实际上是:
(cons
1
(cons
(read) ; (2)
(cons
2
(cons
(read) ; (1)
(cons
3
'()
)
)
)
)
)
因此,很清楚为什么标记为(read)
的标记为(1)的对象要早于标记为(read)
的标记为(2)的地方。