Scala脳のための C# LINQ
これは Scala プログラマのための C# LINQ 機能の覚え書きだが、逆としても使えるはず。
型推論
C# には型推論がある。個人的に、ローカル変数ではできるだけ var
を使うようにしている。
var x = 1;
Scala にも var
があるけど、可能なら不変 (immutable) な val
を使うのが好ましいとされている。
val x = 1
新しい List と Array の作成
C# はインラインでコレクションを作ることができる。
using System.Collections.Generic; var list = new List<string> { "Adam", "Alice", "Bob", "Charlie" }; var array = new [] { 0, 1, 2 };
全ての Scala コレクションにファクトリメソッドがある。
val list = List("Adam", "Alice", "Bob", "Charlie") val array = Array(0, 1, 2)
ラムダ式を使ったフィルタ
C# には "enrich-my-library" 的なモンキーパッチングがあり、普通の Array に Where
メソッドが追加されている。
using System; using System.Collections.Generic; using System.Collections.Linq; var xs = array.Where(x => x >= 1);
これは Scala では何通りかの書き方がある。
array.filter(x => x >= 1) array filter { _ >= 1 } array filter { 1 <= }
投射
C# での投射は Select
と SelectMany
によって行われる。
var xs = array.Select(x => x + 1); var yx = array.SelectMany(x => new [] { x, 3 });
これは map
と flatMap
に対応する。
array map { _ + 1 } array flatMap { Array(_, 3) }
ソート
C# は OrderBy
を使ってソートすることができる。
var xs = list.OrderBy(x => x.Length);
Scala で何かをソートする必要があったことが思い出せないけど、sortBy
を使えばできる。
list sortBy { _.length }
クエリ式を使ったフィルタ
いよいよクエリ式 (query expression) の登場。
var results = from x in array where x >= 1 select x;
Scala でこれに近いものだと多分 for 内包表記 (for comprehension) だと思う。
for (x <- array if x >= 1) yield x
これに似たようなものも C# で書けるんだけど、Scala と違って foreach
が値を返さないから、まるごとメソッドでラッピングする必要がある。
static IEnumerable<int> Foo(int[] array) { foreach (var x in array) if (x >= 1) yield return x; }
クエリ式を使った投射
暗黙型 (anonymous type) への投射を C# で試そう。
var results = from x in array select new { Foo = x + 1 };
Scala は for 内包表記で。
for (x <- array) yield new { def foo = x + 1 }
中間値によるソート
中間値を用いてソートする。
using System; using System.Collections.Generic; using System.Collections.Linq; using System.Text.RegularExpression; 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# でジョインで便利になってくる。
var results = from x in list from c in x.ToCharArray() where c != 'a' && c != 'e' select c;
Scala は for 内包表記で。
for { x <- list c <- x.toCharArray if c != 'a' && c != 'e' } yield c
インナージョイン
C# でのインナージョイン。
var results = from name in list join n in array on name.Length equals n + 3 select new { name, n };
Scala は for 内包表記で。
for { name <- list n <- array if name.length == n + 3 } yield (name, n)
グループ化
C# でのグループ化。
var results = from x in list group x by x[0] into g where g.Count() > 1 select g;
for 内包表記じゃないけど、Scala でも可能。
list groupBy { _(0) } filter { case (k, vs) => vs.size > 1 }
限定子
限定子 (quantifier) はだいたい同じように動作する。
var hasThree = list.Any(x => x.Length == 3) var allThree = list.All(x => x.Length == 3)
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 のコードという形でまとめている。
- Login to post comments