2015/03/24

Clojureのdefun, パターンマッチ付きの関数定義

 defunというと, Common LispやEmacs Lispの関数定義を連想しますが, Clojureでもdefunを使って関数定義ができるようです. さっき, 偶然発見したのですが.

 このdefun, Clojureの普通の関数定義であるdefnとの違いは, パターンマッチ付きの関数定義ができることです. GitHub中の説明には,
A macro to define clojure functions with parameter pattern matching just like erlang or elixir.
 とあります.

 Clojureのパターンマッチングといえば, core.matchかdefmulti/defmethodのマルチディスパッチですが, 関数の引数に対して, そのままパターンを記述できるので, defn+matchの組み合わせよりもシンプルになります.

 .lein/profile.clj (or project.clj)の:pluginsに[defun "0.2.0-RC"]を追加してとりあえず, quicksortもどきを書いてみると,
 core.match版
(use '[clojure.core.match :refer [match]])

(defn quicksort1 [ls]
  (match ls
   ([] :seq) []
   ([x & xs] :seq)
   (concat (quicksort1 (filter #(< % x) xs))
           [x] (quicksort1 (filter #(>= % x) xs)))))
 と, defun版
(use '[defun :only [defun]])

(defun quicksort2
  ([([] :seq)] [])
  ([([x & xs] :seq)]
   (concat (quicksort2 (filter #(< % x) xs))
           [x] (quicksort2 (filter #(>= % x) xs)))))
 余分なシンボルが減り, 括弧だけになってすっきりした印象になりました. matchの一旦変数を定義して, それをそのままmatchマクロに渡すという処理がなくなり1行分減り, 変数名が一つ減っています. しかし, 括弧は, 条件一つにつき, 2ペア増えてしまいました. どうやら, この括弧は(大括弧, 小括弧ともに)外せないようです.
(defun sum
  ([0 m] m)
  ([n m] (recur (dec n) (+ n m))))
こんな感じで, recurが内臓させることも可能なようで, loop-recurの省略にも使えます.

 パターンやガードの記述は, core.matchと同じ(というかcore.matchそのもの)なので, core.matchを使う要領で定義できます.

0 件のコメント :