Javaのラムダ使うAPIとClojureをちょこっと比較してみる

ちょこっとじゃない？？

JavaのStreamとClojureはわりと似たような感じで書けたりします

```range_filter_map_forEach.java
IntStream.range(1, 10)
        .filter(i -> i % 2 == 0)
        .map(i -> i * 3)
        .forEach(System.out::println);
```

```range_filter_map_doseq.clj
(->> (range 1 10)
     (filter #(zero? (rem % 2)))
     (map #(* % 3))
     (#(doseq [i %] (println i))))
```

```skip_limit_reduce.java
IntStream.range(1, 10)
        .skip(3)
        .limit(5)
        .reduce((i, j) -> i * j)
        .ifPresent(System.out::println);
```

```drop_take_reduce.clj
(->> (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にすると以下のような感じでしょうか

```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で実現するとこんな感じでしょうか

```map-vals-with-key.clj
(defn map-vals-with-key [f hmap]
  (reduce (fn [hmap [k v]] (assoc hmap k (f k v))) hmap hmap))
```

また、Clojureのmerge-withをJavaで実現するとこんな感じ？

```MergeWith.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の方です