我在Racket中获得了两条if语句指令:
(define (if-fun c thn els) (if c thn els))
(define-syntax-rule (if-mac c thn els) (if c thn els))
有人请介意解释如何评估这两个if语句之间的差异,并提供使用每个if语句定义的示例吗?我很难区分在这个例子中如何评估宏和函数参数。我尝试了一些小例子,如:
(if-fun (> 3 4) true false) ;; #f
(if-mac (> 3 4) true false) ;; #f
但很明显,这并没有帮助我区分这两个定义。
-Thanks
答案 0 :(得分:5)
从您的评论中听起来您已经想到了这一点。关键问题是,什么时候(如果有的话)会对事情进行评估?
另一个关键点是功能和宏完全不同,即使它们的定义和使用看起来都一样。
使用函数和宏的方式完全相同:(thing other stuff)
。 thing
是函数还是宏是不明显的。这既好又坏。大多数情况下它非常好。
对于定义事物,使用define-syntax-rule
定义宏的方式与函数define
的方式非常相似。这既好又坏。当你第一次学习时,我说它大部分都很糟糕 - 因为它很容易让人忘记完全不同的宏来自功能。这可能令人困惑!
当调用函数时,在运行时评估所有参数,然后赋予函数。这就是为什么if-fun
(/ 1 0)
的参数会导致错误的原因。在控制进入if-fun
之前,它被评估(并引出除以零误差)。
(旁注:当您使用懒惰评估或手动" thunks"来调用某个功能时,评估会延迟。if-lazy
可以调用thn
或els
过程参数,仅在/需要时。当条件为假时,它甚至不会尝试调用els
。)
调用宏:
时何时:宏在您的程序运行之前完成其工作。宏在编译时工作,而不是在运行时。
什么:宏将代码段转换为其他代码段。但是代码还没有被评估。代码仅在运行时稍后进行评估。那么"论证"到if-mac
没有被宏评估。他们只是插入真实if
表单的代码,这是一个宏(或原始的特殊形式),只评估所需的内容。
最后一个令人困惑的部分是,因为您的示例然后和其他表达式没有任何副作用,并且不会有任何副作用导致任何错误,差异不明显。
答案 1 :(得分:0)
(define (verbose arg)
(display arg) ; display
(newline) ; display newline
arg)) ; evaluate to arg
(if-fun (verbose (> 3 4))
(verbose 'true)
(verbose 'false))
; ==> false
打印
#f
true
false
宏版本:
(if-mac (verbose (> 3 4))
(verbose 'true)
(verbose 'false))
; ==> false
打印
#f
false
你看到了区别吗?使用过程计算每个参数并将其绑定到变量,然后评估主体。
在宏版本中,代码被转换,然后进行评估。因此,由于谓词为#f
,因此从未执行过后续表达式。
如果您尝试制作递归函数:
(define (factorial n)
(if-fun (<= n 2)
n
(* n (factorial (- n 1)))))
(factorial 2)
即使它遇到基本情况,由于所有3个参数都被评估,它将做(因子1),然后(因子0),(因子-1).....到负无穷大。它永远不会返回一个值,但是,它不是尾递归,它会耗尽内存。
(define (factorial n)
(if-mac (<= n 2)
n
(* n (factorial (- n 1)))))
(factorial 2)
评估程序时,可以扩展宏,使其变为:
(define (factorial n)
(if (<= n 2)
n
(* n (factorial (- n 1)))))
就好像你根本没有使用你的宏。如果你有一个宏,它在展开时会打印一些东西,它会在你使用之前为程序中的每次使用打印一次。
就像这样,因为Scheme和Racket都有热切的评价。例如。 #!lazy
球拍,这是#!racket
的懒惰版本,if
和其他特殊形式作为程序制作,因为需要进行评估。懒惰的语言不需要宏。