Scala で怠惰なリテラル表記 [Scala]
本質的でない記述に嫌悪感を感じるセンスがあれば、同じキーが何度も現れていることを「めんどくさい」と感じるはずだ。そして、こんなふうに書けないだろうかと一度は思うはずだ。
「怠慢はプログラマの美徳」というけれど - kwatchの日記data = %h{ ['name', 'age', 'email'], ['Foo', 20, 'foo@mail.com'], ['Bar', 21, 'bar@mail.net], ['Baz', 22, 'baz@mail.org'], }
Ruby はこんな書き方できるのか、面白いなー、と思ったらそうではなくて架空の文法みたいですね。
というわけで Scala ではどうするかと思って考えてみました。
元の例はハッシュを構造体的に使って、それを表組み形式で書けるコンストラクタを作るということですが、静的言語ではあまりそういうことをしないので問題を直接には翻訳できません。
まずこういう状況で一番手軽に使えるのはタプル型です。これは全然めんどくさくありません。
val data = List(
("Foo", 20, "foo@mail.com"),
("Bar", 21, "bar@mail.net"),
("Baz", 22, "baz@mail.org")
)
でもこれだけだと name, age, email のような名前を使ったアクセスができないのでちょっと工夫を入れます。
case class Profile(name: String, age: Int, email: String)
implicit def triple2profile(triple: (String, Int, String)): Profile = triple match {
case (name, age, email) => Profile(name, age, email)
}
val data: List[Profile] = List(
("Foo", 20, "foo@mail.com"),
("Bar", 21, "bar@mail.net"),
("Baz", 22, "baz@mail.org")
)
そうすると例えばこう書けるようになります。
scala> data.foreach{ x => println(x.name) } Foo Bar Baz
以上は元の問題の構造体的側面を Scala に置き換えるという観点です。
これで十分な気もしますが、今度は連想配列という点を重視して Scala に翻訳してみます。 この場合、型の混在が問題になってきますが、とりあえず値はすべて文字列ということにして話を進めます。 あと連想配列をさらにコレクションの中に入れるというデータ構造を Scala で作ることは実はあまりない気もしますが、その辺は無視します。
まず私は元記事で提案されている %h 表記があまりよいとは思いませんでした。 第一に、この書き方の意図するところは1行目がキー、2行目以降が値ということなのですが、1行目と2行目以降の意味の違いが表記上の違いとして現れていない。 Excel で表を作るときだってヘッダ行は色分けするし HTML でも th と tr は別物です。 第二に、列の数が固定なのだから行ごとの区切りは改行で十分で、内側の大括弧は不要です。
というわけで、大体このくらいがちょうどいいのではないかと思います。
%('name, 'age, 'email) (
"Foo", "20", "foo@mail.com",
"Bar", "21", "bar@mail.net",
"Baz", "22", "baz@mail.org"
)
この書き方を可能にするための % メソッドは以下のように書きました。
def %[T,U](keys_ : T*)(values_ : U*): List[Map[T,U]] = {
val len = keys_.length
val keys = keys_.toList
val values = values_.toList
def construct(values: List[U], sofar: List[Map[T,U]]): List[Map[T,U]] = {
if (values.isEmpty) sofar.reverse
else {
val m = keys.zip(values.take(len)).foldLeft(Map[T,U]()){(m, p) => m + p}
construct(values.drop(len), m::sofar)
}
}
construct(values, List())
}
試してみます。
scala> %('name, 'age, 'email) ( | "Foo", "20", "foo@mail.com", | "Bar", "21", "bar@mail.net", | "Baz", "22", "baz@mail.org" | ) res31: List[Map[Symbol,java.lang.String]] = List(Map('name -> Foo, 'age -> 20, ' email -> foo@mail.com), Map('name -> Bar, 'age -> 21, 'email -> bar@mail.net), M ap('name -> Baz, 'age -> 22, 'email -> baz@mail.org))
コメント 0