Clojure let vs Common Lisp let

Posted

技术标签:

【中文标题】Clojure let vs Common Lisp let【英文标题】: 【发布时间】:2015-06-04 13:51:48 【问题描述】:

在 Common Lisp 中,let 使用列表进行绑定,即:

(let ((var1 1)
      (var2 2))
  ...)

虽然 Clojure 使用向量代替:

(let [a 1
      b 2]
  ...)

除了可读性之外,Clojure 使用向量是否有任何特定原因?

【问题讨论】:

LET 语法不是 Common Lisp 的特定功能。基本上每个 Lisp(Emacs Lisp、ISLisp、Scheme、...)都支持这种语法。 Clojure 没有。 Clojure let 顺序绑定。因此它等价于 CL 和 Scheme let* 【参考方案1】:

您可以在Simple Made Easy 找到 Rich Hickey 的论点 - 第 14 张幻灯片,大约 26 分钟:

【讨论】:

是的,我认为线索就在这句话中:“overloaded for calls ang grouping”。谢谢! 现在方括号被(更少)分组和向量语法重载。 @RainerJoswig 如果它们不是按顺序绑定的,那么在 Clojure let 和 @987654326 中使用映射文字 ... 而不是矢量文字 [...] 可能更合乎逻辑@. "为调用和分组而重载" 这就是 Lisp 的要点。一切都是列表。 “函数调用”只是evaling 列表。 @darkfeline 我拒绝在上面表达我自己的观点。然而,这里有……如果你有几种特殊的数据结构形式,就像 Clojure 那样,使用它们来组织程序是有意义的(称之为可读性减少认知加载 或你想要的)。你是否应该首先拥有几种这样的特殊形式是没有意义的。在这点上,我更接近 Rich 而不是你,但这是一个品味问题。例如,对我来说,在经典的 Lisp 语法中很难理解多个 arity 函数。【参考方案2】:

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

这样的概念在句法上是不变的。您不会在运行时“传递”结构。在运行时(读取/宏/编译时间)之前是另一回事,因此在可能的情况下,通常最好将事物保留为列表。

[edit] 原来的出处好像没了,这里是另外一个采访记录:https://gist.github.com/rduplain/c474a80d173e6ae78980b91bc92f43d1#file-code-quarterly-rich-hickey-2011-md

【讨论】:

愚人节?访问时间不能超载。不是语法,不是一般的。可以观察或预期访问时间。 Clojure 中的向量没有固定的访问时间。引用名称不传达类型或访问时间信息,除非您命名它们以便它们这样做。无论如何,这与通过文字的语法和构造无关。 Clojure 不“嵌入” Prolog 或任何语言,因此保留括号以嵌入语言的偏好就像要求汽车有翼而不是门。 Clojures ***函数定义宏称为defn,而不是“defun”。 我已经修正了错字并澄清,Rich 的台词(我正在寻找源代码)是列表的使用方式超载。不仅在分组方面,而且在访问时间方面。因为一个元素的访问时间是一个列表和一个向量中的一个元素是不一样的,所以用于分组参数的向量是一个更好的选择,因为它反映了(大致)恒定的访问时间。我也没有说 clojure 嵌入了 prolog,我说 我更喜欢 语法被保留用于嵌入式语言之类的东西。您可能还注意到问题上的 common-lisp 标记... ..这意味着将有与您可以在普通 lisp 中做的事情有关的答案,例如扩展阅读器 用于分组参数的向量(称为绑定向量)被选为出于美学原因的向量。它们优越的随机访问时间特性无关紧要:运行时性能不受影响,并且列表的编译性能应该更好,因为访问它们的第一个元素具有 O(1) 特性。请参考这里fn*特殊形式的编译器源码的相关部分:github.com/clojure/clojure/blob/… 我知道。我试图说明句法概念是重载的......没有运行时或编译时含义,纯粹是认知。我找不到报价,我现在没有时间浏览所有视频来找到它。你所说的没有什么是错的,它只是不适用于我正在尝试(我承认很糟糕)传达的内容。无论如何,为合理的 cmets 和良好的链接欢呼。是时候喝杯啤酒了,Ciao

以上是关于Clojure let vs Common Lisp let的主要内容,如果未能解决你的问题,请参考以下文章

Clojure - recur 适用于循环或 let 语句?

对 Clojure 中的“let”感到困惑

Clojure:虽然 let 绑定不像绑定那样工作?

在没有嵌套 let 的情况下在 Clojure 中绑定多个相关变量

clojure for function resetts let

let 内的 Clojure 循环(全局 v 局部变量)