【问题标题】:Why are <e> inside if and cond designed to be handled differently in Scheme?为什么 <e> 在 if 和 cond 中设计为在 Scheme 中以不同方式处理?
【发布时间】:2022-12-09 06:38:06
【问题描述】:
(if <predicate> <consequent> <alternative>)

(cond (<p1> <e1>)
  (<p2> <e2>)
   ..........
  (<pn> <en>))

ifcond 之间的一个小区别是 cond 的表达式部分 cond 子句可以是一系列表达式。-- SICP

我想知道为什么方案语言的设计者将 if 和 cond 的表达方式不同。

这样设计的目的是什么?

【问题讨论】:

    标签: if-statement conditional-statements scheme sicp


    【解决方案1】:

    在像 Scheme 这样的非纯函数式语言中,允许在语法中有空间的地方使用一系列表达式通常很有用:例如在过程的主体中等等。因此,例如,一个纯函数式 Lisp 可能有一个函数语法,它是

    (λ (<arg> ...)
      <expression>)
    

    但是 Scheme 允许

    (λ (<arg> ...)
      <expression-1>
      <expression-2>
      ...)
    

    除了最后一个表达式之外的所有值都被忽略:它们只是因为副作用而发生。由于在语法中有空间,Scheme 允许这样做。

    然而,在 if 的语法中根本没有空间用于这种情况(见下文)。

    有可能设计多路条件表达式它的语法没有空间,它可能看起来像:

    (kond
     a 1
     b 2
     c 3
     else 4)
    

    例如(这里 elsekond 来说很神奇:我不记得 SICP 的方案是否有)。

    但是如果你考虑一下cond 的语法实际上是什么:

    (cond
     (a 1)
     (b 2)
     (c 3)
     (else 4))
    

    然后那里显然,现在语法中有空间在每个子句的结果位置写入一系列表达式。因此,Scheme 确实允许这样做,因为根本没有理由不这样做。所以而不是

    (cond
     (<t> <e>)
     ...)
    

    你可以写

    (cond
     (<t> <e1> <e2> ...)
     ...)
    

    例如:

    (cond
     (world-has-ended
      (displayln "The world has ended: rain of fire imminent")
      (rain-fire-from-sky 'yes-really))
     ...)
    

    事实上,Scheme 有一个运算符,begin,它的全部目的是在只允许一个表达式的情况下允许一系列表达式。因此,例如,如果你想拥有一系列自然只有一个的表达式,你可以使用begin

    (if world-has-ended
        (begin
          (displayln "The world has ended: rain of fire imminent")
          (rain-fire-from-sky 'yes-really))
        (begin
          (displayln "World has not yet ended, sorry for the frogs")
          (rain-frogs-from-sky)))
    

    然后,您可以将 cond 视为根据 ifbegin 定义的:

    (cond
     (world-has-ended
      (displayln "The world has ended: rain of fire imminent")
      (rain-fire-from-sky 'also-rocks))
     (world-has-nearly-ended
      (displayln "The world has nearly ended")
      (rain-frogs-from-sky 'also-some-fire)))
    

    是相同的

    (if world-has-ended
      (begin
        (displayln "The world has ended: rain of fire imminent")
        (rain-fire-from-sky 'also-rocks))
      (if world-has-nearly-ended
          (begin
            (displayln "The world has nearly ended")
            (rain-frogs-from-sky 'also-some-fire))
          #f))
    

    至于为什么 cond 的语法被设计为有多个表达式的空间:这是一个历史问题。我认为有两件事有助于解释它:

    1. (cond &lt;t1&gt; &lt;e1&gt; &lt;t2&gt; &lt;e2&gt; ...) 之类的语法读起来很痛苦,因为您需要计算正文中的表单数量,而 (cond (&lt;t1&gt; ...) (&lt;t2&gt; ...) ...) 则容易得多。
    2. 在 Lisp 的早期,那些只接触过 FORTRAN 和汇编程序(因为这几乎就是全部)的人倾向于编写类似于 FORTRAN 程序的程序,并且有很多命令式操作,边-效果、排序等。 cond 的语法允许这样做。

    【讨论】:

    • 非常好的答案让我笑了很多,谢谢。 :) 关于(不)纯度的观点很有启发性。缺少的 cond 子句虽然没有被 #f 替换,但我不认为。我在drracket中试过(cond (#f)),根本没有显示任何值。
    • @WillNess:我认为你是对的:我错过了很多边缘案例(比如(cond (3)))。 R7RS(和 R5RS)都说“未指定”,我认为 Racket 在这种情况下会返回其神奇的“void”对象。我还认为所有这些都意味着 (cond (else)) 我认为是非法的,我不知道。
    • 关于历史(“...早期的 Lisp”),Lisp 1.5 将 COND 限制为每个谓词后的单个表达式(M 表达式 [p1-&gt;e1; p2-&gt;e2; ...] 的 S 表达式版本),并且没有 @ 987654351@。 Maclisp 有现在定义的cond,方案“R0RS”复制了它; Maclisp 的 if 是一个宏,它允许一个单一的结果但一系列的选择(!)
    • 当然,有如果您愿意接受不对称,请在if 中留出额外的表达空间。例如,elisp 允许在 else 子句中使用多个表达式:(if &lt;p&gt; &lt;t&gt; &lt;e1&gt; &lt;e2...&gt;)
    • @mnemenaut 有趣的是 Lisp 1.5 有单一形式 cond:我假设它有多种形式。 Standard Lisp 也有这个(我忘了它在哪里分支,但很早)所以我的假设显然是假的。到 1974 年,Interlisp 已经有了隐式 progn。
    【解决方案2】:

    如果您允许在if 中使用任意数量的表达式,您如何判断真正的表达式何时结束以及错误的表达式何时开始?¹对于cond,每个案例都是一个列表,其中第一个元素是测试表达式,其余元素该列表在 true 时得到评估。没有歧义。


    1:与begin

    (if <test>
      (begin <true  exprs> ...)
      (begin <false exprs> ...))
    

    cond 通常是一个扩展为的宏

    (if <p1>
      (begin <e1>)
      (if <p2>
        (begin <e2>)
        ...))
    

    【讨论】:

      【解决方案3】:

      cond 在 Scheme 中隐含了 begin 由于 60 年代母语 LISP 的意外设计!

      如果不是设计condif的Scheme的设计者。 cond 是 McCarhty 论文的原始条件,他的 eval 不支持多个结果表达式。基本上,如果您要写多个结果,它只会写第一个。 if在那篇论文中不存在,当时在 Fortran 中也不存在,因为它有 arithmetic if

      将每个术语包裹在括号中是为较新版本的 lisp 打开的,实际上允许多个结果,最后一个是尾表达式。想象一下,他没有那样做,而是建议这样做:

      ; a and c are predicates
      (cond a b
            c d
            t e)
      

      这样做的问题是,只有我的格式才能帮助确定什么是什么。如果我把它写成一行,那么几乎不可能阅读这个简单的短代码:

      (cond a b c d t e)
      

      添加括号是一种将属于一起的事物组合在一起的方法。当 if 后来出现时,它不支持相同形式的两个以上分支,当 lisp 变得势在必行时,他们引入了 progn cond 由于意外设计而隐含,而 if 需要 progn 形式,所以它只保留 3 个操作数。

      你可能对The roots of lisp感兴趣

      顺便提一句。我的公寓 cond 示例与 Paul Graham 的 lisp 语言 Arc 中 if 的实现非常相似:

      (if p1 c1
          p2 c2
          c3)
      

      【讨论】:

        猜你喜欢
        • 2016-02-01
        • 2016-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-06
        • 1970-01-01
        相关资源
        最近更新 更多