在Common Lisp中,为什么(if)语句的多表达式主体需要(progn)?

时间:2009-02-08 18:27:23

标签: lisp common-lisp

这只是20世纪50年代遗留下来的一些历史遗留问题,还是存在某种合理的理由,为什么(if)形式的多表达体需要(预测)?为什么不能将多个表达式包装在一组括号中,例如with(let):

   (if some-cond
     ((exp1) (exp2) (exp3)) ; multi exp "then"
     (exp4)) ; single exp "else"

看起来编写一个宏来测试每个主体首先看它是否是一个列表然后如果它是,如果它的第一个元素也是一个列表(因此不是一个函数调用)然后到相应地将其子组件包含在(progn)中。

9 个答案:

答案 0 :(得分:17)

在Common Lisp中,这段代码:

(if t
    ((lambda (x) (+ x 5)) 10)
   20)

将返回15.根据您的提议,我认为它会看到true-clause是一个列表,并自动将其转换为:

(if t
    (progn (lambda (x) (+ x 5)) 10)
   20)

将返回10.是吗?

我不确定在CL中区分“list”和“function invocation”是“微不足道的”。您是否打算将此更改与非向后兼容? (新的和有趣的Lisp方言总是很酷,但它不是Common Lisp。)或者你能举出一个你想到的例子吗?

答案 1 :(得分:11)

Common Lisp并不完美,因为它是完美的,它是完美的,因为它是完美的。

整个语言建立在25 special operators之上; if就是其中之一,progn是另一个。

if提供了测试条件的基本机制,然后跳转到一个或另一个代码地址。 progn提供了做几件事并返回最后一个值的基本机制。

语言标准中有几个宏构建于此 - 例如whenunlesscondcase

如果你愿意,你可以选择多种类似你想象的东西:例如,你可以编写一个ifm宏,希望隐式progn作为then-和else-clause,或者你可以像你说的那样写它,以便它检测到意图,或者你甚至可以写一个读宏来为progn添加语法糖。

答案 2 :(得分:6)

  

在语法上是否存在某些理由为什么(if)形式的多表达体需要(预测)?

答案是“是”,尽管可能不是你期望的原因。因为Common Lisp(与Scheme和其他Lisps不同)需要funcall,所以你的提议并不含糊。即使它含糊不清,只要您的用户知道括号在这里暗示progn,它就会起作用。

但是,该语言中没有其他结构*具有可选的单/双括号。很多结构都有隐式progn s,但它们的括号语法总是相同的。

例如,cond对每个分支都有隐式progn

(cond (test1 body1) (test2 body2) ...)

你无法来回切换:

(cond test1 exp1 (test2 body2) t exp3)

因此,即使您的提案不明确,也不符合其他语言的语法。然而!就像你说的那样,这个宏很容易实现。你应该自己做,看看它是否运作良好。我很容易出错;因为我的Lisping几乎都在Scheme中,所以我很偏颇。

* case除外。 HMF。现在我想可能还有其他人。

答案 3 :(得分:4)

并非所有表达都是列表。对于(let ((a 42)) (if some-cond (a b c) (d e f))),您不知道(a b c)是应该被解释为对函数a的调用还是隐含的预测。

答案 4 :(得分:4)

因为IF(< - HyperSpec链接)的语法定义为:

if test-form then-form [else-form] => result*

没有开始或结束标记。有一个THEN-FORM而不是那个形式*。 PROGN是一种定义表单序列的机制,其中表单从左到右执行,并返回最后一个表单的值。

它可以像这样定义:

my-if test-form (then-form*) [(else-form*)] => result*

(defmacro my-if (test then &optional else)
  (assert (and (listp then) (listp else)) (then else))
  `(if ,test (progn ,@then) (progn ,@else)))

(my-if (> (random 10) 5)
       ((print "high")
        :high)
       ((print "low")
        :low))

嗯,已经有一个支持多种形式的构造:COND。

(cond ((> (random 10) 5)
       (print "high")
       :high)
      (t
       (print "low")
       :low))

典型的风格是在必须尝试多个替代方案时以及当有多个then / else-forms时使用COND。当只有一个测试以及then和else形式时,使用IF。对于其他情况,有时间和除外。 WHEN和UNLESS仅支持一种或那种形式(没有其他形式)。

我想至少有一个条件形式(在这种情况下为IF)是没有添加括号层的。写

(if (> (random 10) 5)
    (progn
       (print "high")
       :high)
    (progn
       (print "low")
       :low))
然后

是一个很小的代价。写入额外的PROGN或切换到COND变体。 如果您的代码真的会受益于具有多个then和else表单的IF,那么只需编写该宏(参见上文)。 Lisp有它,所以你可以成为自己的语言设计师。考虑引入一个宏是很重要的:我的宏是正确的吗?它会检查错误吗?这值得么?是否可读(对于其他人?)?

答案 5 :(得分:3)

扩展版已有宏。这本书非常好:http://www.gigamonkeys.com/book/你的答案在本章中:http://www.gigamonkeys.com/book/macros-standard-control-constructs.html

标准宏是when和unless。

答案 6 :(得分:3)

当您没有“其他”分支时,标准宏whenunless会有所帮助。否则,如果在任何分支中有多个表达式,最好使用cond

答案 7 :(得分:3)

请注意,在“{} languages”(C,C ++,Java ...)中,您在大括号中以复合语句的形式拥有PROGN。

请注意这些等价/类比:

(if antecedent consequent alternative)
if (antecedent) consequent; else alternative;

(if antecedent (progn consequent1 consequent2) alternative)
if (antecedent) { consequent1; consequent2; } else alternative;

PROGN运算符(及其表兄弟)是Lisp的“大括号”。 Lisp中的括号不是它的“括号”!

现在考虑Perl。 Perl有一个完全支撑if。这可以在Lisp中完成:

 (if antecedent (consequent1 consequent2 ...) (alternative1 alternative2 ...))

E.g:

 (if (< foo 0)
   ((format t "foo is less than zero")
    (- foo))
   ((format t "foo is not less than zero")
    foo))
我认为,我可以忍受这种情况,但有些人会抱怨额外的括号,特别是在简单的情况下。

答案 8 :(得分:1)

在Lisp中,括号表示功能应用,而不是分组。如果exp1是一个返回函数的函数,那么你的表达意味着什么?是否会使用参数(exp2) (exp3)调用它?