9

リストを再バインドできるのに + を再バインドできない理由を誰か説明できますか?

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

部分評価できるように + を再バインドしたいのですが。

4

2 に答える 2

8

少なくとも Clojure 1.1.0 では、+パフォーマンスのために 2 つの引数をインライン化しています。あなたのバインディングは遅すぎます。より多くの引数を使用すると、動作が異なります。

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

clojure.core/+1 つの回避策は、独自の関数で独自の名前空間とシャドウを作成することです。

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

于 2010-02-28T19:27:01.153 に答える
6

簡単な回避策:バインディングの代わりに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 は特殊な形式です...

特にアリティ 2 を呼び出しているメタデータも疑わしいようです。

于 2010-02-28T19:26:15.763 に答える