📌

Clojure の map で println を出したい時の解決策3選

2025/03/03に公開

We love lazy, yes we love! みんな大好き遅延評価 🩷 ですが、ときどき

(map myfunc coll) を実行中 myfunc の中に println を入れて、途中経過を出力させたい ...

というダサい気持ちになりますよね。

たとえば、今どの引数を処理しているのかプリントしたい、とか。

(defn myfunc [x]
  (println "Processing:" x)
  (* x 2))

こういう時の解決策3選をご紹介します。

1. doall で遅延を強制評価

doall を使うと map の結果を強制的に評価できます。

(doall (map myfunc [1 2 3]))
; Processing: 1
; Processing: 2
; Processing: 3
(2 4 6)

2. doseq を使う

println のような副作用を実行したいのであれば、doseq でも十分です。
doseqは副作用を実行するためのループなので返り値を持ちません(= nil を返す)。

(doseq [x [1 2 3]]
  (println "Processing:" x)
  (* x 2))
; Processing: 1
; Processing: 2
; Processing: 3
nil

(* x 2) は実行はされますが、どこにも格納されずそのまま破棄されます。

3. mapv を使う

個人的に一番かっこいいのはこれです。mapv は、強制評価してベクタを返します。

(mapv myfunc [1 2 3])
; Processing: 1
; Processing: 2
; Processing: 3
[2 4 6]

mapvは、内部的に reduce を使っているので遅延せず評価されます。

https://github.com/clojure/clojure/blob/ce55092f2b2f5481d25cff6205470c1335760ef6/src/clj/clojure/core.clj#L6970-L6986

以上です! Happy Coding!


2025/03/03 10:55 Updated :

近所から、myfunc の引数が2つ以上でもできるの?と質問が来ました。mapv ソースコードを見ると into 使ってあって reduce じゃないじゃん、と。確かに。

やってみましょう。

(mapv myfunc [1 2 3] [4 5 6])
; Processing: 1 4
; Processing: 2 5
; Processing: 3 6
[4 10 18]

できた。では into を見に行きます。(reduce のすぐ上にありました。)

https://github.com/clojure/clojure/blob/ce55092f2b2f5481d25cff6205470c1335760ef6/src/clj/clojure/core.clj#L6950-L6986

into の実装にも reduce が使用されています。なるほど奥深い。

Discussion