仅使用 cond 实现 Scheme 的 if 子句

Implementing Scheme's if clause using only cond

问题: 考虑一个不实现 if 构造但仅实现 cond 的元循环求值器。路易斯·雷纳 声称 if 是不必要的,因为我们可以使用元循环评估器评估以下过程:

(define (if predicate action alternative)
  (cond (predicate action)
        (else alternative)))

解释为什么这不起作用。特别是,假设您使用此 if 过程来定义阶乘:

(define (factorial n)
  (if (= n 1)
      1
      (* n (factorial (- n 1)))))

使用上面的 if 语句计算(阶乘 3)的结果使程序永远 运行,但我不知道为什么。对此的任何提示将不胜感激!谢谢!

if 表达式是一种 特殊形式 ,它不遵循与正常过程相同的求值规则 - 因此它不能由过程,它必须使用语法转换(例如,使用宏或在您的情况下,更改底层解释器)。要了解这一点,请看这个简单的例子:

(if #t 'ok (/ 1 0))
=> 'ok

被零除从未发生,因为只有表达式的 'ok 部分被计算。尝试使用 if 的实现来计算相同的表达式 - 你会得到一个 division by zero 错误。

所以现在你看,if 表达式根据条件的值进行不同的评估,仅执行结果 仅执行替代项,但从来没有。这就是您的 factorial 示例失败的原因 - 即使达到基本情况,也会执行另一个分支,从而创建无限循环。

请注意,if/cond 是语法,而不是过程。使用 if 语法 和实际定义它并尝试使用它是有区别的。为了说明,请考虑以下内容:

(+ 1 (- 4 2))

这里我们有 + 过程,它需要零个或多个 数字 和 returns 它们的总和。我们应用了带有两个参数的过程,第二个是表达式 (- 4 2) 而不是 value。请注意,这不会导致违反合同的错误,实际上评估为 3。为什么?因为任何不是值的参数首先被简化为值。所以表达式的计算结果为:

(+ 1 (- 4 2))
=> (+ 1 2)
=> 3

回到您的 defined if 过程,同样的规则适用,因为任何传递给函数的参数,如果不是值本身,都将被减少使用 if 过程之前的值。因此 (factorial 1) 的评估变为:

(factorial 1)
=> (if (= 1 1) 1 (* 1 (factorial 0)))
=> (if #t 1 (* 1 (factorial 0)))
=> (if #t 1 (* 1 (if (= 0 1) 1 (* 0 (factorial -1)))))
=> (if #t 1 (* 1 (if #f 1 (* 0 (factorial -1)))))
=> ... (endless loop to negative infinity)

这不同于使用 if 语法 ,其中求值是短路的,这意味着 else 情况仅被求值 if 谓词为假。例如,

(if #t 1 (+ 1 'a))

不会 在尝试添加数字和符号时抛出错误,因为它不会首先评估 else 表达式(因为谓词评估为真) .因此,它 returns 1.