高階関数とは
高階関数とは、関数を引数として受け取るか、関数を戻り値として返す関数のことです。Clojureでは関数が第一級オブジェクトなので、高階関数を自然に扱えます。
基本的な高階関数
map - コレクションの各要素に関数を適用
;; 基本的な使い方
(map inc [1 2 3 4 5])
;; => (2 3 4 5 6)
;; 複数のコレクションに対して
(map + [1 2 3] [4 5 6])
;; => (5 7 9)
;; 文字列の操作
(map clojure.string/upper-case ["hello" "world"])
;; => ("HELLO" "WORLD")
filter - 条件に合う要素のみを抽出
;; 偶数のみを抽出
(filter even? [1 2 3 4 5 6])
;; => (2 4 6)
;; 文字列の長さで絞り込み
(filter #(> (count %) 3) ["cat" "dog" "elephant" "ant"])
;; => ("elephant")
reduce - コレクションを1つの値に集約
;; 合計を計算
(reduce + [1 2 3 4 5])
;; => 15
;; 最大値を求める
(reduce max [3 7 2 9 1])
;; => 9
;; 初期値を指定
(reduce + 100 [1 2 3])
;; => 106
無名関数(ラムダ関数)
fn記法
;; 基本的なfn記法
((fn [x] (* x x)) 5)
;; => 25
;; mapと組み合わせ
(map (fn [x] (* x x)) [1 2 3 4])
;; => (1 4 9 16)
;; 複数引数の無名関数
(map (fn [x y] (+ (* x x) (* y y))) [1 2 3] [4 5 6])
;; => (17 29 45)
#()記法
;; シンプルな場合
(#(* % %) 5)
;; => 25
;; mapと組み合わせ
(map #(* % %) [1 2 3 4])
;; => (1 4 9 16)
;; 複数引数(%1, %2, %3...)
(map #(+ (* %1 %1) (* %2 %2)) [1 2 3] [4 5 6])
;; => (17 29 45)
;; ネストした場合(読みにくい例)
(map #(map #(* % %) %) [[1 2] [3 4] [5 6]])
;; => ((1 4) (9 16) (25 36))
その他
過度にネストした括弧
;; 醜い書き方
(reduce + (map #(* % %) (filter even? (range 1 11))))
;; => 220
;; 読みやすい書き方(->>マクロ使用)
(->> (range 1 11)
(filter even?)
(map #(* % %))
(reduce +))
;; => 220
関数を返す関数
;; 乗数を固定した関数を生成
(defn multiplier [n]
(fn [x] (* n x)))
(def double (multiplier 2))
(def triple (multiplier 3))
(double 5) ;; => 10
(triple 4) ;; => 12
;; mapと組み合わせ
(map (multiplier 10) [1 2 3])
;; => (10 20 30)
部分適用とカリー化
;; partialを使った部分適用
(def add-10 (partial + 10))
(add-10 5) ;; => 15
(map add-10 [1 2 3 4])
;; => (11 12 13 14)
;; より複雑な例
(def starts-with-a? (partial re-matches #"^[aA].*"))
(filter starts-with-a? ["apple" "banana" "avocado" "cherry"])
;; => ("apple" "avocado")
データ処理パイプライン
(def sales-data
[{:name "Alice" :sales 1200 :region "East"}
{:name "Bob" :sales 800 :region "West"}
{:name "Charlie" :sales 1500 :region "East"}
{:name "Diana" :sales 900 :region "West"}])
;; East地域の売上合計
(->> sales-data
(filter #(= (:region %) "East"))
(map :sales)
(reduce +))
;; => 2700
;; 各地域の平均売上
(->> sales-data
(group-by :region)
(map (fn [[region people]]
[region (/ (reduce + (map :sales people))
(count people))]))
(into {}))
;; => {"East" 1350, "West" 850}
まとめ
高階関数は Clojure の強力な機能の一つです。適切に使用することで、コードの再利用性と表現力が大幅に向上します。ただし、可読性を犠牲にしないよう注意が必要です。
重要なポイント:
- 無名関数は適度に使用し、複雑になったら名前付き関数に分離する
- スレッディングマクロ(
->、->>)を活用して読みやすくする -
#()記法は単純な場合のみ使用し、複雑な場合はfnを使う - 関数合成や部分適用を活用してコードの表現力を高める