Clojure中的let与Common Lisp中的let比较

6
在Common Lisp中,let使用一个列表来绑定变量,例如:
(let ((var1 1)
      (var2 2))
  ...)

虽然Clojure使用向量(vector)代替:
(let [a 1
      b 2]
  ...)

除了可读性之外,Clojure 为什么要使用向量?是否有其他特定原因?


2
LET 语法不是 Common Lisp 的特定功能。基本上,每个 Lisp(Emacs Lisp、ISLisp、Scheme 等)都支持此语法。但 Clojure 不支持。 - Rainer Joswig
4
Clojure的let按顺序绑定变量,因此它等同于CL和Scheme的let* - Sylwester
2个回答

8
你可以在Simple Made Easy中找到Rich Hickey的论述 - 在第14张幻灯片上,大约26分钟的时候:

Simple Made Easy - slide 14


是的,我认为线索就在这句话中:“overloaded for calls and grouping”。谢谢! - kamituel
2
现在,方括号已经具备了(少量)分组和向量语法的重载功能。 - Rainer Joswig
2
如果它们不是按顺序绑定的,对于Clojure letloop,使用映射字面量{...} 而不是向量字面量 [...]可能更合适一些,更具逻辑性。@RainerJoswig - Thumbnail
1
“overloaded for calls and grouping” 这就是 Lisp 的"精髓"。所有的东西都是列表。"函数调用"只不过是在对列表进行eval操作。 - darkfeline
@darkfeline 我在上面忍住了表达自己的观点。然而,我还是要说一下...如果你有几个像Clojure这样的数据结构的特殊形式,那么使用它们来组织程序对于视觉上的可读性或减少认知负荷是有意义的。是否应该首先拥有几个这样的特殊形式是有争议的。在这方面,我比你更接近Rich的立场,但这是一个品味问题。例如,对我来说,在经典的Lisp语法中理解多元函数会很困难。 - Thumbnail

4

Rich的观点是:

"既然我们正在谈论语法,让我们来看看经典Lisp。它似乎是语法最简单的,一切都是由符号、数字和一些其他东西组成的括号列表。有什么比这更简单的呢?但实际上它并不是最简单的,因为为了实现这种一致性,必须对列表的含义进行大量重载。它们可能是函数调用、分组结构或数据文字等。而确定需要哪个,则需要使用上下文,在扫描代码以评估其含义时增加了认知负荷。Clojure在列表中添加了几个复合数据文字,并将它们用于语法。通过这样做,意味着列表几乎总是类似于函数调用,向量用于分组,映射具有自己的文字。从一个数据结构移动到三个可以大幅减少认知负荷。"

他认为标准语法中过度使用的一项功能是访问时间。所以参数中的向量语法与使用它们时的常数访问时间有关。他说:

虽然似乎很奇怪,因为它只适用于那一个形式......一旦被存储在变量中或以任何方式传递,信息就会“丢失”。例如......

(defn test [a]
  (nth a 0)) ;;<- what is the access time of and element of a?

我个人更喜欢像括号这样的严格语法变化被保留,只有当程序员需要切换心理模型时才使用,例如嵌入式语言。

;; Example showing a possible syntax for an embedded prolog.

{size [],0}
{size([H|T],N) :- size(T,N1), N is N1+1}

(size '(1 2 3 4) 'n) ;; here we are back to lisp code

这个概念在语法上是恒定的。你不会在运行时“传递”结构。但是在运行之前(读取/宏/编译时间)则另当别论,因此在可能的情况下最好将事物保持为列表。[编辑]原始来源似乎已经消失了,但这里有另一个采访记录:https://gist.github.com/rduplain/c474a80d173e6ae78980b91bc92f43d1#file-code-quarterly-rich-hickey-2011-md

2
愚人节?访问时间无法重载。不是在语法中,也不是一般情况下。可以观察或预期访问时间。Clojure中的向量没有恒定的访问时间。引用名称不传达类型或访问时间信息,除非您将它们命名为这样做。无论如何,这与通过字面值的语法和结构无关。Clojure不“嵌入”Prolog或任何语言,因此保留括号以嵌入语言的偏好就像要求汽车有翅膀而不是门一样。Clojure的顶级函数定义宏称为defn,而不是“defun”。 - Leon Grapenthin
我已经修正了拼写错误,并澄清了一下,Rich的那行代码(我正在寻找源代码)是指列表使用方式被过度使用。不仅在分组方面,而且在访问时间方面也是如此。因为列表中元素的访问时间和向量中元素的访问时间不同,所以向量更适合用于分组参数,因为它反映了(大致)恒定的访问时间。我也没有说Clojure内嵌了Prolog,我说我更喜欢将语法保留给像内嵌语言这样的东西使用。你可能还注意到了问题上的common-lisp标签... - Baggers
这意味着将会有关于在Common Lisp中可以做的事情的答案,比如扩展阅读器。 - Baggers
出于美学原因,选择将分组参数的向量称为绑定向量。它们优越的随机访问时间特征并不重要:运行时性能不受影响,而由于访问列表的第一个元素具有O(1)特性,编译性能应更好。请参考 fn* 特殊形式的编译器源代码的相关部分:https://github.com/clojure/clojure/blob/69afe91ae07a4c75c34615a4af14327f98d78510/src/jvm/clojure/lang/Compiler.java#L5224 - Leon Grapenthin
我知道。我试图表达的是语法概念被过度使用了...没有运行时或编译时的影响,纯粹是认知上的。我找不到引用,现在也没有时间去查找所有的视频。你说的都没错,只是不适用于我试图(虽然我承认很差)传达的内容。无论如何,感谢你合理的评论和好的链接。该喝一杯啤酒了,再见。 - Baggers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接