日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

Clojure の destructuring でちょっと驚いた事

祝 Clojure1.6 正式リリース

個人的に目を引くような変更点 はあんまりないんだが、1.5 から使い勝手は変わらずに不具合修正もいくらかされているみたいので、既に 遭遇してる人はもちろんの事、この先要らん地雷を踏みたくない人もアップデートかけた方が良いかと。

で、強調までしたものの今回の記事の本題は 1.6 とはまるで関係ないお話。

clojure.core.memoizeを使いたくてコード書いてる時、

基本形が

(require '[clojure.core.memoize :as memo]) ; もちろんここは一度きり

(def func-a-lrufn (memo/lru (fn [some-params] (some-proc some-params))))
(defn func-a
  [some-params]
  (func-a-lrufn some-params))

であるのはいいんだが、これが 1 つ 2 つまでならまだしも割とたくさん定義したくなる時面倒になる。

んでこんな感じの手抜きマクロを定義してみた。

(defmacro defn-lru
  [name [& params*] threshold-val & body] ;; <- ん?
  (let [private-fn (symbol (str name "-lrufn"))]
    `(do
       (def ~(with-meta private-fn {:private true})
         (memo/lru (fn [~@params*] ~@body) :lru/threshold ~threshold-val))
       (defn ~name [~@params*] (~private-fn ~@params*)))))

使い方はこんな感じ。

(defn-lru func-a [some-params] 2
  (some-proc some-params))
;=> #'user/func-a

defn と殆ど変わらないが、

  • private 化しているものの固定 suffix で func-a-lrufn と言う名前のメモ化関数を暗に定義している
  • 引数リストの後に LRU の上限指定(threshold-val)必須
  • このマクロからの meta や docstring 定義不可

あたりが手抜きな部分。

それはさておき今回の記事で言いたいのはコメントの部分。 何となくできるかなあ、できないんだろうなあと思いつつ [& params*] って書いてみたら動いたのに我ながらちょっと驚いたw 一番最後に付けると言うのさえ守れば、& ってネスト可能なんだなと。

公式のここって事かな?

おまけ - スニペット全部

最初 Gist に書いて埋め込もうとしたけど、指定行だけ埋め込む方法分からなかったので今回ははてな記法の方で書いた。 けどせっかくなので全部入りの Gist ものっけとく。