仅使用cond实现Scheme的if子句

时间:2017-04-17 02:39:07

标签: scheme lisp racket sicp

问题: 考虑一个不实现if结构的metacircular评估器,但只实现cond。路易斯理查德 声称如果没有必要,因为我们可以使用metacircular评估员评估以下程序:

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

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

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

使用上面的if语句评估(factorial 3)的结果使程序永远运行,但我不知道为什么。任何暗示都将不胜感激!谢谢!

2 个答案:

答案 0 :(得分:1)

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

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

从未发生除零,因为只评估了表达式的'ok部分。尝试使用if的实现来评估相同的表达式 - 您将收到division by zero错误。

现在你看,if表达式的计算方式不同,具体取决于条件的值,只执行结果只执行替代,但不能同时执行。这就是你的factorial示例失败的原因 - 即使达到基本情况,另一个分支也会被执行,从而创建一个无限循环。

答案 1 :(得分:0)

请注意if/cond是语法,而不是过程。使用if 语法并实际定义并尝试使用它有所不同。为了说明,请考虑以下事项:

(+ 1 (- 4 2))

这里我们有+程序,它接受零个或多个数字并返回它们的总和。我们已经使用两个参数应用了该过程,第二个是表达式(- 4 2)而不是。请注意,这不会导致合同违规错误,并且实际评估为3.为什么?因为任何非值的参数首先被简化为值。所以表达式的评估结果如下:

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

回到定义的 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 #t 1 (+ 1 'a))

会因尝试添加数字和符号而抛出错误,因为它不会首先评估else-expression(因为谓词的计算结果为true)。因此,它返回1.