Scheme 用cond定义是否不';行不通

Scheme 用cond定义是否不';行不通,scheme,racket,Scheme,Racket,我尝试实现一个“特殊if”,假设它的行为与cond的常规“if”类似。代码如下: (define (special-if pre act alt) (cond (pre act) (else alt))) 为了测试这是否有效,我用这个“特殊if”编写了一个阶乘函数: 然而,当我计算(阶乘3)时,它将永远运行。似乎从未计算谓词部分(=n1)。有人能告诉我为什么这行不通吗?谢谢。您的特别如果注定要失败,恐怕。请记住:如果是一种具有不同求值规则的特殊形式(而cond是使用如果实现

我尝试实现一个“特殊if”,假设它的行为与cond的常规“if”类似。代码如下:

(define (special-if pre act alt)
  (cond (pre act)
        (else alt)))
为了测试这是否有效,我用这个“特殊if”编写了一个阶乘函数:


然而,当我计算(阶乘3)时,它将永远运行。似乎从未计算谓词部分
(=n1)
。有人能告诉我为什么这行不通吗?谢谢。

您的特别
如果注定要失败,恐怕。请记住:
如果
是一种具有不同求值规则的特殊形式(而
cond
是使用
如果
实现的宏),这就是为什么这样的表达式即使被零除也可以正常运行的原因:

(if true 'ok (/ 1 0))
=> 'ok
。。。而您的
特殊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
等值,从而导致无限循环。一句话:您必须使用现有的特殊形式(或定义一个新宏)来实现条件行为,用户定义的标准过程在这里根本不起作用。

您的特殊
如果
注定要失败,我担心。请记住:
如果
是一种具有不同求值规则的特殊形式(而
cond
是使用
如果
实现的宏),这就是为什么这样的表达式即使被零除也可以正常运行的原因:

(if true 'ok (/ 1 0))
=> 'ok
。。。而您的
特殊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
等值,从而导致无限循环。一句话:您必须使用现有的特殊形式(或定义新的宏)来实现条件行为,用户定义的标准过程在这里根本不起作用。

您已经听说了为什么您的
特殊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,那么宏将是一个简单的函数,它将未编译代码片段(以列表、符号、字符串和数字的形式)作为参数,并将未编译代码作为其值返回。在Common Lisp中,
special if
只返回一个列表:

(defmacro special-if (pre act alt)
   (list 'cond (list pre act)
               (list t alt)))
Scheme宏的工作方式相同,只是列表、符号等被包装在“语法对象”中,其中也包含词汇信息,而不是使用列表 诸如
car
cdr
、Scheme和Racket等操作符提供了一个模式匹配和模板引擎,可以深入研究语法对象并处理额外的数据(但不允许您直接处理任何数据)

您已经听说了为什么您的
特殊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,那么宏将是一个简单的函数,它将未编译代码片段(以列表、符号、字符串和数字的形式)作为参数,并将未编译代码作为其值返回。在Common Lisp中,
special if
只返回一个列表:

(defmacro special-if (pre act alt)
   (list 'cond (list pre act)
               (list t alt)))
Scheme宏的工作方式相同,只是列表、符号等被包装在“语法对象”中,其中也包含词汇信息,而不是使用列表
诸如
car
cdr
、Scheme和Racket等操作符提供了一个模式匹配和模板引擎,可以深入研究语法对象并处理额外的数据(但不允许您直接处理任何数据)

尝试使用步进器,看看在传递给函数之前(如果您使用的是DrRacket),参数会被评估。因此,当
newif
时,将同时计算
act
alt
参数。尝试使用步进器,看看在传递给函数之前(如果使用DrRacket)参数会发生什么情况。因此,当
newif
时,将同时计算
act
alt
参数。这就是你的无限递归。SICP有一个关于这个问题的练习:|谢谢!这就解释了一切。SICP有一个关于这个问题的练习:|谢谢!这就解释了一切。感谢您提供的高级解决方案!感谢您提供的高级解决方案!