Hatena::ブログ(Diary)

すにぺっと

2011-04-12

clojureプログラミング入門-19 マルチメソッドを定義するその2

| 09:49 | clojureプログラミング入門-19 マルチメソッドを定義するその2を含むブックマーク

マルチメソッド続き。

前回定義したマルチメソッドmy-printでは

ディスパッチの値に:defaultとしておけば、

メソッドののどれにもマッチしなかった場合の処理を定義できる。

(defmethod my-print :default [s]
 (.write *out* "#<")
 (.write *out* s)
 (.write *out* ">"))

ちなみに、:defaultに別の意味として使いたい場合(あまりないと思うけど)

(defmulti name dispatch-fn :default default-value)

とすればOK。


これらの例でやったように、最初の引数の型でディパッチというのは

よく使われるやりかたとのこと。

次にその例を示す。


単純なディスパッチの先に

clojureのprint関数は、シーケンス的なものをリストとして出力する。

my-printでもおなじようにするには、コレクション型(Colection)でディパッチ

するようにする。

user=> (defmulti my-print class)
nil
user=> (use '[clojure.contrib.str-utils :only (str-join)])
nil
user=> (defmethod my-print java.util.Collection [c]
  (.write *out* "(")
  (.write *out* (str-join " " c))
  (.write *out* ")"))
#<MultiFn clojure.lang.MultiFn@7df44ec7>
user=> (my-print (take 6 (cycle [1 2 3])))
(1 2 3 1 2 3)nil

次にベクタに特化したmy-printを追加。

user=> (defmethod my-print clojure.lang.IPersistentVector [c]
  (.write *out* "[")
  (.write *out* (str-join " " c))
  (.write *out* "]"))
#<MultiFn clojure.lang.MultiFn@7df44ec7>
user=> (my-print [1 2 3])
java.lang.IllegalArgumentException: Multiple methods in multimethod 'my-print' match dispatch value: class clojure.lang.PersistentVector -> interface clojure.lang.IPersistentVector and interface java.util.Collection, and neither is preferred (NO_SOURCE_FILE:0)

するとエラーが。

これはベクタ引数にたいして2つのメソッドがマッチしてしまったから。


clojureの場合、衝突した場合にどちらを優先するか実装者がprefer-methodで決定する。

(prefer-method multi-name loved-dispatch dissed-dispatch)

メソッドが衝突した場合、loved-dispatchのほうが、dissed-dispatchより

優先されることを明示する。

user=> (prefer-method my-print clojure.lang.IPersistentVector java.util.Collection)
#<MultiFn clojure.lang.MultiFn@7df44ec7>
user=> (my-print [1 2 3])
[1 2 3]nil

これでうまく解決できるようになった。


なるほど。

ほかの言語では曖昧さを解消するために規則や制約をかけているが、

clojureの場合はあえてそれを許して、曖昧だったらそのときに解消させると。


次回はアドホックに分類を導入するの巻



プログラミングClojure
プログラミングClojure
posted with amazlet at 11.04.12
Stuart Halloway
オーム社
売り上げランキング: 177567