ちょこっとじゃない??
JavaのStreamとClojureはわりと似たような感じで書けたりします
IntStream.range(1, 10)
.filter(i -> i % 2 == 0)
.map(i -> i * 3)
.forEach(System.out::println);
(->> (range 1 10)
(filter #(zero? (rem % 2)))
(map #(* % 3))
(#(doseq [i %] (println i))))
IntStream.range(1, 10)
.skip(3)
.limit(5)
.reduce((i, j) -> i * j)
.ifPresent(System.out::println);
(->> (range 1 10)
(drop 3)
(take 5)
(reduce *)
println)
わりと似てる・・・
以降ではクラスごとにどんな機能があるか見てみます
java.util.stream.Stream関連
Streamクラス、IntStreamクラス等の機能です
機能 | Java | Clojure |
---|---|---|
範囲(endを含まない) | range | range |
範囲(endを含む) | rangeClosed | (range (inc x)) |
条件がtrueのものを残す | filter | filter |
条件がfalseのものを残す | filter((...).negate()) | remove |
関数適用した結果に変換 | map | map |
関数適用した結果がnullのものを除外するmap | map(...).filter(x -> x != null) | keep |
適用する関数の入力にindexがあるmap,keep | map-indexed,keep-indexed | |
関数適用した結果のネスト構造を1つ解くmap | flatMap | mapcat |
副作用を伴うループ実行 | forEach | doseq |
畳み込み処理 | reduce | reduce |
指定した最大件数まで取得(先頭から) | limit | take |
指定した件数を破棄(先頭から) | skip | drop |
指定件数を最後から取得、破棄 | take-last,drop-last | |
指定条件を満たす要素が現れるまで取得、破棄 | take-while,drop-while | |
条件を満たすものが1つでもあるか | anyMatch | some |
すべて条件を満たすか | allMatch | every? |
1つも条件を満たさないか | noneMatch | not-any? |
少なくとも1つ条件を満たさないものがあるか | !(... allMatch(...)) | not-every? |
1番目の要素を取得 | findFirst | first |
2番目、最後、任意の箇所の要素を取得 | second,last,nth | |
副作用処理を挟み込む | peek | #(do (...) %) |
重複要素を排除 | distinct | distinct |
ソート | sorted | sort |
関数を繰り返し適用して要素生成 | iterate | iterate |
繰り返し要素生成 | generate | repeatedly |
リストを繰り返して要素生成 | cycle | |
連結 | concat | concat |
和を取得 | sum | apply + |
最小の要素を取得 | min | min |
最大の要素を取得 | max | max |
要素数を取得 | count | count |
算術平均を取得 | average | (fn [x] (/ (apply + x) (count x))) |
Clojureのmapは複数のシーケンスを並行して扱うことができます。また、reduceは途中で止められます。
Clojureのcycle関数は、Javaにすると以下のような感じでしょうか
static <T> Stream<T> cycle(T t0, T... ts){
return Stream.generate(new Supplier<T>() {
int index = -1;
@Override
public T get() {
index = (index + 1) % (ts.length + 1);
if(index == 0){
return t0;
}
return ts[index - 1];
}
});
}
他に複数のStreamを並行して1つにまとめるいわゆるzip関数と、Streamのcloneを作れる関数がセットで欲しいところでしょうか
java.util.stream.Collectors関連
他に統計値(IntSummaryStatisticsなど)を取得できる便利な機能もあります
機能 | Java | Clojure |
---|---|---|
文字列の(区切り文字付)連結 | joining | clojure.string/join |
(条件指定して)最小の要素取得 | minBy | min-key |
(条件指定して)最大の要素取得 | maxBy | max-key |
指定分類でグルーピング | groupingBy | group-by |
条件の適用結果(true/false)でグルーピング | partitioningBy | group-by |
java.util.List関連
Javaの方はミュータブルな(Listの中身が変わる)処理で、Clojureの方はイミュータブルな処理です
機能 | Java | Clojure |
---|---|---|
条件に合致したものを削除 | removeIf | remove |
関数適用した結果に変換 | replaceAll | map |
java.util.Map関連
Listと同様にJavaの方はミュータブル
機能 | Java | Clojure |
---|---|---|
指定したキーと対応する値に関数適用して値を変換する | compute | |
指定したキーが存在しない場合、computeを行う | computeIfAbsent | |
指定したキーが存在する場合、computeを行う | computeIfPresent | |
Mapのすべての値を関数適用した結果に変換 | replaceAll | |
新しい値と指定したキーの値を(関数適用しつつ)マージする | merge | update-in |
MapとMapを(関数適用しつつ)マージする | merge-with |
※ClojureのmergeはJavaではputAllとほぼ同じ
JavaのreplaceAllをClojureで実現するとこんな感じでしょうか
(defn map-vals-with-key [f hmap]
(reduce (fn [hmap [k v]] (assoc hmap k (f k v))) hmap hmap))
また、Clojureのmerge-withをJavaで実現するとこんな感じ?
public static <K, V> Map<K, V> mergeWith(Map<K, V> m1, Map<K, V> m2, BinaryOperator<V> f){
m2.forEach((k2, v2) -> {
m1.computeIfPresent(k2, (k1, v1) -> f.apply(v1, v2));
m1.putIfAbsent(k2, v2);
});
return m1;
}
その他
Streamを取得できるメソッドとして便利なもの
機能 | Java | Clojure |
---|---|---|
行のStream | java.io.BufferedReader#lines | line-seq |
文字のStream | CharSequence#chars | (seq "abc") |
文字コードポイントのStream | CharSequence#codePoints |
文字のStreamはIntStreamになっています。サロゲートペアが2つのintになるのがcharsの方です