关于Scheme中cond表达式的一个问题
A question about cond expressions in Scheme
使用 Chez Scheme 版本 9.5.5,考虑 cond
的两个示例:
(cond #t (else 2))
(cond (> 2 1) (else 2))
第一个表达式的计算结果为 #t
,而第二个表达式的计算结果为 1
。
好像第一个表达式先展开为(cond (#t #t) (else 2))
,而第二个表达式在求值前展开为(cond ((> 2 1) 1) (else 2))
。
我的问题是:由于表达式 (> 2 1)
的值是 #t
,因此两个 cond
表达式具有不同的值对我来说是违反直觉的。以这种方式定义 cond
是否有充分的理由?
编辑:我用 GNU Guile 3.0.1 尝试了这两个表达式:
- 表达式
(cond #t (else 2))
触发错误,显示“cond: invalid clause in subform #t of (cond #t (else 2)”。
- 与 Chez Scheme 9.5.5 一样,表达式
(cond (> 2 1) (else 2))
的计算结果为 1
。
- 表达式
(cond (#t) (else 2))
的计算结果为 #t
。
Edit2:使用 CHICKEN Scheme 5.1.0:
- 表达式
(cond #t (else 2))
触发错误,它说“在 (cond ...) 扩展期间 - 在 `cond' 中 - 不是正确的列表:#t”。
- 与 Chez Scheme 和 GNU Guile 一样,表达式
(cond (> 2 1) (else 2))
的计算结果为 1
。
- 表达式
(cond (#t) (else 2))
的计算结果为 #t
,但带有一条警告“警告:#t' clause in
cond' 之后的子句:(else 2)”。
一如既往,read the docs:
[test-expr]
The result of the test-expr is returned as the result of the cond form. The test-expr is not in tail position.
所以第一个表达式 returns #t
, 正如预期的那样。
第二个表达式也是returns #t
,我不确定你为什么说你得到1
。
如果你想return一些具体的东西,你应该正确地形成它:
(cond [#t "hey it's true"])
--> "hey it's true"
但是,Chez 使用了一些 C 主义,我相信整数作为布尔值就是其中之一。 Chez returned 1
for #t
并不让我感到惊讶。 (免责声明:我没有亲自使用过 Chez。)
第一个表达式在 Chez 9.5.4 中引发异常,这是最新的 release:
> (cond #t (else 2))
Exception: invalid syntax (cond #t (else 2))
Type (debug) to enter the debugger.
我不确定为什么 9.5.5 在这方面会有所不同;如果为真,这可能表明 9.5.5 中 cond
的底层实现发生了变化。
在任何情况下,the cond
form 接受一个或多个 <cond clause>
个参数,以及一个可选的 else
子句,其中 <cond clause>
必须采用 (<test> <expression1> ...)
的形式;即,<cond clause>
必须是一个带括号的表达式,其中包含一个测试形式和零个或多个表达式。每个 <cond clause>
都按顺序求值,直到 <test>
求值为真,之后的表达式按顺序求值,最终表达式的值为 returned。当真<cond clause>
中没有后续表达式时,<test>
的值是returned .
表达式 (cond #t (else 2))
违反了这一点(因为第一个 <cond clause>
格式错误)并且可能会引发语法错误,而 9.5.4 确实如此。如果 9.5.5 在这种情况下没有引发语法错误,则可能应该向维护者提交错误报告。再次注意,9.5.5 不是 Chez Scheme 的发布版本。
这在上面已经说过,但我会在这里重复:当<test>
表达式的计算结果为真,并且[=中没有后续表达式时103=]<cond clause>
,子句的计算结果为 <test>
。所以这预计会根据 R6RS 工作,并且在 9.5.4 中按预期工作:
> (cond (#t) (else 2))
#t
这也是表达 OP 问题中第二个表达式的正确方法。这里我将 else
子句修改为 return 3
以使其更清楚评估哪个分支:
> (cond ((> 2 1)) (else 3))
#t
> (cond ((> 1 2)) (else 3))
3
明确一点:上面第一个表达式中的第一个<cond clause>
是((> 2 1))
,其中包含<test>
(> 2 1)
。此测试的计算结果为 #t
,并且由于此子句中没有后续表达式,子句 returns #t
。第二个表达式的 ((> 1 2))
作为其 <cond clause>
,<test>
(> 1 2)
。此测试的计算结果为 #f
,因此计算结果将继续到下一个 <cond clause>
、(else 3)
,即 returns 3
.
问题中发布的未更改的第二个表达式在 9.5.4 中的行为与 OP 在 9.5.5 中报告的相同。此行为与 R6RS 指定的完全一样:
> (cond (> 2 1) (else 2))
1
这里的第一个<cond clause>
是形式(> 2 1)
,其中>
是<test>
,而2
和1
分别是<expression1>
和<expression2>
.现在,如果 <test>
的计算结果为真,则对后续表达式进行计算,最终表达式的值为 returned.
所以,对于 (cond (> 2 1) (else 2))
,第一个 <cond clause>
是 (> 2 1)
,对于 <test>
是表达式 >
,其计算结果为一个过程,该过程不是 #f
,因此是一个真值。然后依次计算后面的表达式2
和1
,最终表达式的值为1
,然后returned.
请注意,形式 (> 2 1)
在此处未被评估为 <test>
;单独的形式 >
被评估为 <test>
。此外,在这种情况下,(< 2 1)
的行为应与 (> 2 1)
相同。事实上,只要第一个元素的计算结果不是 #f
:
,任何类似形式的行为都与此相同
> (cond (> 2 1) (else 3))
1
> (cond (< 2 1) (else 3))
1
> (cond ('any-truthy-thing 2 1) (else 3))
1
版本更新
在我发布这个答案几天后,新版本的 Chez 发布了。请注意,OP 使用的是 9.5.5,它从来都不是发布版本(大概是 OP 从源代码构建了 9.5.5)。新版本是 9.5.6。我没有从源代码构建 9.5.5 来测试其中的任何一个;我当时使用的是当前版本,即 9.5.4。我现在已经在我的主机上更新到 9.5.6,并且 9.5.4 和 9.5.6 对于 OP 询问的表达式具有相同的行为。第一个 OP 表达式是 (cond #t (else 2))
,OP 发现它的计算结果为 #t
。在这种情况下,Chez 应该 not return #t
,但需要引发语法错误,如我在上面的回答中所述。 9.5.5 的维护者提出了一个问题,但该问题无法重现。第二个表达式是 (cond (> 2 1) (else 2))
;这个表达式在 9.5.4、OP 的 9.5.5 和现在的 9.5.6 版本中得到了正确处理。
使用 Chez Scheme 版本 9.5.5,考虑 cond
的两个示例:
(cond #t (else 2))
(cond (> 2 1) (else 2))
第一个表达式的计算结果为 #t
,而第二个表达式的计算结果为 1
。
好像第一个表达式先展开为(cond (#t #t) (else 2))
,而第二个表达式在求值前展开为(cond ((> 2 1) 1) (else 2))
。
我的问题是:由于表达式 (> 2 1)
的值是 #t
,因此两个 cond
表达式具有不同的值对我来说是违反直觉的。以这种方式定义 cond
是否有充分的理由?
编辑:我用 GNU Guile 3.0.1 尝试了这两个表达式:
- 表达式
(cond #t (else 2))
触发错误,显示“cond: invalid clause in subform #t of (cond #t (else 2)”。 - 与 Chez Scheme 9.5.5 一样,表达式
(cond (> 2 1) (else 2))
的计算结果为1
。 - 表达式
(cond (#t) (else 2))
的计算结果为#t
。
Edit2:使用 CHICKEN Scheme 5.1.0:
- 表达式
(cond #t (else 2))
触发错误,它说“在 (cond ...) 扩展期间 - 在 `cond' 中 - 不是正确的列表:#t”。 - 与 Chez Scheme 和 GNU Guile 一样,表达式
(cond (> 2 1) (else 2))
的计算结果为1
。 - 表达式
(cond (#t) (else 2))
的计算结果为#t
,但带有一条警告“警告:#t' clause in
cond' 之后的子句:(else 2)”。
一如既往,read the docs:
[test-expr]
The result of the test-expr is returned as the result of the cond form. The test-expr is not in tail position.
所以第一个表达式 returns #t
, 正如预期的那样。
第二个表达式也是returns #t
,我不确定你为什么说你得到1
。
如果你想return一些具体的东西,你应该正确地形成它:
(cond [#t "hey it's true"])
--> "hey it's true"
但是,Chez 使用了一些 C 主义,我相信整数作为布尔值就是其中之一。 Chez returned 1
for #t
并不让我感到惊讶。 (免责声明:我没有亲自使用过 Chez。)
第一个表达式在 Chez 9.5.4 中引发异常,这是最新的 release:
> (cond #t (else 2))
Exception: invalid syntax (cond #t (else 2))
Type (debug) to enter the debugger.
我不确定为什么 9.5.5 在这方面会有所不同;如果为真,这可能表明 9.5.5 中 cond
的底层实现发生了变化。
在任何情况下,the cond
form 接受一个或多个 <cond clause>
个参数,以及一个可选的 else
子句,其中 <cond clause>
必须采用 (<test> <expression1> ...)
的形式;即,<cond clause>
必须是一个带括号的表达式,其中包含一个测试形式和零个或多个表达式。每个 <cond clause>
都按顺序求值,直到 <test>
求值为真,之后的表达式按顺序求值,最终表达式的值为 returned。当真<cond clause>
中没有后续表达式时,<test>
的值是returned .
表达式 (cond #t (else 2))
违反了这一点(因为第一个 <cond clause>
格式错误)并且可能会引发语法错误,而 9.5.4 确实如此。如果 9.5.5 在这种情况下没有引发语法错误,则可能应该向维护者提交错误报告。再次注意,9.5.5 不是 Chez Scheme 的发布版本。
这在上面已经说过,但我会在这里重复:当<test>
表达式的计算结果为真,并且[=中没有后续表达式时103=]<cond clause>
,子句的计算结果为 <test>
。所以这预计会根据 R6RS 工作,并且在 9.5.4 中按预期工作:
> (cond (#t) (else 2))
#t
这也是表达 OP 问题中第二个表达式的正确方法。这里我将 else
子句修改为 return 3
以使其更清楚评估哪个分支:
> (cond ((> 2 1)) (else 3))
#t
> (cond ((> 1 2)) (else 3))
3
明确一点:上面第一个表达式中的第一个<cond clause>
是((> 2 1))
,其中包含<test>
(> 2 1)
。此测试的计算结果为 #t
,并且由于此子句中没有后续表达式,子句 returns #t
。第二个表达式的 ((> 1 2))
作为其 <cond clause>
,<test>
(> 1 2)
。此测试的计算结果为 #f
,因此计算结果将继续到下一个 <cond clause>
、(else 3)
,即 returns 3
.
问题中发布的未更改的第二个表达式在 9.5.4 中的行为与 OP 在 9.5.5 中报告的相同。此行为与 R6RS 指定的完全一样:
> (cond (> 2 1) (else 2))
1
这里的第一个<cond clause>
是形式(> 2 1)
,其中>
是<test>
,而2
和1
分别是<expression1>
和<expression2>
.现在,如果 <test>
的计算结果为真,则对后续表达式进行计算,最终表达式的值为 returned.
所以,对于 (cond (> 2 1) (else 2))
,第一个 <cond clause>
是 (> 2 1)
,对于 <test>
是表达式 >
,其计算结果为一个过程,该过程不是 #f
,因此是一个真值。然后依次计算后面的表达式2
和1
,最终表达式的值为1
,然后returned.
请注意,形式 (> 2 1)
在此处未被评估为 <test>
;单独的形式 >
被评估为 <test>
。此外,在这种情况下,(< 2 1)
的行为应与 (> 2 1)
相同。事实上,只要第一个元素的计算结果不是 #f
:
> (cond (> 2 1) (else 3))
1
> (cond (< 2 1) (else 3))
1
> (cond ('any-truthy-thing 2 1) (else 3))
1
版本更新
在我发布这个答案几天后,新版本的 Chez 发布了。请注意,OP 使用的是 9.5.5,它从来都不是发布版本(大概是 OP 从源代码构建了 9.5.5)。新版本是 9.5.6。我没有从源代码构建 9.5.5 来测试其中的任何一个;我当时使用的是当前版本,即 9.5.4。我现在已经在我的主机上更新到 9.5.6,并且 9.5.4 和 9.5.6 对于 OP 询问的表达式具有相同的行为。第一个 OP 表达式是 (cond #t (else 2))
,OP 发现它的计算结果为 #t
。在这种情况下,Chez 应该 not return #t
,但需要引发语法错误,如我在上面的回答中所述。 9.5.5 的维护者提出了一个问题,但该问题无法重现。第二个表达式是 (cond (> 2 1) (else 2))
;这个表达式在 9.5.4、OP 的 9.5.5 和现在的 9.5.6 版本中得到了正确处理。