用cond定义是否有效

时间:2015-04-08 22:25:50

标签: scheme racket

我尝试实现一个“特殊 - 如果”,假设其行为与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)从未被评估过。谁能告诉我为什么这不起作用?感谢。

2 个答案:

答案 0 :(得分:4)

你的特殊if注定会失败,我很害怕。请记住:if是一种具有不同评估规则的特殊表单(cond是使用if实现的宏),这就是为什么这样的表达式运行正常,即使它有除以零:

(if true 'ok (/ 1 0))
=> 'ok

...而你的special-if会引发错误,因为它使用了适用于程序的正常评估规则,这意味着在执行之前,所有参数都会被评估程序:

(special-if true 'ok (/ 1 0))
=> /: division by zero

现在你明白了你的代码失败的原因:当n1 时,在递归的底部结果结果将会执行!

(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表单之前,altspecial-ifcond不会被评估。

使用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;它还包含词法信息,而不是使用列表 诸如carcdr之类的运算符,Scheme和Racket提供了模式匹配和模板引擎,可以深入研究语法对象并处理额外数据(但不允许您处理任何它直接)。