LoginSignup
2
0

More than 5 years have passed since last update.

[Clojure]デバッグのための簡単なマクロ

Last updated at Posted at 2019-04-22

「プログラミングとはデバッグである」

そんな言葉を言っていた人がいました。そして、デバッグで基本となるのが

「とりあえず怪しい変数をプリントしていく」

事ではないでしょうか。

ClojureではREPL(Read Eval Print Loop)が使えるため、データの可視化は容易にできます。しかし、それでも関数が長くなるとその関数の中でどのようなデータが処理されているのか不透明になることもあるかと思います。

そこで、関数内で任意の変数をその変数名と共にプリントするマクロを作りました。

debug

下のマクロdebugは任意個の引数をとり、そられを変数名とともにプリントします。

(defmacro debug
  "Prints args. This is useful when debugging."
  [& args]
  (let [sym (gensym "sym") val (gensym "val")]
    `(do
       (println "debug information:\n")
       ~@(map (fn [sym val] `(println ~sym " : " ~val)) (map str args) args)
       (println))))

マクロを作成した場合、

macroexpand

を使って、どのようにコードが展開されるのかを確認できます。

(def x 1)

(def y 2)

(macroexpand '(debug x y))

;; => (do
;;      (clojure.core/println "debug information:\n")
;;      (clojure.core/println sym15155 " : " val15156)
;;      (clojure.core/println sym15155 " : " val15156)
;;      (clojure.core/println))

実際に上記のコードを評価してみると、以下の結果を得ました。

(def x 1)

(def y 2)

(debug x y)

;; =>   x  :  1
;;      y  :  2

debugを関数の中に挿入すれば、好きな変数をプリントしその値を確認することができます。

例として、整数のシーケンスを引数にとり、

「偶数のみフィルターしたあとそれらを二乗する」

という変形をする関数 filter-even-then-square を作ってみます。

(defn filter-even-then-square [coll]
  (let [original coll
        only-even (filter even? original)
        squared (map #(* % %) only-even)]
    squared))

(filter-even-then-square [1 2 3 4]) ;; => (4 16)

上の例では想定どおりの結果が得られていますが、「途中のデータの変形」については情報がありません。

そこで、debugを挿入するとデータ変形の途中経過を可視化できます。

(defn filter-even-then-square [coll]
  (let [original coll
        only-even (filter even? coll)
        squared (map #(* % %) only-even)]

    ;; letしたデータをプリント
    (debug original only-even squared)

    squared))

(filter-even-then-square [1 2 3 4]) ;; => (4 16)

結果は同じですが、repl bufferにdebugに渡したデータが表示されました。

debug information:

original  :  [1 2 3 4]
only-even  :  (2 4)
squared  :  (4 16)

ちなみに、filter-even-then-squareのような関数は、Clojureに標準装備されている「アローマクロ」を使うと簡潔に書けます。

;; 上と同じ関数をアローマクロで作成
(defn filter-even-then-square [coll]
  (->> coll (filter even?) (map #(* % %))))

この程度の変形であれば、わざわざデータをプリントする必要はないかもしれませえんが、もっと長い関数を書く場合にはdebugのようなマクロが役に立つのではないでしょうか。

今回の内容は以上になります。最後までお読み頂きありがとございました。

2
0
0

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
2
0