Scalaのリスト-概要と構築方法-

概要
配列のような構造物。List(x1, x2,...xn)というように使うことができる。…良く見てみると、new List() じゃなく、List() なのね。applyを使ってるのだろうか?ソース見てみると…
object List {

def apply[A](xs: A*): List[A] = xs.toList
ビンゴっぽいけど、よくわからない点がいくつか。まず、Listはclassじゃなくobject?まじか?と思ったら、下の方には…
sealed abstract class List[+A] extends Seq[A] with Product
とclass Listの定義があり、ここにListのメソッド群が定義されていた。object Listのクラスコメントを見るとわかるのだが、object Listはclass Listのインスタンスを生成するためのFactoryのようなもので、記法上のわかりやすさからclass Listと同じ名前になっている(が、継承関係はまったくない)ようだ。なんかすごいな。こんなのJavaではお目にかかったことがないぞ。
もう1つのわからない点は、xs: A*の「*」。これは後で説明する。

Listクラス階層
Listクラスには、「Nil」と「::」の2つのサブクラスしかなく、このいずれもがcaseクラスとなっている。さらに、Nilはシングルトンで良いため、実際にはobjectとして定義されている。

Listの特徴
Listのクラス定義でわかるとおり、ListはHomogenious。つまり、List内の要素は同じ型でないといけない。さらに、Listは共変。Listの配列との大きな違いは以下の3つらしい。
  • List内の要素は、割り当てによって変更できない
  • 再帰的な構造をとることができる
  • Listを操作する機能が豊富
Listの構築
さて、あらためてListの構築。以下の方法が主なものらしい。
  • List()を使う
  • :: Nilを使う
1番目の方法は、Listオブジェクトのapply()を利用するもので、先ほども少し触れたのだが、
  def apply[A](xs: A*): List[A] = xs.toList
この部分で実現されている。なぜxsに対してtoListが呼び出せるのかはよくわからない。A*が何か関係してるのだろうか。わからんわからん言っててもしょうがないので、Language Specificationで調べてみたところ、これはRepeated Parameterといって、繰り返しを表すものらしい。で、この型はscala.Seq[+A]になるようだ。でも、SeqにはtoListなんてないんだけどどうなってんだ?検索してみると、ListBufferがtoListを持っているようだ。xsは結局ListBufferになっているんだろうか?謎は深まるばかりなり。

2番目の方法は、:: 演算子を使う方法。といってもこれはListクラスのメソッドで、「:: Nil」はNilオブジェクトに対して::メソッドを呼び出す形になる。::メソッドはというと…
  def ::[B >: A] (x: B): List[B] =
new scala.::(x, this)
こうなっている。一見するとわけがわからないが、ここでの「::」はメソッドではなくListのサブクラスの方。で、コンストラクタパラメータとしてxとthisを渡して::インスタンスを作っているわけだ。よくこんなことするよ、ほんと。

Scalaでは、演算子はすべてメソッドとして実装されており、実際はメソッド呼び出しに展開される。この中でも「:」で終了する演算子(メソッド)は特別な扱いとなり、これは右結合の演算子として扱われる。
にもかかわらず、評価は左から順に行われることになっているため、D :: Eは{val x = D; E.::(x)}に変換されるらしい。単純にE.::(D)に変換しちゃうと、Eから先に評価されちゃうからねー。

ここまでわかれば、Listの構築に関しては完璧だろう!たぶん!Listは長いので、いったんここで終了。各種操作は次回エントリにまわします。なんかHaskellの復習してるみたいでいまいちつまんないんだけどね…。

コメント