我尝试实现一个“特殊 - 如果”,假设其行为与cond一样常规“if”。这是代码:
(define (special-if pre act alt)
(cond (pre act)
(else alt)))
为了测试这是否有效,我用这个“special-if”编写了一个阶乘函数:
(define (factorial n)
(special-if (= n 1)
1
(* n (factorial (- n 1)))))
但是,当我评估(阶乘3)时,它会永远运行。似乎谓词部分(= n 1)
从未被评估过。谁能告诉我为什么这不起作用?感谢。
答案 0 :(得分:4)
你的特殊if
注定会失败,我很害怕。请记住:if
是一种具有不同评估规则的特殊表单(cond
是使用if
实现的宏),这就是为什么这样的表达式运行正常,即使它有除以零:
(if true 'ok (/ 1 0))
=> 'ok
...而你的special-if
会引发错误,因为它使用了适用于程序的正常评估规则,这意味着在执行之前,所有参数都会被评估程序:
(special-if true 'ok (/ 1 0))
=> /: division by zero
现在你明白了你的代码失败的原因:当n
为1
时,在递归的底部结果结果将会执行!
(special-if (= 1 1)
1 ; this expression is evaluated
(* 1 (factorial (- 1 1)))) ; and this expression too!
... factorial
很乐意继续执行值0
,-1
,-2
等等,从而导致无限循环。结论:你必须使用现有的特殊形式(或定义一个新的宏)来实现条件行为,用户定义的标准程序在这里简单地工作。
答案 1 :(得分:2)
您已经听说为什么您的special-if
无法正常工作,但您接受的答案并没有告诉您如何使其发挥作用:< / p>
(define-syntax special-if
(syntax-rules ()
((_ pre act alt)
(cond (pre act)
(else alt)))))
它定义了一个在编译时扩展的宏。每次编译器看到与此模式匹配的内容并以special-if
开头时,它都会被显示的模板替换。因此,在pre
表单变为act
表单之前,alt
,special-if
和cond
不会被评估。
使用Scheme了解宏是如何工作的,因为Scheme最大限度地隐藏了真正发生的事情。如果您使用的是Common Lisp,那么宏将是一个简单的函数,它将未编译代码的片段(以列表,符号,字符串和数字的形式)作为参数,并返回未编译的代码作为其值。在Common Lisp中,special-if
只返回一个列表:
(defmacro special-if (pre act alt)
(list 'cond (list pre act)
(list t alt)))
Scheme宏以相同的方式工作,除了列表,符号等包含在&#34;语法对象&#34;它还包含词法信息,而不是使用列表
诸如car
和cdr
之类的运算符,Scheme和Racket提供了模式匹配和模板引擎,可以深入研究语法对象并处理额外数据(但不允许您处理任何它直接)。