Scala脳のための C# LINQ
これは Scala プログラマのための C# LINQ 機能の覚え書きだが、逆としても使えるはず。
型推論
C# には型推論がある。個人的に、ローカル変数ではできるだけ var
を使うようにしている。
Scala にも var
があるけど、可能なら不変 (immutable) な val
を使うのが好ましいとされている。
val x = 1
新しい List と Array の作成
C# はインラインでコレクションを作ることができる。
var list = new List
全ての Scala コレクションにファクトリメソッドがある。
val list = List("Adam", "Alice", "Bob", "Charlie")
val array = Array(0, 1, 2)
ラムダ式を使ったフィルタ
C# には “enrich-my-library” 的なモンキーパッチングがあり、普通の Array に Where
メソッドが追加されている。
var xs = array.Where(x => x >= 1);
これは Scala では何通りかの書き方がある。
array.filter(x => x >= 1)
array filter { _ >= 1 }
array filter { 1 <= }
投射
C# での投射は Select
と SelectMany
によって行われる。
これは map
と flatMap
に対応する。
array map { _ + 1 }
array flatMap { Array(_, 3) }
ソート
C# は OrderBy
を使ってソートすることができる。
Scala で何かをソートする必要があったことが思い出せないけど、sortBy
を使えばできる。
list sortBy { _.length }
クエリ式を使ったフィルタ
いよいよクエリ式 (query expression) の登場。
Scala でこれに近いものだと多分 for 内包表記 (for comprehension) だと思う。
for (x <- array if x >= 1)
yield x
これに似たようなものも C# で書けるんだけど、Scala と違って foreach
が値を返さないから、まるごとメソッドでラッピングする必要がある。
クエリ式を使った投射
暗黙型 (anonymous type) への投射を C# で試そう。
Scala は for 内包表記で。
for (x <- array)
yield new { def foo = x + 1 }
中間値によるソート
中間値を用いてソートする。
var results = from x in list let cs = new Regex(@"[aeiou]").Replace(x.ToLower(), “”) orderby cs.Length select x;
Scala の for 内包表記ではソートできないけど、後付けでソートできる。
list sortBy { x =>
val cs = """[aeiou]""".r.replaceAllIn(x.toLowerCase, "")
cs.length
}
クロスジョイン
この SQLっぽさは C# でジョインで便利になってくる。
Scala は for 内包表記で。
for {
x <- list
c <- x.toCharArray
if c != 'a' && c != 'e'
} yield c
インナージョイン
C# でのインナージョイン。
Scala は for 内包表記で。
for {
name <- list
n <- array if name.length == n + 3
} yield (name, n)
グループ化
C# でのグループ化。
for 内包表記じゃないけど、Scala でも可能。
list groupBy { _(0) } filter { case (k, vs) =>
vs.size > 1 }
限定子
限定子 (quantifier) はだいたい同じように動作する。
Scala では。
val hasThree = list exists { _.length == 3 }
val allThree = list forall { _.length == 3 }
パターンマッチング
Scala に特徴的なのはラムダ式が期待されている所に部分関数 (partial function) を渡すことができることだ。
array map {
case 1 => "foo"
case n if n % 2 == 0 => n.toString + "!"
}
C# でこれを真似するには自分で例外を投げる必要があると思う。
感想
Scala では、for 内包表記よりも普通の filter
とか map
を呼び出すのが好みだ。演算子の中置記法とプレースホルダ構文のお陰で array filter { _ >= 1 }
が十分簡潔になってるから、入れ子で使わない限りは for 内包表記の方が見た目が大きくなっている。
一方 C# は、クエリ式の構文はメソッド構文からいくつかのシンボルを (.
, ()
, =>
) を取り除いている。
ここで書ききれなかったことの全ては、Rahul (@missingfaktor) が Enumerable のメソッドとそれに対応する Scala のコードという形でまとめている。