Clojure中let和let*的区别

16

考虑以下宏:

(defmacro somemacro []
    (list 'let ['somevar "Value"] 'somevar))

展开它会得到以下结果:

(macroexpand '(somemacro))

结果:

(let* [somevar "Value"] somevar)

关于 let*(带星号)我有两个问题:

  • 它是什么意思?(特别是:是否有记录在某处?)
  • 为什么这个宏在使用“正常”的let时没有被展开呢?(即,没有星号的let。)两者产生相同的结果(在我的实验中)。是否存在反例?

不幸的是,我找不到任何关于let*的“官方”文档,这就是为什么我在这里提问的原因。

我已经考虑过的来源:

(doc let*)  ; --> nil
(source let*)  ; --> source not found
  1. https://clojuredocs.org/clojure.core --> 我看不到 let*,虽然有 list* 等函数。
  2. https://clojuredocs.org/clojure.core/let --> 在评论中只提到了一次,对我来说不太清楚:

    Nota Bene:Clojure 中的 let 就像 Scheme 中的 let* -- 每个 init-expr 都可以访问前面的绑定形式。(也有一个 let*,但它更像是没有解构的 let,实际上是底层实现。)

  3. LET versus LET* in Common Lisp --> 这个问题是关于 Common Lisp 的,但在 Clojure 中可能是一样的吗?
  4. 这个答案:https://dev59.com/jG445IYBdhLWcg3wBVrz#5084339

    在 Clojure 中,它基本上意味着 "foo* 就像 foo,但某种程度上不同,你可能想要 foo"。换句话说,这意味着该代码的作者无法想出第二个函数的更好名称,因此他们只是在其后加了一个星号。

--> let 和 let* 是否也是如此?但即使如此,问题仍然存在,究竟有什么区别?

  1. Scheme 中 let 和 let* 有什么区别? --> 在 Clojure 中是一样的吗?

显然还有一个 ifif* 对。与 let 一样,文档不容易找到。我不知道 if 是否解构,所以肯定还有其他事情发生了。 - Reb.Cabin
@Reb.Cabin,有趣。由于if*没有被记录,它可能是内部使用的,而不是用于公共消费。我查看了GitHub上的Clojure源代码,但GitHub的搜索功能会去掉*。有很多if的情况。我想答案是下载本地源代码并在本地运行grep - Shannon Severance
Clojure没有Scheme的let等价物。Clojure的let具有Scheme的let*的语义。 - DaoWen
2个回答

21

1
谢谢您的解释,我试过了,事实上(let [[a b c & d :as e] [1 2 3 4 5 6 7]] [a b c d e])是可以运行的,而(let* [[a b c & d :as e] [1 2 3 4 5 6 7]] [a b c d e])会导致编译异常。 - Attilio
1
我可能没有表达清楚。 let* 没有解构功能。let 宏调用 let*,在此过程中提供了解构功能。 - Shannon Severance
是的,我完全理解了(我的意思是,我找到了一个例子,正好证明了这一点 :))对于造成的困惑,我感到抱歉。 - Attilio

1
我觉得我需要补充一下,为什么macroexpand返回let*而不是let,可以在macroexpand文档中找到:

重复调用表单的macroexpand-1,直到它不再表示一个宏表单,然后返回它。

所以发生的情况是第一次调用macroexpand-1返回(let [somevar "Value"] somevar),第二次将let扩展为let*

确实如此,

user=> (println (clojure.string/join "\n" (take 3 (iterate macroexpand-1 '(somemacro)))))
(somemacro)
(let [somevar "Value"] somevar)
(let* [somevar "Value"] somevar)
nil

如果您在宏中使用解构,输出结果会更有趣:
user=> (defmacro destructuring-macro [] `(let [[x y z] [:x :y :z]] y))
#'user/destructuring-macro

user=> (println (clojure.string/join "\n" (take 3 (iterate macroexpand-1 '(destructuring-macro)))))
(destructuring-macro)
(clojure.core/let [[testing.core/x testing.core/y testing.core/z] [:x :y :z]] testing.core/y)
(let* [vec__8356 [:x :y :z] x (clojure.core/nth vec__8356 0 nil) y (clojure.core/nth vec__8356 1 nil) z (clojure.core/nth vec__8356 2 nil)] testing.core/y)
nil

注意到let被语法引用完全限定,因为它不是特殊形式(尽管它的文档说它是)。底层特殊形式是let*,它没有被语法引用完全限定。

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