为什么我无法绑定+在 Clojure 中?

发布于 2024-08-23 10:05:45 字数 187 浏览 9 评论 0原文

谁能解释为什么我可以重新绑定列表但不能重新绑定+?

(binding [list vector]
  (list 1 3))
(binding [list +]
  (list 1 3))
(binding [+ list]
  (+ 1 3))

我想重新绑定 + 这样我就可以进行部分评估。

Can anyone explain why I can rebind list but not +?

(binding [list vector]
  (list 1 3))
(binding [list +]
  (list 1 3))
(binding [+ list]
  (+ 1 3))

I'd like to rebind + so I can do partial evaluation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

儭儭莪哋寶赑 2024-08-30 10:05:45

至少在 Clojure 1.1.0 中,为了提高性能,带有两个参数的 + 被内联。你的绑定发生得太晚了。随着更多的争论,它的工作方式会有所不同。

Clojure 1.1.0-master-SNAPSHOT
user=> (binding [+ -] (+ 1 2))
3
user=> (binding [+ -] (+ 1 2 3))
-4

一种解决方法是使用您自己的函数创建您自己的命名空间和影子 clojure.core/+

user=> (ns foo (:refer-clojure :exclude [+]))
nil
foo=> (defn + [& args] (reduce clojure.core/+ args))
#'foo/+
foo=> (+ 1 2)
3
foo=> (binding [+ -] (+ 1 2))
-1

请注意,在 Clojure 1.2.0 的当前快照中,内联似乎更加频繁地发生。

Clojure 1.2.0-master-SNAPSHOT
user=> (binding [+ -] (+ 1 2))
3
user=> (binding [+ -] (+ 1 2 3))
6

最明智的做法可能是使用 + 以外的函数名称,例如 add,以避免混淆。

In Clojure 1.1.0 at least, + with two arguments is inlined for performance. Your binding happens too late. With more arguments it works differently.

Clojure 1.1.0-master-SNAPSHOT
user=> (binding [+ -] (+ 1 2))
3
user=> (binding [+ -] (+ 1 2 3))
-4

One workaround is to make your own namespace and shadow clojure.core/+ with your own function.

user=> (ns foo (:refer-clojure :exclude [+]))
nil
foo=> (defn + [& args] (reduce clojure.core/+ args))
#'foo/+
foo=> (+ 1 2)
3
foo=> (binding [+ -] (+ 1 2))
-1

Note that inlining appears to happen even more aggressively in the current snapshot of Clojure 1.2.0.

Clojure 1.2.0-master-SNAPSHOT
user=> (binding [+ -] (+ 1 2))
3
user=> (binding [+ -] (+ 1 2 3))
6

It may be wisest to use a function name other than +, e.g. add, to avoid confusion.

多彩岁月 2024-08-30 10:05:45

快速解决方法:使用let而不是绑定,这对你来说很好:

user=> (let [+ list] (+ 2 3))
(2 3)

稍微(不完整)深入探究一下原因:

看看 + 的源代码函数:

(defn +
  "Returns the sum of nums. (+) returns 0."
  {:inline (fn [x y] `(. clojure.lang.Numbers (add ~x ~y)))
   :inline-arities #{2}}
  ([] 0)
  ([x] (cast Number x))
  ([x y] (. clojure.lang.Numbers (add x y)))
  ([x y & more]
   (reduce + (+ x y) more)))

请注意,有多个内联函数定义用于不同数量的参数。如果您尝试重新绑定 0 或 1 个参数定义,它会正常工作:

user=> (binding [+ (fn [] "foo")] (+))
"foo"
user=> (binding [+ (fn [a] (list a))] (+ 1))
(1)

现在,这对于 2 个参数的情况绝对不起作用(正如您所发现的)。我不太能把这些点联系起来,但是. (特殊形式)让我怀疑结合绑定是一个宏,而 let 是一种特殊形式......

专门调用 arity 2 的元数据也似乎很可疑。

Quick workaround: use let instead of binding and this will work for you just fine:

user=> (let [+ list] (+ 2 3))
(2 3)

A little (incomplete) digging into the reason:

Take a look at the source for the + function:

(defn +
  "Returns the sum of nums. (+) returns 0."
  {:inline (fn [x y] `(. clojure.lang.Numbers (add ~x ~y)))
   :inline-arities #{2}}
  ([] 0)
  ([x] (cast Number x))
  ([x y] (. clojure.lang.Numbers (add x y)))
  ([x y & more]
   (reduce + (+ x y) more)))

Notice that there are several inline function definitions for different numbers of arguments. If you try to rebind the 0 or 1 arity definitions, it works just fine:

user=> (binding [+ (fn [] "foo")] (+))
"foo"
user=> (binding [+ (fn [a] (list a))] (+ 1))
(1)

Now, this definitely doesn't work (as you discovered) for the 2-argument case. I'm not quite connecting the dots, but the . (special form) makes me suspicious combined with binding being a macro whereas let is a special form...

The metadata specially calling out arity 2 also seems suspicious.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文