仅使用 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.
问题: 考虑一个不实现 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.