【问题标题】:Difference between let and let* in ClojureClojure 中 let 和 let* 的区别
【发布时间】:2015-10-18 02:47:42
【问题描述】:

考虑以下宏:

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

展开它会产生以下结果:

(macroexpand '(somemacro))

结果:

(let* [somevar "Value"] somevar)

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

  • 这是什么意思? (特别是:它是否记录在某处?)
  • 为什么宏没有用“普通”let 展开? (即,不带星号。)两者都产生相同的结果(在我的实验中)。有反例吗?

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

我已经考虑过的来源:

(doc let*)  ; --> nil
(source let*)  ; --> source not found
  1. https://clojuredocs.org/clojure.core --> 我在这里看到 not let* (虽然有例如列表*)
  2. https://clojuredocs.org/clojure.core/let --> 只提到过一次 评论,这对我来说并不完全清楚:

    注意事项:Clojure 中的 let 就像 Scheme 中的 let* —— 每个 init-expr 都可以访问前面的绑定形式。 (还有一个let*,但多多少少是不解构的let,其实是底层实现。)

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

    在 Clojure 中,它的基本意思是“foo* 与 foo 类似,但有些不同,你可能想要 foo”。换句话说,这意味着该代码的作者无法为第二个函数想出更好的名称,所以他们只是给它打了一颗星。

--> let 和 let* 是这种情况吗?但如果是这样,那么问题仍然存在,到底有什么区别?

  1. What is the difference between let and let* in Scheme? --> Clojure 也是这样吗?

【问题讨论】:

  • 显然还有一对ifif*。与let 一样,文档也不容易找到。我不知道 if 会解构,所以肯定有其他事情发生。
  • @Reb.Cabin,有趣。 if* 没有被记录在案,它可能是内部的,不适合消费。我查看了 GitHub 上的 Clojure 源代码,但 GitHub 的搜索将 * 删除了。 if的案例很多。我想答案是在本地下载源代码并在本地运行grep
  • Clojure 没有 Scheme 的 let 等价物。 Clojure 的 let 具有 Scheme 的 let* 的语义。

标签: clojure


【解决方案1】:

我想我会补充一点,macroexpand 返回let* 而不是let 的原因可以在documentation of 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*,它没有被语法引号完全限定。

【讨论】:

    【解决方案2】:

    let* 是一个内部实现细节。 let 是根据let* 实现的宏。 https://github.com/clojure/clojure/blob/clojure-1.7.0/src/clj/clojure/core.clj#L4301

    let 将参数destructuring 添加到let*。这是 Clojure 中 xyzxyz* 的标准模式,没有记录 * 版本。 listlist* 是一个例外。

    【讨论】:

    • 感谢您的解释,我试过了,事实上(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]) 导致了 CompilerException。
    • 我可能不太清楚。 let* 没有解构。 let 宏调用let*,在过程中提供解构。
    • 是的,我完全理解了(我的意思是,我找到了一个可以证明这一点的例子:))抱歉造成混淆。
    猜你喜欢
    • 2013-09-22
    • 2013-10-04
    • 2018-12-09
    • 2013-02-06
    • 2015-06-04
    • 1970-01-01
    • 1970-01-01
    • 2016-09-27
    • 2017-10-28
    相关资源
    最近更新 更多