KotlinでC#のLINQっぽいのを実装してみた


どうも最近Kotlinを使い始めたJava(Android)からの移行者です
普段はC#使ってます

C#のLINQ便利でいいですよね！
~~KotlinにもfilterやmapなどJavaのStreamライクなメソッドはありますがfilterなどの[ソース](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/generated/_Collections.kt#L709)を見に行くとどうも遅延実行していないっぽいのです~~

追記)遅延実行するリスト操作関数として[Sequenceとその拡張関数](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/)が用意されてました。すいません(>_<)
Sequence作成にはasSequence()関数を呼べばいいみたいです、なのでLINQライクな関数名がいいという方向けの記事になります

ということで自作してみました！


ちなみに*遅延評価*とよく聞くのですが
[遅延評価いうなキャンペーンとかどうか - ぐるぐる～](http://bleis-tift.hatenablog.com/entry/20130102/1357062031)
によるとこの種の動作は*遅延評価*と表現するのは正しくないようです
ではどう表現するのか？となるのでC#のLINQ Whereの[リファレンス](https://msdn.microsoft.com/ja-jp/library/bb534803(v=vs.110).aspx)を見に行くと

>This method is implemented by using deferred execution.
>このメソッドは遅延実行を使用して実装されます。

と表記されているので、ここでは*遅延実行*と表現します

#### 注意
* 勉強足らずなので内容には間違いがあるかもしれません
* 関数とメソッドがごちゃまぜになっています
 - なぜKotlinは関数なのか
* 圧倒的ドキュメント、コメント不足の内容になっています
* テストコードが不足しています
* mavenとかgradleよくわかっていない人が作ってます←おいw
* stdlibは1.0.0-rc-1036です

## KLinq
ほぼC#のLINQと同じ名前、引数で遅延実行を実装しているライブラリです

## ソース
ソースは[Github](https://github.com/MeilCli/KLinq)で公開しています

```groovy:build.gradle
repositories {
    mavenCentral()
    maven { url "https://raw.github.com/MeilCli/KLinq/master/klinq/repository" }
}

dependencies {
    compile 'meilcli:klinq:1.2'
}
```
で利用できるはずです

## 使い方
```kotlin
import com.twitter.meil_mitu.klinq.*
```
お決まりのおまじない()

```kotlin
var linq  = arrayOf(1,2).toEnumerable()
linq.where{ x -> x>0}...
```
toEnumerable()以外だいたいC#のと同じ感覚でイケるはずです
(上のは分離させてるけどもちろんメソッドチェーンできます)
...あれ、この場合は関数じゃなくてメソッドでよかったのかな....

```kotlin
Enumerable.range(0,10)
(0..9).toEnumeable()
```
よく使うrangeもありますが(start..end).toEnumeable()のほうが分かりやすいかもしれません
他にもEnumerable.repeat,Enumerable.emptyがあります

## 比較
|機能|LINQ(C#)|KLinq(Kotlin)|
|:--|:--|:--|
|**要素の取得(単一)**|　|　|
|　|ElementAt|elementAt|
|　|ElementAtOrDefault|elementAtOrDefault|
|　|First|first|
|　|FirstOrDefault|firstOrDefault|
|　|Last|last|
|　|LastOrDefault|lastOrDefault|
|　|Single|single|
|　|SingleOrDefault|singleOrDefault|
|**要素の取得(複数)**|　|　|
|　|Where|where|
|　|Distinct|distinct|
|　|Skip|skip|
|　|SkipWhile|skipWhile|
|　|Take|take|
|　|TakeWhile|takeWhile|
|**集計**|　|　|
|　|Max|max|
|　|Min|min|
|　|Average|average|
|　|Sum|sum|
|　|Count|count|
|　|Aggregate|aggregate|
|**判定**|　|　|
|　|All|all|
|　|Any|any|
|　|Contains|contains|
|　|SequenceEqual|sequenceEqual|
|**集合**|　|　|
|　|Union|union|
|　|Except|except|
|　|Intersect|intersect|
|**ソート**|　|　|
|　|OrderBy|orderBy|
|　|OrderByDescending|orderByDescending|
|　|ThenBy|thenBy|
|　|ThenByDescending|thenByDescending|
|　|Reverse|reverse|
|**射影**|　|　|
|　|Select|select|
|　|SelectMany|selectMany|
|　|GroupBy|groupBy|
|**結合**|　|　|
|　|Join|join|
|　|GroupJoin|groupJoin|
|　|Concat|concat|
|　|DefaultIfEmpty|defaultIfEmpty|
|　|Zip|zip|
|**変換**|　|　|
|　|OfType|ofType|
|　|Cast|cast|
|　|ToArray|toArray|
|　|ToDictionary|toDictionary|
|　|ToList|toList|
|　|ToLookup|toLookup|
|　|AsEnumerable|asEnumerable|
|**よくやる奴**|　|　|
|　|　|forEach|


## ToDo
* ArrayかIterableに対して.toEnumeable()を呼ばなければいけない
 - 関数の名前がstdlibのと衝突する場合があるので名前を変えずに実装したかったのでこの対処法になりました
 - 追記)SequenceもasSequence()関数を用意してるあたりこうするしかないようですね
* KotlinにはC#のdefault(T)がないのでxxxOrDefault関数などで規定値nullになる
 - TがNumberを継承しているかどうかさえ見ればなんとかなる気がしないこともないけどむりそう
* average関数でJVMだとジェネリクスのオーバーロードで衝突するのでNumberを利用
* sum関数でJVMだとジェネリクスのオーバーロードで衝突するのでNumberを利用→返り値がすべてDoubleになった
* ~~ofType,cast関数、いい実装方法が思いつかなかった~~
 - 追記)reifiedでそれっぽく対応
* deferredの実装がいいのだろうけど一部lazyの実装してしまった
* 関数のオーバーロードにデフォルト引数を多用したけどIntellij IDEAの補完的に普通にオーバーロードしたほうがいいかもしれない

## 参考
* [LINQの拡張メソッド一覧と、ほぼ全部のサンプルを作ってみました。- 地平線に行く](http://d.hatena.ne.jp/chiheisen/20111031/1320068429)
 - 関数の並び順、LINQの挙動など参考にしました
* [IEnumerableなどのリファレンス](https://msdn.microsoft.com/ja-jp/library/9eekhta0(v=vs.110).aspx)
 - 引数などの参考にしました