3

代替

(let [x 1] (my-expression))

我正在尝试使用:

(let (vector x 1) (my-expression))

不要问为什么,我只是更喜欢普通的括号。但是 Clojure 说:

let requires a vector for its binding in ...

怎么了?

4

4 回答 4

7

let 特殊形式绑定形式需要是一个向量字面量,而不仅仅是一个将评估为向量的表达式。

为什么?粗略地说,必须先编译表达式,然后才能对其求值。在编译时(vector x 1)不会被评估为 a vector,它只是一个列表。实际上,如果要对其进行评估,则将评估 的论点,必须解决vector含义。x但是,你不想x被解决,你希望它被绑定。

于 2013-02-19T21:33:29.920 回答
3

查看宏的来源let

(defmacro let
  "binding => binding-form init-expr

  Evaluates the exprs in a lexical context in which the symbols in
  the binding-forms are bound to their respective init-exprs or parts
  therein."
  {:added "1.0", :special-form true, :forms '[(let [bindings*] exprs*)]}
  [bindings & body]
  (assert-args let
     (vector? bindings) "a vector for its binding"
     (even? (count bindings)) "an even number of forms in binding vector")
  `(let* ~(destructure bindings) ~@body))

您会注意到,bindings当宏试图确保通过assert-args.

在 clojure 计算时(vector? bindings)bindings是一个表单 ( list),其中包含 afn作为第一个元素,后跟它的参数,因此此时不是 a vector

于 2013-02-19T21:39:07.493 回答
3

let它的绑定需要一个向量(在编译时),因此试图将向量函数调用放在它的位置是行不通的(因为这只会在运行时产生一个向量)。

但是,您可以使用一些宏来制作自己的 let:

(defmacro mylet [bindings & exprs]
  `(let ~(vec bindings) ~@exprs))

(mylet (x 1) (inc x))
=> 2
于 2013-02-20T03:16:35.060 回答
0

例如,此代码确实有效:

(eval `(let ~(vector 'a 1) (println ~'a)))

这意味着您可以编写自己的 let 宏来接受列表而不是向量。这对您的整体 Clojure 体验非常不利,我不建议这样做。

于 2013-02-19T21:57:28.873 回答