定义 cond 是否不起作用
define if with cond doesn't work
我尝试实现一个 "special-if",假设它的行为与常规 "if" 一样,带有 cond。这是代码:
(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)))))
但是,当我评估 (factorial 3) 时,它会永远运行。似乎谓词部分 (= n 1)
从未被评估过。谁能告诉我为什么这行不通?谢谢
你的专项if
恐怕注定要失败。请记住:if
是一种具有不同计算规则的特殊形式(而 cond
是使用 if
实现的宏),这就是为什么像这样的表达式即使被零除也能正常运行:
(if true 'ok (/ 1 0))
=> 'ok
... 而你的 special-if
会引发错误,因为它使用适用于程序的正常评估规则,这意味着它的所有参数都在 之前 被评估执行过程:
(special-if true 'ok (/ 1 0))
=> /: division by zero
现在你明白为什么你的代码失败了:在递归的底部,当 n
是 1
both 时,结果和替代将执行!
(special-if (= 1 1)
1 ; this expression is evaluated
(* 1 (factorial (- 1 1)))) ; and this expression too!
... 而 factorial
将愉快地继续执行 0
、-1
、-2
等值,从而导致死循环。底线:您必须使用现有的特殊形式(或定义新的宏)来实现条件行为,用户定义的标准过程在这里根本行不通。
您听说过 为什么 您的 special-if
不起作用,但您接受了一个没有告诉您如何使其起作用的答案:
(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,宏将是一个简单的函数,它将未编译代码片段(以列表、符号、字符串和数字的形式)作为参数,并将 returns 未编译代码作为其值。在 Common Lisp 中,special-if
只是 return 一个列表:
(defmacro special-if (pre act alt)
(list 'cond (list pre act)
(list t alt)))
方案宏的工作方式相同,除了列表、符号等被包装在还包含词法信息的 "syntax objects" 中,而不是使用列表
car
和 cdr
、Scheme 和 Racket 等运算符提供了模式匹配和模板引擎,可深入研究语法对象并处理额外数据(但不允许您直接处理任何数据) ).
我尝试实现一个 "special-if",假设它的行为与常规 "if" 一样,带有 cond。这是代码:
(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)))))
但是,当我评估 (factorial 3) 时,它会永远运行。似乎谓词部分 (= n 1)
从未被评估过。谁能告诉我为什么这行不通?谢谢
你的专项if
恐怕注定要失败。请记住:if
是一种具有不同计算规则的特殊形式(而 cond
是使用 if
实现的宏),这就是为什么像这样的表达式即使被零除也能正常运行:
(if true 'ok (/ 1 0))
=> 'ok
... 而你的 special-if
会引发错误,因为它使用适用于程序的正常评估规则,这意味着它的所有参数都在 之前 被评估执行过程:
(special-if true 'ok (/ 1 0))
=> /: division by zero
现在你明白为什么你的代码失败了:在递归的底部,当 n
是 1
both 时,结果和替代将执行!
(special-if (= 1 1)
1 ; this expression is evaluated
(* 1 (factorial (- 1 1)))) ; and this expression too!
... 而 factorial
将愉快地继续执行 0
、-1
、-2
等值,从而导致死循环。底线:您必须使用现有的特殊形式(或定义新的宏)来实现条件行为,用户定义的标准过程在这里根本行不通。
您听说过 为什么 您的 special-if
不起作用,但您接受了一个没有告诉您如何使其起作用的答案:
(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,宏将是一个简单的函数,它将未编译代码片段(以列表、符号、字符串和数字的形式)作为参数,并将 returns 未编译代码作为其值。在 Common Lisp 中,special-if
只是 return 一个列表:
(defmacro special-if (pre act alt)
(list 'cond (list pre act)
(list t alt)))
方案宏的工作方式相同,除了列表、符号等被包装在还包含词法信息的 "syntax objects" 中,而不是使用列表
car
和 cdr
、Scheme 和 Racket 等运算符提供了模式匹配和模板引擎,可深入研究语法对象并处理额外数据(但不允许您直接处理任何数据) ).