如果`if`没有评估它的所有参数?

时间:2013-09-29 10:48:17

标签: lisp

我正在尝试深入学习和理解Lisp编程语言。函数+按应用顺序评估其参数:

(+ 1 (+ 1 2))
将评估

(+ 1 2),然后评估(+ 1 3),但if函数的工作方式不同:

(if (> 1 2) (not-defined 1 2) 1)

由于未评估表单(not-defined 1 2),程序不会中断。

相同的语法如何导致不同的参数评估?如何定义if函数以便不评估其参数?

6 个答案:

答案 0 :(得分:13)

ifspecial operator,而非普通函数。

这意味着在调用与rest元素相关联的函数之前评估compound form中的first元素的常规规则是适用(因为它类似于macro forms)。

在编译器和/或解释器中实现的方式是查看复合形式并根据its first element决定如何处理它:

  • 如果它是一个特殊的操作员,它会做它特别的事情;
  • 如果它是macro,它的宏功能会得到整个形式;
  • 否则将其视为一个函数 - 即使没有定义函数。

请注意,某些特殊形式可以定义为扩展为其他特殊形式的宏,但实际上必须存在一些特殊形式。

例如,可以用cond

来定义if
(defmacro my-if (condition yes no) 
  `(cond (,condition ,yes)
         (t ,no)))

反之亦然(更复杂 - 实际上,cond 宏,通常会扩展为if s的序列。

PS。请注意,系统提供的宏和特殊运算符之间的区别,而技术上清晰明确(请参阅special-operator-pmacro-function),在意识形态上模糊because

  

实现可以自由地实现Common Lisp特殊运算符   作为一个宏。实现可以自由地实现任何宏运算符   作为一个特殊的运算符,但只有当一个等价的定义   还提供了宏。

答案 1 :(得分:5)

Lisp语法是常规的,比其他语言更常规,但它仍然不是完全正常的:例如在

(let ((x 0))
   x)

let不是函数的名称,((x 0))不是一个错误形式,其中第一个位置使用的不是lambda形式的列表。

有很多“特殊情况”(当然还比其他语言少得多),其中不遵循每个列表的一般规则是函数调用,if就是其中之一。 Common Lisp有很多“特殊形式”(因为绝对最小化不是重点),但你可以在方案方言中只用其中的五个来逃避:ifprogn,{{ 1}},quotelambda(如果需要宏,则为6)。

虽然Lisp的语法并不完全一致,但代码的基本表示却非常统一(只是列表和原子),表示的一致性和简单性有助于元编程(宏)。 / p>

“Lisp没有语法”是一个含有一些真理的语句,但是它的语句是“Lisp有两种语法”:一种语法是使用set!从字符流转换为s表达式的语法,另一种语法是使用编译器/赋值器将s表达式转换为可执行代码。

Lisp也没有语法,因为这两个级别都没有修复。与其他编程语言不同,您可以自定义第一步(使用阅读器宏)和第二步(使用宏)。

答案 2 :(得分:5)

sds's answer很好地回答了这个问题,但我认为还有一些更为一般的方面值得一提。正如答案和其他人所指出的那样,if作为特殊算子被内置于语言中,因为它确实是一种原始的。最重要的是,if 不是一个函数。

也就是说,if功能可以使用函数和普通函数调用来实现,其中所有参数都被评估。因此,条件可以在lambda calculus中实现,该族中的语言在某些基础上,但没有条件运算符。

在lambda演算中,可以将true和false定义为两个参数的函数。假定参数是函数,true调用其第一个参数,而false调用第二个参数。 (这是Church booleans的略微变化,只是返回他们的第一个或第二个参数。)

 true = λ[x y].(x)
false = λ[x y].(y)

(这显然与Common Lisp中的布尔值不同,其中nil为false且其他任何内容都是真的。)但是,这样做的好处是我们可以使用布尔值来调用其中一个两个函数,取决于布尔值是true还是false。考虑Common Lisp形式:

(if some-condition
  then-part
  else-part)

如果正在使用上面定义的布尔值,那么评估some-condition将生成truefalse,如果我们使用参数调用该结果

(lambda () then-part)
(lambda () else-part)

然后只会调用其中一个,因此实际上只会评估then-partelse-part中的一个。一般来说,将某些表单包装在lambda中是一种很好的方法,可以延迟对这些表单的评估。

Common Lisp宏系统的强大功能意味着我们可以使用上述布尔类型实际定义if宏:

(defconstant true
  (lambda (x y)
    (declare (ignore y))
    (funcall x)))

(defconstant false
  (lambda (x y)
    (declare (ignore x))
    (funcall y)))

(defmacro new-if (test then &optional else)
  `(funcall ,test
            (lambda () ,then)
            (lambda () ,else)))

使用这些定义,有些代码如下:

(new-if (member 'a '(1 2 3))
  (print "it's a member")
  (print "it's not a member"))))

扩展到:

(FUNCALL (MEMBER 'A '(1 2 3))                     ; assuming MEMBER were rewritten 
         (LAMBDA () (PRINT "it's a member"))      ; to return `true` or `false`
         (LAMBDA () (PRINT "it's not a member")))

通常,如果存在某种形式且某些参数未被评估,则表单的(car)形式是Common Lisp特殊运算符或宏。如果您需要编写一个将评估参数的函数,但是您希望不评估某些表单,则可以将它们包装在lambda个表达式中并让你的函数有条件地调用那些匿名函数。

这是实现if的一种可能方式,如果您还没有使用该语言。当然,现代计算机硬件不是基于lambda演算解释器,而是基于具有测试和跳转指令的CPU,因此提供if基元并编译为适当的语言更有效。机器说明。

答案 3 :(得分:4)

这样做没有任何意义。示例:(if (ask-user-should-i-quit) (quit) (continue))。是否应该退出,即使用户不想?

IF不是Lisp中的函数。它是一个特殊的内置操作员。 Lisp有几个内置的特殊运算符。见:Special Forms。那些不是功能。

答案 4 :(得分:2)

不会对函数计算参数,因为if特殊运算符。可以以任意方式评估特殊运算符,这就是为什么它们被称为特殊运算符。

考虑例如。

(if (not (= x 0))
    (/ y x))

如果总是评估除法,则可能存在零误差除法,这显然不是意图。

答案 5 :(得分:2)

如果不是函数,它是一种特殊形式。如果您想自己实现类似功能,可以通过定义宏而不是函数来实现。

这个答案适用于Common Lisp,但对于大多数其他Lisp来说可能是相同的(尽管在某些if中可能是宏而不是特殊形式)。