关于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 尝试了这两个表达式:

Edit2:使用 CHICKEN Scheme 5.1.0:

一如既往,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> ,而21分别是<expression1><expression2> .现在,如果 <test> 的计算结果为真,则对后续表达式进行计算,最终表达式的值为 returned.

所以,对于 (cond (> 2 1) (else 2)),第一个 <cond clause>(> 2 1),对于 <test> 是表达式 >,其计算结果为一个过程,该过程不是 #f,因此是一个真值。然后依次计算后面的表达式21,最终表达式的值为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 版本中得到了正确处理。