15

这只是 1950 年代遗留下来的一点历史遗留问题,还是从语法上讲,为什么 (if) 形式的多表达式主体需要 (progn)?为什么不能将多个表达式包装在一组括号中,例如 with (let):

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

编写一个宏来测试每个主体以首先查看它是否是一个列表,然后如果它是,它的第一个元素是否也是一个列表(因此不是函数调用)然后包装它的子组件似乎是微不足道的相应地在 (progn) 内。

4

9 回答 9

17

在 Common Lisp 中,这段代码:

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

将返回 15。根据您的建议,我认为它会看到 true 子句是一个列表,并自动将其转换为:

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

这将返回 10。对吗?

我不确定在 CL 中区分“列表”和“函数调用”是否“微不足道”。您是否打算让此更改不向后兼容?(新的有趣的 Lisp 方言总是很酷,但它不是 Common Lisp。)或者你能举个例子来说明你的想法吗?

于 2009-02-09T19:17:11.837 回答
12

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

整个语言建立在 25 个特殊运算符之上; if是其中之一, progn是另一个。

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

语言标准中有几个以此为基础的宏——例如when, unless, cond, case

如果你愿意,你有几个选择来做你所设想的事情:一个,你可以写一个ifm宏,期望隐式progns 作为 then- 和 else- 子句,或者你可以像你说的那样写它,以便它检测到意图,或者您甚至可以编写一个读取宏来为progn.

于 2009-02-08T20:46:28.777 回答
6

从语法上讲,为什么(if)形式的多表达式体需要(progn)?

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

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

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

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

你不能来回切换:

(cond test1 exp1 (test2 body2) t exp3)

因此,即使您的提议没有歧义,它也不符合该语言其余部分的语法。然而!就像你说的那样,宏实现起来很简单。你应该自己做,看看它是否运作良好。我很容易出错;我很偏颇,因为我几乎所有的 Lisping 都在 Scheme。

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

于 2009-02-08T20:16:56.193 回答
4

并非所有表达式都是列表。因为(let ((a 42)) (if some-cond (a b c) (d e f)))您不知道是否(a b c)应该将其解释为对函数的调用a或隐式预测。

于 2009-02-08T18:36:02.443 回答
4

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

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

没有开始或结束标记。有一个 THEN-FORM 而不是 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。WHEN 和 UNLESS 仅支持一种或 THEN 形式(不支持 else 形式)。

我想最好有至少一个条件形式(在这种情况下是 IF )没有额外的括号层。写作

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

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

于 2009-03-11T12:31:11.510 回答
3

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

标准宏是 when 和 unless。

于 2009-02-08T18:33:11.763 回答
3

请注意,在“{} 语言”(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 ...))

例如:

 (if (< foo 0)
   ((format t "foo is less than zero")
    (- foo))
   ((format t "foo is not less than zero")
    foo))

我认为我可以忍受这一点,但有些人会抱怨额外的括号,特别是在简单的情况下。

于 2012-03-16T03:07:23.840 回答
2

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

于 2009-02-08T19:15:34.797 回答
1

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

于 2009-02-08T18:55:40.627 回答