LoginSignup
4
4

More than 5 years have passed since last update.

Clojureでオプションで場所指定可能なスレッディングマクロを作る

Posted at

Clojureの-> ->>スレッディングマクロで前回フォーム結果を任意の場所に展開したい場合、以下のようにラムダを使用することができます。

(->> (range 10) (map -) (#(nth % 3)))

ただ、この方法だと (#( となるのが少し気持ち悪いです。また、ラムダはネストすることができないので、なるべく節約したいところです。
as->マクロもありますがas->は毎回場所を指定しないといけないので面倒です。

そこで、シンボルとして<>が出現したときだけその場所に前回フォーム結果が展開され、<>がないときは-> ->>と同じ動作をするスレッディングマクロとしてo> o>>を作ってみます。

optional_threading_macro.clj
(defn cons-nth [index x coll]
  (concat (take index coll) [x] (drop index coll)))
(defn cons-last [x coll]
  (concat coll [x]))

(defn- find-symbol [form sym]
  (cond (or (sequential? form) (set? form)) (some #(find-symbol % sym) form)
        (map? form) (or (find-symbol (keys form) sym)
                        (find-symbol (vals form) sym))
        :else (= form sym)))

(defmacro o> [x & forms]
  (loop [x x forms forms]
    (if-not forms x
      (let [form (first forms)
            threaded (if (seq? form)
                       (-> (if (find-symbol form '<>)
                             (list 'let ['<> x] form)
                             (cons-nth 1 x form))
                           (with-meta (meta form)))
                       (list form x))]
        (recur threaded (next forms))))))

(defmacro o>> [x & forms]
  (loop [x x forms forms]
    (if-not forms x
      (let [form (first forms)
            threaded (if (seq? form)
                       (-> (if (find-symbol form '<>)
                             (list 'let ['<> x] form)
                             (cons-last x form))
                           (with-meta (meta form)))
                       (list form x))]
        (recur threaded (next forms))))))

このマクロを用いたサンプルは以下のとおりです。<>が現れたときのみ、その場所に前回フォーム結果が展開されています。

user=> (o>> 10 (range) reverse (map -) (nth <> 3) (identity {:a [1 <>]}))
{:a [1 -6]}
user=> (o> 10 (range) reverse (nth 3) (map - (range <>)))
(0 -1 -2 -3 -4 -5)
4
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4