20

在一种let形式(Clojure here)中,我可以做类似的事情

(let [[u s v] (svd A)] 
   (do-something-with u v))

wheresvd返回一个长度为 3 的列表。这是一件很自然的事情,所以为什么我们没有

(def [u s v] (svd A))

及其作为def表单默认行为的各种概括?我看不出这会如何干扰def已经在做的任何事情。了解 Lisp 或 Clojure 的 Zen 的人能否解释一下为什么def不支持强大的绑定(带解构)let

4

4 回答 4

19

def是编译器级别的一种特殊形式:它生成一个 Var。def必须在解构可用之前可用和可用。你会看到类似 with的东西let*,它是一个不支持解构的编译器原语:经过几千行clojure/core.clj语言之后,它终于强大到足以提供一个letwith 解构的版本,作为let*.

如果您愿意,您可以编写一个宏(例如def+)来为您执行此操作。就我个人而言,我认为它有点粗俗,不会使用它,但使用 Lisp 意味着要使用适合您个人的语言。

于 2011-11-19T23:37:03.243 回答
3

这并不完美,但它开始于编写def+ https://clojuredocs.org/clojure.core/destructure

(defmacro def+
  "binding => binding-form
  internalizes binding-forms as if by def."
  {:added "1.9", :special-form true, :forms '[(def+ [bindings*])]}
  [& bindings]
  (let [bings (partition 2 (destructure bindings))]
    (sequence cat 
      ['(do) 
       (map (fn [[var value]] `(def ~var ~value)) bings)
       [(mapv (fn [[var _]] (str var)) bings)]])))

有了它,你可以做...

(def+ [u s v] [1 5 9], foo "bar")

...同时不影响def...的简单性

(def+ foo "bar")

...这是要求和建议的。这仍然存在将 gensym 变量引入全局命名空间的问题。可以处理 gensym 问题,但考虑到用例(在 repl 中使用),附加变量可能是可以接受的。

于 2018-02-26T22:41:47.223 回答
2

def基本上是 Vars 的构造函数。第一个参数是命名 Var 的符号。它接受该符号并为该符号返回一个 Var。解构会改变这些语义。

不过,您可以编写一个宏来完成它。

于 2011-11-19T21:19:05.230 回答
1

以下是一些哲学上的理由。

Clojure 支持不变性而不是可变性,并且应该仔细考虑和命名所有可变性来源。def创建可变变量。因此,惯用的 Clojure 无论如何都不会过多地使用它们,并且也不希望在不小心(例如通过解构)的情况下创建许多可变变量太容易。let然而,函数参数解构会创建不可变绑定,因此 Clojure 使这些绑定易于创建。

由创建的变量def具有全局范围。因此,您应该def仔细命名 ed var,并保持它们的数量很少。解构def会使def不小心创建许多 s 变得太容易了。let另一方面,函数参数解构会创建局部的、词法范围的绑定,因此解构的便利性不会导致名称污染。

于 2018-02-27T06:44:16.527 回答