diff --git a/ja/overviews/collections/arrays.md b/ja/overviews/collections/arrays.md new file mode 100644 index 0000000000..110bd87ebc --- /dev/null +++ b/ja/overviews/collections/arrays.md @@ -0,0 +1,122 @@ +--- +layout: overview-large +title: 配列 + +disqus: true + +partof: collections +num: 10 +language: ja +--- + +配列 ([`Array`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/Array.html)) は Scala のコレクションの中でも特殊なものだ。Scala 配列は、Java の配列と一対一で対応する。どういう事かと言うと、Scala の配列 `Array[Int]` は Java の `int[]` で実装されており、`Array[Double]` は Java の `double[]`、`Array[String]` は Java の `String[]` で実装されている。その一方で、Scala の配列は Java のそれに比べて多くの機能を提供する。まず、Scala の配列は**ジェネリック**であることができる。 つまり、型パラメータか抽象型の `T` に対する `Array[T]` を定義することができる。次に、Scala の配列は Scala の列と互換性があり、`Seq[T]` が期待されている所に `Array[T]` を渡すことができる。さらに、Scala の配列は列の演算の全てをサポートする。以下に具体例で説明する: + + scala> val a1 = Array(1, 2, 3) + a1: Array[Int] = Array(1, 2, 3) + scala> val a2 = a1 map (_ * 3) + a2: Array[Int] = Array(3, 6, 9) + scala> val a3 = a2 filter (_ % 2 != 0) + a3: Array[Int] = Array(3, 9) + scala> a3.reverse + res1: Array[Int] = Array(9, 3) + +Scala の配列は Java の配列で実装されているのに、どのようにして新たな機能をサポートしてるのだろうか。実は、Scala 2.8 とその前のバージョンではその問に対する答が変わってくる。以前は Scala のコンパイラが、ボックス化 (boxing) とボックス化解除 (unboxing) と呼ばれる「魔法」により配列と `Seq` オブジェクトの間を変換していた。この詳細は、特にジェネリック型の `Array[T]` が作成された場合、非常に複雑なものとなる。不可解な特殊ケースなどもあり、配列演算の性能特性は予想不可能なものとなった。 + +Scala 2.8 の設計はより単純なものだ。ほぼ全てのコンパイラの魔法は無くなった。代わりに、Scala 2.8 配列の実装は全体的に暗黙の変換 (implicit conversion) を利用する。Scala 2.8 では、配列はあたかも列であるようなふりをしない。 そもそもネイティブな配列のデータ型の実装は `Seq` の子クラスではないため、それは不可能というものだ。代わりに、配列を `Seq` の子クラスである `scala.collection.mutable.WrappedArray` クラスで「ラッピング」する暗黙の変換が行われる。以下に具体例で説明する: + + scala> val seq: Seq[Int] = a1 + seq: Seq[Int] = WrappedArray(1, 2, 3) + scala> val a4: Array[Int] = s.toArray + a4: Array[Int] = Array(1, 2, 3) + scala> a1 eq a4 + res2: Boolean = true + +上記のやりとりは、配列から `WrappedArray` への暗黙の変換があるため、配列と列に互換性があることを示す。`WrappedArray` から `Array` へ逆の方向に変換するには、`Traversable` に定義されている `toArray` メソッドを使うことで実現できる。上記の REPL の最後の行は、ラッピングした後、`toArray` でそれを解除したときに、同一の配列が得られることを示す。 + +配列に適用されるもう一つの暗黙の変換がある。この変換は単に列メソッドの全てを配列に「追加」するだけで、配列自身を列には変換しない。この「追加」は、配列が全ての列メソッドをサポートする `ArrayOps` 型のオブジェクトにラッピングされることを意味している。典型的には、この `ArrayOps` は短命で、列メソッドを呼び出し終えた後にはアクセス不可能となり、そのメモリ領域はリサイクルされる。現代的な仮想機械 (VM) は、しばしばこのようなオブジェクトの生成そのものを省略できる。 + +以下の REPL のやりとりで、二つの暗黙の変換の違いを示す: + + scala> val seq: Seq[Int] = a1 + seq: Seq[Int] = WrappedArray(1, 2, 3) + scala> seq.reverse + res2: Seq[Int] = WrappedArray(3, 2, 1) + scala> val ops: collection.mutable.ArrayOps[Int] = a1 + ops: scala.collection.mutable.ArrayOps[Int] = [I(1, 2, 3) + scala> ops.reverse + res3: Array[Int] = Array(3, 2, 1) + +実際には `WrappedArray` である `seq` に対して `reverse` を呼ぶと再び `WrappedArray` が返っているのが分かる。`WrappedArray` は `Seq` であり、`Seq` に対して `reverse` を呼ぶと再び `Seq` が返るため、この結果は論理的だ。一方、`ArrayOps` クラスの値 `ops` に対して `reverse` を呼ぶと、`Seq` ではなく、`Array` が返る。 + +上記の `ArrayOps` の例は。`WrappedArray` との違いを示すためだけのかなり恣意的な物で、通常は `ArrayOps` クラスの値を定義することはありえない。単に配列に対して `Seq` メソッドを呼び出すだけでいい: + + scala> a1.reverse + res4: Array[Int] = Array(3, 2, 1) + +`ArrayOps` オブジェクトは暗黙の変換により自動的に導入されるからだ。よって、上の一行は以下に等しく、 + + scala> intArrayOps(a1).reverse + res5: Array[Int] = Array(3, 2, 1) + +`intArrayOps` が先の例で暗黙の変換により導入されたものだ。ここで問題となるのが、先の例でコンパイラがどうやってもう一つの暗黙の変換である `WrappedArray` に対して `intArrayOps` を優先させたのかということだ。結局の所、両方の変換も配列をインプットが指定した `reverse` メソッドをサポートする型へ変換するものだ。二つの暗黙の変換には優先順序が付けられているというのがこの問への答だ。`ArrayOps` 変換には、`WrappedArray` 変換よりも高い優先順位が与えられている。`ArrayOps` 変換は `Predef` オブジェクトで定義されているのに対し、`WrappedArray` 変換は +`Predef` が継承する `scala.LowPritoryImplicits` で定義されている。子クラスや子オブジェクトで定義される暗黙の変換は、親クラスで定義される暗黙の変換に対して優先される。よって、両方の変換が適用可能な場合は、`Predef` +で定義されるものが選ばれる。文字列まわりにも似た仕組みがある。 + +これで、配列において列との互換性および全ての列メソッドのサポートを実現しているかが分かったと思う。ジェネリック性についてはどうだろう。Java では型パラメータ `T` に対して `T[]` と書くことはできない。では、Scala の `Array[T]` はどのように実装されるのだろう。`Array[T]` のようなジェネリックな配列は実行時においては、Java の 8つあるプリミティブ型の +`byte[]`、`short[]`、`char[]`、`int[]`、`long[]`、`float[]`、`double[]`、`boolean[]` のどれか、もしくはオブジェクトの配列である可能性がある。 これらの型に共通の実行時の型は `AnyRef` (もしくは、それと等価な `java.lang.Object`) であるので、Scala のコンパイラは `Array[T]` を `AnyRef` にマップする。実行時に、型が `Array[T]` である配列の要素が読み込まれたり、更新された場合、実際の配列型を決定するための型判定手順があり、その後 Java 配列に対して正しい型演算が実行される。この型判定は配列演算を多少遅くする。ジェネリックな配列へのアクセスはプリミティブ型やオブジェクトの配列に比べて 3〜4倍遅いと思っていい。つまり、最高の性能を必要とするなら、ジェネリック配列ではなく具象配列を使ったほうがいいことを意味する。いくらジェネリック配列を実装しても、それを**作成する**方法がなければ意味が無い。これは更に難しい問題で、あなたにも少し手を借りる必要がある。この問題を説明するのに、配列を作成するジェネリックなメソッドの失敗例を見てほしい。 + + // これは間違っている! + def evenElems[T](xs: Vector[T]): Array[T] = { + val arr = new Array[T]((xs.length + 1) / 2) + for (i <- 0 until xs.length by 2) + arr(i / 2) = xs(i) + arr + } + +`evenElems` メソッドは、引数のベクトル `xs`内の偶数位置にある全ての要素から成る新しい配列を返す。`evenElems` の本文一行目にて、引数と同じ型を持つ戻り値の配列が定義されている。そのため、実際の型パラメータ `T` の実際の型により、これは `Array[Int]`、`Array[Boolean]`、もしくはその他の Java のプリミティブ型の配列か、参照型の配列であるかもしれない。これらの型は実行時に異なる実装を持つため、Scala ランタイムはどのようにして正しいものを選択するのだろう。実際のところ、型パラメータ `T` に対応する実際の型は実行時に消去されてしまうため、与えられた情報だけでは選択することができない。そのため、上記のコードをコンパイルしようとすると以下のエラーが発生する: + + error: cannot find class manifest for element type T + val arr = new Array[T]((arr.length + 1) / 2) + ^ + +あなたがコンパイラを手伝ってあげて、`evenElems` の型パタメータの実際の型が何であるかの実行時のヒントを提供することが必要とされている。この実行時のヒントは `scala.reflect.ClassManifest` +型の**クラスマニフェスト**という形をとる。クラスマニフェストとは、型の最上位クラスが何であるかを記述する型記述オブジェクトだ。型に関するあらゆる事を記述する `scala.reflect.Manifest` 型の完全マニフェストというものもある。配列の作成にはクラスマニフェストで十分だ。 + +Scala コンパイラは、指示を出すだけでクラスマニフェストを自動的に構築する。「指示を出す」とは、クラスマニフェストを以下のように暗黙のパラメータ (implicit parameter) として要求することを意味する: + + def evenElems[T](xs: Vector[T])(implicit m: ClassManifest[T]): Array[T] = ... + +**context bound** という、より短い別の構文を使うことで型がクラスマニフェストを連れてくることを要求できる。これは、型の後にコロン (:) とクラス名 `ClassManifest` を付けることを意味する: + + // これは動作する + def evenElems[T: ClassManifest](xs: Vector[T]): Array[T] = { + val arr = new Array[T]((xs.length + 1) / 2) + for (i <- 0 until xs.length by 2) + arr(i / 2) = xs(i) + arr + } + +2つの `evenElems` の改訂版は全く同じことを意味する。どちらの場合も、`Array[T]` が構築されるときにコンパイラは型パラメータ `T` のクラスマニフェスト、つまり `ClassManifest[T]` 型の暗黙の値 (implicit value)、を検索する。暗黙の値が見つかれば、正しい種類の配列を構築するのにマニフェストが使用される。見つからなければ、その前の例のようにエラーが発生する。 + +以下に `evenElems` を使った REPL のやりとりを示す。 + + scala> evenElems(Vector(1, 2, 3, 4, 5)) + res6: Array[Int] = Array(1, 3, 5) + scala> evenElems(Vector("this", "is", "a", "test", "run")) + res7: Array[java.lang.String] = Array(this, a, run) + +両者の場合とも、Scala コンパイラは要素型 (`Int`、そして `String`) のクラスマニフェストを自動的に構築して、`evenElems` メソッドの暗黙のパラメータに渡した。コンパイラは全ての具象型についてクラスマニフェストを構築できるが、引数そのものがクラスマニフェストを持たない型パラメータである場合はそれができない。以下に失敗例を示す: + + scala> def wrap[U](xs: Array[U]) = evenElems(xs) + :6: error: could not find implicit value for + evidence parameter of type ClassManifest[U] + def wrap[U](xs: Array[U]) = evenElems(xs) + ^ +何が起こったかというと、`evenElems` は型パラメータ `U` に関するクラスマニフェストを要求するが、見つからなかったのだ。当然この場合は、`U` に関する暗黙のクラスマニフェストを要求することで解決するため、以下は成功する: + + scala> def wrap[U: ClassManifest](xs: Array[U]) = evenElems(xs) + wrap: [U](xs: Array[U])(implicit evidence$1: ClassManifest[U])Array[U] + +この例から、`U` の定義の context bound 構文は `evidence$1` と呼ばれる `ClassManifest[U]` 型の暗黙のパラメータの略記法であることが分かる。 + +要約すると、ジェネリックな配列の作成はクラスマニフェストを必要とする。型パラメータ `T` の配列を作成する場合、`T` に関する暗黙のクラスマニフェストも提供する必要がある。その最も簡単な方法は、`[T: ClassManifest]` のように、型パラメータを context bound 構文で `ClassManifest` と共に定義することだ。 diff --git a/ja/overviews/collections/concrete-immutable-collection-classes.md b/ja/overviews/collections/concrete-immutable-collection-classes.md new file mode 100644 index 0000000000..8a75093641 --- /dev/null +++ b/ja/overviews/collections/concrete-immutable-collection-classes.md @@ -0,0 +1,195 @@ +--- +layout: overview-large +title: 具象不変コレクションクラス + +disqus: true + +partof: collections +num: 8 +language: ja +--- + +Scala は様々な具象不変コレクションクラス (concrete immutable collection class) を提供する。これらはどのトレイトを実装するか(マップ、集合、列)、無限を扱えるか、様々な演算の速さなどの違いがある。ここに、Scala で最もよく使われる不変コレクション型を並べる。 + +## リスト + +リスト ([`List`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/List.html)) は有限の不変列だ。リストは最初の要素とリストの残りの部分に定数時間でアクセスでき、また、新たな要素をリストの先頭に追加する定数時間の cons 演算を持つ。他の多くの演算は線形時間で行われる。 + +リストは Scala プログラミングの働き者であり続けてきたので、あえてここで語るべきことは多くない。Scala 2.8 での大きな変更点は `List` クラスはそのサブクラスである `::` とそのサブオブジェクトである `Nil` とともに、論理的にふさわしい `scala.collection.immutable` パッケージで定義されるようになったことだ。`scala` パッケージには `List`、`Nil`、および `::` へのエイリアスがあるため、ユーザの立場から見ると、リストは今まで通り使うことができる。 + +もう一つの変更点は、リストは以前のような特殊扱いではなく、コレクションフレームワークにより緊密に統合されたことだ。例えば、`List` のコンパニオンオブジェクトにあった多くのメソッドは廃止予定になった。代わりに、それらは全てのコレクションが継承する[共通作成メソッド](creating-collections-from-scratch.html)に取って代わられた。 + +## ストリーム + +ストリーム ([`Stream`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Stream.html)) はリストに似ているが、要素は遅延評価される。そのため、ストリームは無限の長さをもつことができる。呼び出された要素のみが計算される。他の点においては、ストリームはリストと同じ性能特性をもつ。 + +リストは `::` 演算子によって構築されるが、ストリームはそれに似た `#::` 演算子によって構築される。以下は、整数の 1, 2, 3 からなる簡単なストリームの例だ: + + scala> val str = 1 #:: 2 #:: 3 #:: Stream.empty + str: scala.collection.immutable.Stream[Int] = Stream(1, ?) + +このストリームの head は 1 で、tail は 2 と 3 だ。上の例では tail が表示されていないが、それはまだ計算されていないからだ。ストリームは遅延評価されるため、`toString` は余計な評価を強いないように慎重に設計されているのだ。 + +以下に、もう少し複雑な例を示す。任意の二つの数から始まるフィボナッチ数列を計算するストリームだ。フィボナッチ数列とは、それぞれの要素がその前二つの要素の和である数列のことだ。 + + scala> def fibFrom(a: Int, b: Int): Stream[Int] = a #:: fibFrom(b, a + b) + fibFrom: (a: Int,b: Int)Stream[Int] + +この関数は嘘のように単純だ。数列の最初の要素は明らかに `a` で、残りは `b` そして `a + b` から始まるフィボナッチ数列だ。無限の再帰呼び出しに陥らずにこの数列を計算するのが難しい所だ。もしこの関数が `#::` の代わりに `::` を使っていたなら、全ての呼び出しはまた別の呼び出しを招くため、無限の再帰呼び出しに陥ってしまう。しかし、`#::` +を使っているため、右辺は呼び出されるまでは評価されないのだ。 + +2つの 1 から始まるフィボナッチ数列の最初の数要素を以下に示す: + + scala> val fibs = fibFrom(1, 1).take(7) + fibs: scala.collection.immutable.Stream[Int] = Stream(1, ?) + scala> fibs.toList + res9: List[Int] = List(1, 1, 2, 3, 5, 8, 11) + +## ベクトル + +リストはアルゴリズムが慎重にリストの先頭要素 (`head`) のみを処理する場合、非常に効率的だ。`head` の読み込み、追加、および削除は一定数時間で行われるのに対して、リストの後続の要素に対する読み込みや変更は、その要素の深さに依存した線形時間で実行される。 + +ベクトル ([`Vector`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Vector.html)) は、ランダムアクセス時の非効率性を解決するために Scala 2.8 から導入された新しいコレクション型だ。ベクトルはどの要素の読み込みも「事実上」定数時間で行う。リストの `head` の読み込みや配列の要素読み込みに比べると大きい定数だが、定数であることには変りない。この結果、ベクトルを使ったアルゴリズムは列の `head` のみを読み込むことに神経質にならなくていい。任意の場所の要素を読み込んだり、変更したりできるため、コードを書くのに便利だ。 + +ベクトルは、他の列と同じように作成され、変更される。 + + scala> val vec = scala.collection.immutable.Vector.empty + vec: scala.collection.immutable.Vector[Nothing] = Vector() + scala> val vec2 = vec :+ 1 :+ 2 + vec2: scala.collection.immutable.Vector[Int] = Vector(1, 2) + scala> val vec3 = 100 +: vec2 + vec3: scala.collection.immutable.Vector[Int] = Vector(100, 1, 2) + scala> vec3(0) + res1: Int = 100 + +ベクトルは分岐度の高い木構造で表される。全てのノードは32以下の要素か、32以下の他のノードを格納する。32個以下の要素を持つベクトルは単一のノードで表すことができる。ベクトルは、たった一つの間接呼び出しで、`32 * 32 = 1024`個までの要素を扱うことができる。木構造の根ノードから末端ノードまで 2ホップで 215個、3ホップで 220個、4ホップで +230個以下までの要素をベクトルは扱うことができる。よって、普通のサイズのベクトルの要素選択は 5回以内の配列選択で行うことができる。要素選択が「事実上定数時間」と言ったのは、こういうことだ。 + +ベクトルは不変であるため、ベクトルの変更無しにベクトル内の要素を変更することはできない。しかし、`updated` メソッドを使うことで一つの要素違いの新たなベクトルを作成することができる: + + scala> val vec = Vector(1, 2, 3) + vec: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3) + scala> vec updated (2, 4) + res0: scala.collection.immutable.Vector[Int] = Vector(1, 2, 4) + scala> vec + res1: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3) + +最後の行が示すように、`updated` の呼び出しは元のベクトル `vec` には一切影響しない。読み込みと同様に、ベクトルの関数型更新も「事実上定数時間」で実行される。ベクトルの真ん中にある要素を更新するには、その要素を格納するノードと、木構造の根ノードからを初めとする全ての親ノードをコピーすることによって行われる。これは関数型更新は、32以内の要素か部分木を格納する 1 〜 5個の ノードを作成することを意味する。これは、可変配列の in-place での上書きに比べると、ずっと時間のかかる計算であるが、ベクトル全体をコピーするよりはずっと安いものだ。 + +ベクトルは高速なランダム読み込みと高速な関数型更新の丁度いいバランスを取れているため、不変添字付き列 ([`immutable.IndexedSeq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/IndexedSeq.html)) トレイトのデフォルトの実装となっている: + + scala> collection.immutable.IndexedSeq(1, 2, 3) + res2: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3) + +## 不変スタック + +後入れ先出し (LIFO: last in first out) の列が必要ならば、スタック ([`Stack`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Stack.html)) がある。 `push` メソッドを使ってスタックに要素をプッシュ、`pop` を使ってポップ、そして`top` を使って削除することなく一番上の要素を読み込むことができる。これらの演算は、全て定数時間で行われる。 + +以下はスタックに対して行われる簡単な演算の例だ: + + scala> val stack = scala.collection.immutable.Stack.empty + stack: scala.collection.immutable.Stack[Nothing] = Stack() + scala> val hasOne = stack.push(1) + hasOne: scala.collection.immutable.Stack[Int] = Stack(1) + scala> stack + stack: scala.collection.immutable.Stack[Nothing] = Stack() + scala> hasOne.top + res20: Int = 1 + scala> hasOne.pop + res19: scala.collection.immutable.Stack[Int] = Stack() + +機能的にリストとかぶるため、不変スタックが Scala のプログラムで使われることは稀だ: 不変スタックの `push` はリストの `::` と同じで、`pop` はリストの `tail` と同じだ。 + +## 不変キュー + +キュー ([`Queue`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Queue.html)) はスタックに似ているが、後入れ先出し (LIFO: last in first out) ではなく、先入れ先出し (FIFO: +first in first out) だ。 + +以下に空の不変キューの作り方を示す: + + scala> val empty = scala.collection.immutable.Queue[Int]() + empty: scala.collection.immutable.Queue[Int] = Queue() + +`enqueue` を使って不変キューに要素を追加することができる: + + scala> val has1 = empty.enqueue(1) + has1: scala.collection.immutable.Queue[Int] = Queue(1) + +複数の要素をキューに追加するには、enqueue の引数にコレクションを渡す: + + scala> val has123 = has1.enqueue(List(2, 3)) + has123: scala.collection.immutable.Queue[Int] + = Queue(1, 2, 3) + +キューの先頭から要素を削除するには、`dequeue` を使う: + + scala> val (element, has23) = has123.dequeue + element: Int = 1 + has23: scala.collection.immutable.Queue[Int] = Queue(2, 3) + +`dequeue` は削除された要素と残りのキューのペアを返すことに注意してほしい。 + +## 範囲 + +範囲 ([`Range`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Range.html)) は順序付けされた等間隔の整数の列だ。例えば、「1、2、3」は範囲であり、「5、8、11、14」も範囲だ。Scala で範囲を作成するには、予め定義された `to` メソッドと `by` メソッドを使う。 + + scala> 1 to 3 + res2: scala.collection.immutable.Range.Inclusive + with scala.collection.immutable.Range.ByOne = Range(1, 2, 3) + scala> 5 to 14 by 3 + res3: scala.collection.immutable.Range = Range(5, 8, 11, 14) + +上限を含まない範囲を作成したい場合は、`to` の代わりに、便宜上用意された `until` メソッドを使う: + + scala> 1 until 3 + res2: scala.collection.immutable.Range.Inclusive + with scala.collection.immutable.Range.ByOne = Range(1, 2) + +範囲は、開始値、終了値、ステップ値という、たった三つの数で定義できため定数空間で表すことができる。そのため、範囲の多くの演算は非常に高速だ。 + +## ハッシュトライ + +ハッシュトライは不変集合と不変マップを効率的に実装する標準的な方法だ。ハッシュトライは、[`immutable.HashMap`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/HashMap.html) クラスによりサポートされている。データ構造は、全てのノードに 32個の要素か 32個の部分木があるという意味でベクトルに似ている。しかし、キーの選択はハッシュコードにより行われる。たとえば、マップから任意のキーを検索する場合、まずキーのハッシュコードを計算する。その後、最初の部分木を選択するのにハッシュコードの下位 5ビットが使われ、次の 5ビットで次の部分木が選択される、という具合だ。ノード内の全ての要素が、その階層までで選ばれているビット範囲内でお互いと異なるハッシュコードを持った時点で選択は終了する。 + +ハッシュトライは、サイズ相応の高速な検索と、相応に効率的な関数型加算 `(+)` と減算 `(-)` の調度良いバランスが取れている。そのため、ハッシュトライは Scala の不変マップと不変集合のデフォルトの実装を支えている。実は、Scala は要素が 5個未満の不変集合と不変マップに関して、更なる最適化をしている。1 〜 4個の要素を持つ集合とセットは、要素 (マップの場合は、キー/値のペア) をフィールドとして持つ単一のオブジェクトとして格納する。空の不変集合と、空の不変マップは、ぞれぞれ単一のオブジェクトである。空の不変集合や不変マップは、空であり続けるため、データ構造を複製する必要はない。 + +## 赤黒木 + +赤黒木は、ノードが「赤」か「黒」に色付けされている平衡二分木の一種だ。他の平衡二分木と同様に演算は木のサイズのログ時間内に確実に完了する。 + +Scala は内部で赤黒木を使った不変集合と不変セットの実装を提供する。[`TreeSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/TreeSet.html) と [`TreeMap`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/TreeMap.html) クラスがそれだ。 + + scala> scala.collection.immutable.TreeSet.empty[Int] + res11: scala.collection.immutable.TreeSet[Int] = TreeSet() + scala> res11 + 1 + 3 + 3 + res12: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 3) + +赤黒木は、全ての要素をソートされた順序で返す効率的なイテレータを提供するため、整列済み集合 (`SortedSet`) +の標準実装となっている。 + +## 不変ビット集合 + +ビット集合 ([`BitSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/BitSet.html)) は大きい整数のビットを小さな整数のコレクションを使って表す。例えば、3, 2, と 0 を格納するビット集合は二進法で整数の 1101、十進法で 13 を表す。 + +内部では、ビット集合は 64ビットの `Long` の配列を使っている。配列の最初の `Long` は 整数の 0〜63、二番目は 64〜127 という具合だ。そのため、ビット集合は最大値が数百以下の場合は非常にコンパクトだ。 + +ビット集合の演算はとても高速だ。所属判定は一定数時間で行われる。集合への要素の追加は、ビット集合の配列内の `Long` の数に比例するが、普通は小さい数だ。以下にビット集合の使用例を示す: + + scala> val bits = scala.collection.immutable.BitSet.empty + bits: scala.collection.immutable.BitSet = BitSet() + scala> val moreBits = bits + 3 + 4 + 4 + moreBits: scala.collection.immutable.BitSet = BitSet(3, 4) + scala> moreBits(3) + res26: Boolean = true + scala> moreBits(0) + res27: Boolean = false + +## リストマップ + +リストマップ ([`ListMap`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/ListMap.html)) は、キー/値ペアの連結リスト (linked list) により実装されたマップを表す。一般的に、リストマップの演算はリスト全体を総なめする必要がある可能性がある。そのため、リストマップの演算はマップのサイズに対して線形時間をとる。標準の不変マップの方が常に高速なので Scala のリストマップが使われることはほとんど無い。唯一性能の差が出る可能性としては、マップが何らかの理由でリストの最初の要素が他の要素に比べてずっと頻繁に読み込まれるように構築された場合だ。 + + scala> val map = scala.collection.immutable.ListMap(1->"one", 2->"two") + map: scala.collection.immutable.ListMap[Int,java.lang.String] = + Map(1 -> one, 2 -> two) + scala> map(2) + res30: String = "two" diff --git a/ja/overviews/collections/concrete-mutable-collection-classes.md b/ja/overviews/collections/concrete-mutable-collection-classes.md new file mode 100644 index 0000000000..a873212233 --- /dev/null +++ b/ja/overviews/collections/concrete-mutable-collection-classes.md @@ -0,0 +1,165 @@ +--- +layout: overview-large +title: 具象可変コレクションクラス + +disqus: true + +partof: collections +num: 9 +language: ja +--- + +Scala が標準ライブラリで提供する不変コレクションで最もよく使われるものをこれまで見てきた。続いて、具象可変コレクションクラス (concrete mutable collection class) を見ていく。 + +## 配列バッファ + +配列バッファ ([`ArrayBuffer`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/ArrayBuffer.html)) は、配列とサイズを格納するバッファだ。配列バッファの演算のほとんどは、単に内部の配列にアクセスして変更するだけなので、配列の演算と同じ速さで実行される。また、配列バッファは効率的にデータをバッファの最後に追加できる。配列バッファへの要素の追加はならし定数時間 (amortized constant time) で実行される。よって、配列バッファは要素が常に最後に追加される場合、大きいコレクションを効率的に構築するのに便利だ。 + + scala> val buf = scala.collection.mutable.ArrayBuffer.empty[Int] + buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer() + scala> buf += 1 + res32: buf.type = ArrayBuffer(1) + scala> buf += 10 + res33: buf.type = ArrayBuffer(1, 10) + scala> buf.toArray + res34: Array[Int] = Array(1, 10) + +## リストバッファ + +リストバッファ ([`ListBuffer`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/ListBuffer.html)) は、内部に配列の代わりに連結リストを使うこと以外は配列バッファに似ている。バッファを構築後にリストに変換する予定なら、配列バッファの代わりにリストバッファを使うべきだ。 + + scala> val buf = scala.collection.mutable.ListBuffer.empty[Int] + buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer() + scala> buf += 1 + res35: buf.type = ListBuffer(1) + scala> buf += 10 + res36: buf.type = ListBuffer(1, 10) + scala> buf.toList + res37: List[Int] = List(1, 10) + +## 文字列ビルダ + +配列バッファが配列を構築するのに便利で、リストバッファがリストを構築するのに便利なように、文字列ビルダ ([`StringBuilder`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/StringBuilder.html)) は文字列を構築するのに便利なものだ。文字列ビルダはあまりに頻繁に使われるため、デフォルトの名前空間に既にインポートされている。以下のように、単に `new StringBuilder` で文字列ビルダを作成することができる: + + scala> val buf = new StringBuilder + buf: StringBuilder = + scala> buf += 'a' + res38: buf.type = a + scala> buf ++= "bcdef" + res39: buf.type = abcdef + scala> buf.toString + res41: String = abcdef + +## 連結リスト + +連結リスト ([`LinkedList`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/LinkedList.html)) は、`next` ポインタにより連結されたノードから成る可変列だ。多くの言語では空の連結リストを表すのに `null` が選ばれるが、空の列も全ての列演算をサポートする必要があるため、Scala のコレクションでは `null` は都合が悪い。特に、`LinkedList.empty.isEmpty` は `true` を返すべきで、`NullPointerException` を発生させるべきではない。 代わりに、空の連結リストは `next` フィールドがノード自身を指すという特殊な方法で表現されている。不変リスト同様、連結リストは順列通りに探索するのに適している。また、連結リストは要素や他の連結リストを簡単に挿入できるように実装されている。 + +## 双方向リスト + +双方向リスト ([`DoubleLinkedList`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/DoubleLinkedList.html)) は、`next` の他に、現ノードの一つ前の要素を指す `prev` +というもう一つの可変フィールドがあることを除けば、単方向の連結リストに似ている。リンクが一つ増えたことで要素の削除が非常に高速になる。 + +## 可変リスト + +可変リスト ([`MutableList`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/MutableList.html)) は単方向連結リストとリスト終端の空ノードを参照するポインタから構成される。これにより、リストの最後への要素の追加が、終端ノードのためにリスト全体を探索しなくてよくなるため、定数時間の演算となる。[`MutableList`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/MutableList.html) は、現在 Scala の [`mutable.LinearSeq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/LinearSeq.html) トレイトの標準実装だ。 + +## キュー +Scala は不変キューの他に可変キュー ([`mutable.Queue`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/Queue.html)) も提供する。可変キューは不変のものと同じように使えるが、`enqueue` の代わりに `+=` と `++=` 演算子を使って加算する。また、可変キューの `dequeue` は先頭の要素を削除して、それを返す。次に具体例で説明する: + + scala> val queue = new scala.collection.mutable.Queue[String] + queue: scala.collection.mutable.Queue[String] = Queue() + scala> queue += "a" + res10: queue.type = Queue(a) + scala> queue ++= List("b", "c") + res11: queue.type = Queue(a, b, c) + scala> queue + res12: scala.collection.mutable.Queue[String] = Queue(a, b, c) + scala> queue.dequeue + res13: String = a + scala> queue + res14: scala.collection.mutable.Queue[String] = Queue(b, c) + +## 配列シーケンス + +配列シーケンス ([`ArraySeq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/ArraySeq.html)) は、内部で要素を `Array[Object]` に格納する固定長の可変列だ。 + +典型的には、配列の性能特性が欲しいが、要素の型が特定できず、実行時に `ClassManifest` も無く、ジェネリックな列を作成したい場合に [`ArraySeq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/ArraySeq.html) を使う。この問題は後ほど[配列の節](arrays.html)で説明する。 + +## スタック + +既に不変スタックについては説明した。スタックには、[`mutable.Stack`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/Stack.html) クラスにより実装される可変バージョンもある。変更が上書き処理されるという違いの他は、不変バージョンと全く同じように動作する。 + + scala> val stack = new scala.collection.mutable.Stack[Int] + stack: scala.collection.mutable.Stack[Int] = Stack() + scala> stack.push(1) + res0: stack.type = Stack(1) + scala> stack + res1: scala.collection.mutable.Stack[Int] = Stack(1) + scala> stack.push(2) + res0: stack.type = Stack(1, 2) + scala> stack + res3: scala.collection.mutable.Stack[Int] = Stack(1, 2) + scala> stack.top + res8: Int = 2 + scala> stack + res9: scala.collection.mutable.Stack[Int] = Stack(1, 2) + scala> stack.pop + res10: Int = 2 + scala> stack + res11: scala.collection.mutable.Stack[Int] = Stack(1) + +## 配列スタック + +配列スタック ([`ArrayStack`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/ArrayStack.html) ) は、内部に必要に応じて大きさを変えた `Array` を使った、可変スタックの代替実装だ。配列スタックは、高速な添字読み込みを提供し、他の演算に関しても普通の可変スタックに比べて少し効率的だ。 + +## ハッシュテーブル + +ハッシュテーブルは、内部で要素を配列に格納し、要素の位置はハッシュコードにより決められる。ハッシュテーブルへの要素の追加は、既に配列の中に同じハッシュコードを持つ他の要素が無い限り、定数時間で行われる。そのため、ハッシュコードの分布が適度である限り非常に高速だ。結果として、Scala の可変マップと可変集合のデフォルトの実装はハッシュテーブルに基づいており、[`mutable.HashSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/HashSet.html) クラスと [`mutable.HashMap`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/HashMap.html) クラスから直接アクセスできる。 + +ハッシュ集合とハッシュマップは他の集合やマップと変りなく使うことができる。以下に具体例で説明する: + + scala> val map = scala.collection.mutable.HashMap.empty[Int,String] + map: scala.collection.mutable.HashMap[Int,String] = Map() + scala> map += (1 -> "make a web site") + res42: map.type = Map(1 -> make a web site) + scala> map += (3 -> "profit!") + res43: map.type = Map(1 -> make a web site, 3 -> profit!) + scala> map(1) + res44: String = make a web site + scala> map contains 2 + res46: Boolean = false + +ハッシュテーブルからの要素の取り出しは、特定の順序を保証していない。要素の取り出しは単に内部の配列を通して行われるため、順序はその時の配列の状態により決まる。要素の取り出しの順序を保証したい場合は、普通のハッシュマップではなく**連結**ハッシュマップを使うべきだ。連結ハッシュマップもしくは連結ハッシュ集合は、要素を追加した順序を保った連結リストを含む以外は普通のハッシュマップやハッシュ集合とほとんど同じだ。それにより、コレクションからの要素の取り出しは要素が追加された順序と常に一致する。 + +## ウィークハッシュマップ + +ウィークハッシュマップ([`WeakHashMap`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/WeakHashMap.html)) は、ガーベッジコレクタがマップから「弱キー」への参照を追跡しないという特殊なハッシュマップだ。他にキーを参照するものが無くなると、キーとその関連する値はマップから勝手にいなくなる。ウィークハッシュマップは、キーに対する時間のかかる計算結果を再利用するキャッシュのような用途に向いている。キーとその計算結果が普通のハッシュマップに格納された場合、そのマップは限りなく大きくなり続け、キーはガベージコレクタに永遠に回収されない。ウィークハッシュマップを使うことでこの問題を回避できる。キーオブジェクトが到達不可能になり次第、そのエントリーごとウィークハッシュマップから削除される。Scala のウィークハッシュマップは、Java による実装 `java.util.WeakHashMap` のラッパーである [`WeakHashMap`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/WeakHashMap.html) クラスにより実装されている。 + +## 並行マップ + +並行マップ ([`ConcurrentMap`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/ConcurrentMap.html)) は複数のスレッドから同時にアクセスすることできる。通常の[マップ](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Map.html)の演算の他に、以下のアトミックな演算を提供する: + +### ConcurrentMap クラスの演算 ### + +| 使用例 | 振る舞い| +| ------ | ------ | +| `m putIfAbsent(k, v)` |既に `k` がある場合を除き、キー/値ペア `k -> m` を `m` に追加する。| +| `m remove (k, v)` |`k` に関連付けられた値が `v` である場合、そのエントリを削除する。| +| `m replace (k, old, new)`|`k` に関連付けられた値が `old` である場合、それを `new` で上書きする。| +| `m replace (k, v)` |`k` に任意の関連付けられた値がある場合、それを `v` で上書きする。| + +[`ConcurrentMap`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/ConcurrentMap.html) は Scala コレクションライブラリ内のトレイトだ。現在そのトレイトを実装するのは Java の +`java.util.concurrent.ConcurrentMap` クラスだけで、それは [Java/Scala コレクションの標準変換](conversions-between-java-and-scala-collections.html)を使って Scala のマップに変換することができる。 + +## 可変ビット集合 + +可変ビット集合 ([`mutable.BitSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/BitSet.html)) は、変更が上書き処理される他は不変のものとほとんど同じだ。可変ビット集合は、変更しなかった `Long` をコピーしなくても済むぶん不変ビット集合に比べて少し効率的だ。 + + scala> val bits = scala.collection.mutable.BitSet.empty + bits: scala.collection.mutable.BitSet = BitSet() + scala> bits += 1 + res49: bits.type = BitSet(1) + scala> bits += 3 + res50: bits.type = BitSet(1, 3) + scala> bits + res51: scala.collection.mutable.BitSet = BitSet(1, 3) diff --git a/ja/overviews/collections/conversions-between-java-and-scala-collections.md b/ja/overviews/collections/conversions-between-java-and-scala-collections.md new file mode 100644 index 0000000000..2f2cefc179 --- /dev/null +++ b/ja/overviews/collections/conversions-between-java-and-scala-collections.md @@ -0,0 +1,59 @@ +--- +layout: overview-large +title: Java と Scala 間のコレクションの変換 + +disqus: true + +partof: collections +num: 17 +language: ja +--- + +Scala と同様に、Java +にも豊富なコレクションライブラリがある。両者には多くの共通点がある。例えば、両方のライブラリともイテレータ、`Iterable`、集合、マップ、そして列を提供する。しかし、両者には重要な違いもある。特に、Scala では不変コレクションに要点を置き、コレクションを別のものに変換する演算も多く提供している。 + +時として、コレクションを一方のフレームワークから他方へと渡す必要がある。例えば、既存の Java のコレクションを Scala のコレクションであるかのようにアクセスしたいこともあるだろう。もしくは、Scala のコレクションを Java のコレクションを期待している Java メソッドに渡したいと思うかもしれない。Scala は [`JavaConversions`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/JavaConversions$.html) オブジェクトにより主要なコレクション間の暗黙の変換 (implicit conversion) +を提供するため、簡単に相互運用できる。特に以下の型に関しては、双方向変換を提供する。 + + Iterator <=> java.util.Iterator + Iterator <=> java.util.Enumeration + Iterable <=> java.lang.Iterable + Iterable <=> java.util.Collection + mutable.Buffer <=> java.util.List + mutable.Set <=> java.util.Set + mutable.Map <=> java.util.Map + mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap + +このような変換を作動させるには、[`JavaConversions`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/JavaConversions$.html) オブジェクトからインポートするだけでいい: + + scala> import collection.JavaConversions._ + import collection.JavaConversions._ + +これで Scala コレクションとそれに対応する Java コレクションの自動変換が行われる。 + + scala> import collection.mutable._ + import collection.mutable._ + scala> val jul: java.util.List[Int] = ArrayBuffer(1, 2, 3) + jul: java.util.List[Int] = [1, 2, 3] + scala> val buf: Seq[Int] = jul + buf: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2, 3) + scala> val m: java.util.Map[String, Int] = HashMap("abc" -> 1, "hello" -> 2) + m: java.util.Map[String,Int] = {hello=2, abc=1} + +内部では、このような変換は全ての演算を委譲する「ラッパー」オブジェクトを作ることで実現されている。そのため、Java と Scala の間でコレクションを変換してもコレクションはコピーされることはない。興味深い特性として、例えば Java 型から対応する Scala 型に変換して再び Java 型に逆変換するといった、ラウンドトリップを実行した場合、始めた時と同一のオブジェクトが返ってくるというものがある。 + +Scala コレクションの中には、Java 型に変換できるが、逆変換はできないというものもある。それらは以下の通り: + + Seq => java.util.List + mutable.Seq => java.utl.List + Set => java.util.Set + Map => java.util.Map + +Java は可変コレクションと不変コレクションを型で区別しないため、例えば `scala.immutable.List` からの変換は、上書き演算を呼び出すと `UnsupportedOperationException` を発生する `java.util.List` を返す。次に具体例で説明する: + + scala> jul = List(1, 2, 3) + jul: java.util.List[Int] = [1, 2, 3] + scala> jul.add(7) + java.lang.UnsupportedOperationException + at java.util.AbstractList.add(AbstractList.java:131) + diff --git a/ja/overviews/collections/creating-collections-from-scratch.md b/ja/overviews/collections/creating-collections-from-scratch.md new file mode 100644 index 0000000000..0fae788631 --- /dev/null +++ b/ja/overviews/collections/creating-collections-from-scratch.md @@ -0,0 +1,58 @@ +--- +layout: overview-large +title: コレクションの作成 + +disqus: true + +partof: collections +num: 16 +language: ja +--- + +`List(1, 2, 3)` 構文によって 3つの整数から成るリストを作成でき、`Map('A' -> 1, 'C' -> 2)` 構文によって 2つの写像から成るマップを作成することができる。これは Scala コレクションの統一された機能だ。どのコレクションを取っても、その名前に括弧付けされた要素のリストを付け加えることができる。結果は渡された要素から成る新しいコレクションだ。以下に具体例で説明する: + + Traversable() // 空の traversable オブジェクト + List() // 空のリスト + List(1.0, 2.0) // 要素 1.0, 2.0 を含むリスト + Vector(1.0, 2.0) // 要素 1.0, 2.0 を含むベクトル + Iterator(1, 2, 3) // 3つの整数を返すイテレータ + Set(dog, cat, bird) // 3種類の動物の集合 + HashSet(dog, cat, bird) // 同じ動物のハッシュ集合 + Map(a -> 7, 'b' -> 0) // 文字から整数へのマップ + +上の全ての行での呼び出しは内部では何らかのオブジェクトの `apply` メソッドを呼び出している。例えば、3行目は以下のように展開する: + + List.apply(1.0, 2.0) + +つまり、これは `List` クラスのコンパニオンオブジェクトの `apply` メソッドを呼び出している。このメソッドは任意の数の引数を取り、それを使ってリストを構築する。Scala ライブラリの全てのコレクションクラスには、コンパニオンオブジェクトがあり、そのような `apply` メソッドを定義する。コレクションクラスが `List`、`Stream`、や `Vector` のような具象実装を表しているのか、`Seq`、`Set`、や `Traversable` のような抽象基底クラスを表しているのかは関係ない。後者の場合は、`apply` の呼び出しは抽象基底クラスの何らかのデフォルト実装を作成するだけのことだ。用例: + + scala> List(1, 2, 3) + res17: List[Int] = List(1, 2, 3) + scala> Traversable(1, 2, 3) + res18: Traversable[Int] = List(1, 2, 3) + scala> mutable.Traversable(1, 2, 3) + res19: scala.collection.mutable.Traversable[Int] = ArrayBuffer(1, 2, 3) + +`apply` とは別に、全てのコレクションのコンパニオンオブジェクトは、空のコレクションを返す `empty` を定義する。よって、`List()` の代わりに `List.empty` と書いたり、`Map()` の代わりに `Map.empty` と書くことができる。 + +`Seq` を継承するクラスは、コンパニオンオブジェクトにおいて他の factory 演算を提供する。以下の表にこれらの演算をまとめた。要約すると、 + +* `concat` は任意の数の traversable を連結する。 +* `fill` と `tabulate` は単次元か任意の多次元の列を生成して、なんらかの式かテーブル化関数によりその列を初期化する。 +* `range` は一定のステップ値で整数の列を生成する。 +* `iterate` は開始要素に連続して関数を適用することによって得られる列を生成する。 + +### 列の factory 演算 + +| 使用例 | 振る舞い| +| ------ | ------ | +| `S.empty` | 空の列。 | +| `S(x, y, z)` | 要素 `x`, `y`, `z` からなる列。 | +| `S.concat(xs, ys, zs)` | `xs`, `ys`, `zs` の要素を連結することによって得られる列。 | +| `S.fill(n){e}` | 全ての要素が式 `e` によって計算された長さ `n` の列。 | +| `S.fill(m, n){e}` | 全ての要素が式 `e` によって計算された `m × n` の大きさの列の列。(より高次元なものもある) | +| `S.tabulate(n){f}` | 添字 `i` の位置の要素が `f(i)` によって計算された長さ `n` の列。 | +| `S.tabulate(m, n){f}` | 添字 `(i, j)` の位置の要素が `f(i, j)` によって計算された `m×n` の大きさの列の列。(より高次元なものもある)| +| `S.range(start, end)` | `start` ... `end-1` の整数の列。 | +| `S.range(start, end, step)`| `start` より始まり `end` 未満まで `step` づつ増加する整数の列。 | +| `S.iterate(x, n)(f)` | 要素 `x`、`f(x)`、`f(f(x))`、… からなる長さ `n` の列。 | diff --git a/ja/overviews/collections/equality.md b/ja/overviews/collections/equality.md new file mode 100644 index 0000000000..1a26154866 --- /dev/null +++ b/ja/overviews/collections/equality.md @@ -0,0 +1,31 @@ +--- +layout: overview-large +title: 等価性 + +disqus: true + +partof: collections +num: 13 +language: ja +--- + +コレクションライブラリは等価性 (equality) とハッシング (hashing) に関して統一的な方法を取る。おおまかに言えば、まず、コレクションは集合、マップ、列の三種に大別される。別カテゴリのコレクションは常に不等だと判定される。例えば、`Set(1, 2, 3)` と `List(1, 2, 3)` は同じ要素を格納するが、不等だ。一方、同カテゴリ内ではコレクション内の要素が全く同じ要素から成る (列の場合は、同じ順序の同じ要素) 場合のみ等価だと判定される。例えば、`List(1, 2, 3) == Vector(1, 2, 3)` であり、`HashSet(1, 2) == TreeSet(2, 1)` だ。 + +コレクションが可変であるか不変であるかは、等価性の判定には関わらない。可変コレクションに関しては、等価性判定が実行された時点での要素の状態が用いられる。これは、可変コレクションが追加されたり削除されたりする要素によって、別のコレクションと等価であったり不等であったりすることを意味する。これはハッシュマップのキーとして可変コレクションを使用した場合、落とし穴となりうる。具体例としては: + + scala> import collection.mutable.{HashMap, ArrayBuffer} + import collection.mutable.{HashMap, ArrayBuffer} + scala> val buf = ArrayBuffer(1, 2, 3) + buf: scala.collection.mutable.ArrayBuffer[Int] = + ArrayBuffer(1, 2, 3) + scala> val map = HashMap(buf -> 3) + map: scala.collection.mutable.HashMap[scala.collection. + mutable.ArrayBuffer[Int],Int] = Map((ArrayBuffer(1, 2, 3),3)) + scala> map(buf) + res13: Int = 3 + scala> buf(0) += 1 + scala> map(buf) + java.util.NoSuchElementException: key not found: + ArrayBuffer(2, 2, 3) + +この例では、最後から二番目の行において配列 `xs` のハッシュコードが変わったため、最後の行の選択は恐らく失敗に終わる。ハッシュコードによる検索は `xs` が格納されていた元の位置とは別の場所を探しているからだ。 diff --git a/ja/overviews/collections/introduction.md b/ja/overviews/collections/introduction.md new file mode 100644 index 0000000000..3f74218f08 --- /dev/null +++ b/ja/overviews/collections/introduction.md @@ -0,0 +1,49 @@ +--- +layout: overview-large +title: はじめに + +disqus: true + +partof: collections +num: 1 +language: ja +--- + +**Martin Odersky, Lex Spoon 著**
+**Eugene Yokota 訳** + +Scala 2.8 の変更点で最も重要なものは新しいコレクションフレームワークだと多くの人が思っている。確かに以前の Scala にもコレクションはあったが、Scala 2.8 になって一貫性があり、包括的な、統一コレクション型のフレームワークを提供することができた。(大部分において新しいフレームワークは旧版と互換性がある) + +コレクションの変更点は、一見すると微細なものだがプログラミングスタイルに重大な変化をもたらすことができる。コレクション内の要素ではなくコレクション全体を、プログラムを組む時の基本的なパーツとすることで、あたかも一つ上の階層でプログラミングをしているかのように感じるだろう。この新しいスタイルには慣れを必要とするが、幸い新しいコレクションの特長のお陰で楽に適合できるようになっている。 +新しいコレクションは簡単に使えて、短く書けて、安全、高速で、統一性がある。 + +**簡単に使える:** 基本的なボキャブラリは、演算 (operation) とよばれる 20〜50 のメソッドから成る。これを身につけてしまえば、二つの演算を組み合わせるだけで、ほとんどのコレクションの問題を解決することができる。複雑なループ構造や再帰に頭を悩ませる必要はない。 +永続的なコレクションと副作用のない演算は、既存のコレクションを間違って新しいデータで壊してしまう心配をする必要がないことを意味する。イテレータとコレクションの更新の間の干渉は完全になくなった。 + +**短く書ける:** 一つまたは複数のループが必要だった作業を単語一つで実現することができる。関数型の演算もライトウェイトな構文で表現でき、簡単に演算を組み合わせることでカスタム代数を扱っているかのように感じるはずだ。 + +**安全である:** これは実際に経験してみないと分からないだろう。 +静的型付きでかつ関数型という Scala のコレクションの特徴は、可能なエラーの圧倒的多数はコンパイル時に捕捉されることを意味する。 +その理由は、 + +
    +
  1. コレクションの演算はが大量に使用されているため、十分にテスト済みである。
  2. +
  3. コレクション演算を使うことで、インプットとアウトプットが関数のパラメータと結果という形で明示的になる。
  4. +
  5. これらの明示的なインプットとアウトプットは静的な型チェックの対象だ。
  6. +
+ +結論としては、誤用の大多数が型エラーとして表出するということだ。数百行のプログラムが初回の一発で実行できることは決して稀ではない。 + +**速い:** コレクション演算はライブラリの中で調整され最適化されている。その結果、コレクションを使用することは一般的にかなり効率的だ。手作業で丁寧に調整されたデータ構造と演算により多少高速化することができるかもしれないが、不適切な実装上の決断を途中でしてしまうとかなり遅くなってしまうということもありえる。さらに、現在コレクションはマルチコア上での並列実行に適応されている途中だ。並列コレクションは順次コレクションと同じ演算をサポートするため、新しい演算を習ったりコードを書き変えたりする必要はない。`par` メソッドを呼ぶだけで順次コレクションを並列に変えることができる。 + +**統一性がある:** コレクションは出来る限りどの型にも同じ演算を提供している。これにより、少ないボキャブラリの演算でも多くのことができる。例えば、文字列は概念的には文字の列 (sequence) だ。その結果、Scalaのコレクションでは、文字列は列の演算の全てをサポートする。配列に関しても同様だ。 + +**用例:** これは Scala のコレクションの多くの利点を示す一行コードだ。 + + val (minors, adults) = people partition (_.age < 18) + +この演算が何をしているかは一目瞭然だ: `people` のコレクションを年齢によって `minors` と `adult` に分割している。`partition` メソッドはコレクションの基底型である `TraversableLike` で定義されているため、このコードは配列を含むどのコレクションでも動作する。これによって生じる `minors` と `adult` のコレクションは、`people`コレクションと同じ型となる。 + +このコードは、従来の 1〜3個のループを用いたコレクション処理に比べて、より簡潔なものになっている (中間結果をどこかにバッファリングする必要があるため、配列を用いた場合はループ3個)。基本的なコレクションのボキャブラリを覚えてしまえば、明示的なループを書くよりも、このようなコードを書く方が簡単かつ安全だと思うようになるだろう。さらに、`partition` 演算は高速であり、将来的にはマルチコア上で並列コレクションにかけると更に速くなるだろう。(並列コレクションは開発ビルドに入っており、Scala 2.9 の一部としてリリースされる予定。) + +このガイドはユーザーの視点から Scala 2.8 のコレクションクラスの API について詳細に説明する。全ての基本的なクラスと、それらのメソッドをみていこう。 diff --git a/ja/overviews/collections/iterators.md b/ja/overviews/collections/iterators.md new file mode 100644 index 0000000000..1b78cbb911 --- /dev/null +++ b/ja/overviews/collections/iterators.md @@ -0,0 +1,177 @@ +--- +layout: overview-large +title: イテレータ + +disqus: true + +partof: collections +num: 15 +language: ja +--- + +イテレータ ([`Iterator`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterator.html)) はコレクションではなく、コレクションから要素を1つづつアクセスするための方法だ。イテレータ `it` に対する基本的な演算として `next` と `hasNext` の2つがある。 `it.next()` を呼び出すことで、次の要素が返り、イテレータの内部状態が前進する。よって、同じイテレータに対して `next` を再び呼び出すと、前回返したものの次の要素が得られる。返す要素が無くなると、`next` の呼び出しは `NoSuchElementException` を発生させる。返す要素が残っているかは [`Iterator`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterator.html) の `hasNext` メソッドを使って調べることができる。 + +イテレータ `it` が返す全ての要素を渡り歩くのに最も率直な方法は while ループを使うことだ: + + while (it.hasNext) + println(it.next()) + +`Traversable`、`Iterable`、および `Seq` クラスのほとんどのメソッドに類似するものを Scala のイテレータは提供している。たとえば、与えられた手順をイテレータが返す全ての要素に対して実行する `foreach` メソッドを提供する。 `foreach` を使うことで、先ほどのループは以下のように短縮できる: + + it foreach println + +例にならって、`foreach`、`map`、`withFilter`、および `flatMap` の代替構文として for 式を使うことができるので、イテレータが返す全ての要素を表示するもう一つの方法として以下のように書ける: + + for (elem <- it) println(elem) + +イテレータの `foreach` メソッドと traversable の同メソッドには重大な違いがある。イテレータのそれを呼び出した場合、`foreach` はイテレータを終端に置いたままで終了するということだ。そのため、`next` を再び呼び出すと `NoSuchElementException` を発生して失敗する。それに比べ、コレクションに対して呼び出した場合、`foreach` +はコレクション内の要素の数を変更しない (渡された関数が要素を追加もしくは削除した場合は別の話だが、これは予想外の結果になることがあるので非推奨だ)。 + +`Iterator` と `Traversable` に共通の他の演算も同じ特性を持つ。例えば、イテレータは新たなイテレータを返す `map` メソッドを提供する: + + scala> val it = Iterator("a", "number", "of", "words") + it: Iterator[java.lang.String] = non-empty iterator + scala> it.map(_.length) + res1: Iterator[Int] = non-empty iterator + scala> res1 foreach println + 1 + 6 + 2 + 5 + scala> it.next() + java.util.NoSuchElementException: next on empty iterator + +上記の通り、`it.map` の呼び出しの後、イテレータ `it` は終端まで前進してしまっている。 + +次の具体例は、ある特性をもつイテレータ内の最初の要素を検索するのに使うことができる `dropWhile` だ。例えば、イテレータ内で二文字以上の最初の語句を検索するのに、このように書くことができる: + + scala> val it = Iterator("a", "number", "of", "words") + it: Iterator[java.lang.String] = non-empty iterator + scala> it dropWhile (_.length < 2) + res4: Iterator[java.lang.String] = non-empty iterator + scala> it.next() + res5: java.lang.String = number + +`dropWhile` を呼び出すことで `it` が変更された事に注意してほしい。イテレータは二番目の語句「number」を指している。実際に、`it` と `dropWhile` の返した戻り値である `res4` 同じ要素の列を返す。 + +同じイテレータを再利用するための標準演算が一つだけある。以下の + + val (it1, it2) = it.duplicate + +への呼び出しはイテレータ `it` と全く同じ要素を返すイテレータを**2つ**返す。この2つのイテレータは独立して作動するため、片方を前進しても他方は影響を受けない。一方、元のイテレータ `it` は `duplicate` により終端まで前進したため、使いものにならない。 + +要約すると、イテレータは**メソッドを呼び出した後、絶対にアクセスしなければ**コレクションのように振る舞う。Scala +コレクションライブラリは、[`Traversable`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Traversable.html) と [`Iterator`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterator.html) に共通の親クラスである [`TraversableOnce`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/TraversableOnce.html) を提供することで、明示的にこれを示す。名前が示す通り、 `TraversableOnce` は `foreach` を用いて一度だけ探索することができるが、探索後のそのオブジェクトの状態は指定されていない。`TraversableOnce` オブジェクトが `Iterator` ならば、探索後はその終端にあるし、もし `Traversable` ならば、そのオブジェクトは今まで通り存在する。 `TraversableOnce` のよく使われる事例としては、イテレータか `Traversable` を受け取ることができるメソッドの引数の型だ。その例として、 `Traversable` クラスの追加メソッド `++` がある。`TraversableOnce` パラメータを受け取るため、イテレータか `Traversable` なコレクションの要素を追加することができる。 + +イテレータの全演算は次の表にまとめられている。 + +### Iterator クラスの演算 + +| 使用例 | 振る舞い| +| ------ | ------ | +| **抽象メソッド:** | | +| `it.next()` | イテレータの次の要素を返し、前進させる。 | +| `it.hasNext` | `it` が次の要素を返せる場合、`true` を返す。 | +| **他のイテレータ:** | | +| `it.buffered` | `it` が返す全ての要素を返すバッファ付きイテレータ。 | +| `it grouped size` | `it` が返す要素を固定サイズの「かたまり」にして返すイテレータ。 | +| `xs sliding size` | `it` が返す要素を固定サイズの「窓」をスライドさせて返すイテレータ。 | +| **複製:** | | +| `it.duplicate` | `it` が返す全ての要素を独立して返すイテレータのペア。 | +| **加算:** | | +| `it ++ jt` | イテレータ `it` が返す全ての要素に続いてイテレータ `jt` の全ての要素を返すイテレータ。 | +| `it padTo (len, x)` | 全体で `len`個の要素が返るように、イテレータ `it` の全ての要素に続いて `x` を返すイテレータ。 | +| **map 演算:** | | +| `it map f` | `it` が返す全ての要素に関数 `f` を適用することによって得られるイテレータ。 | +| `it flatMap f` | `it` が返す全ての要素に対してイテレータ値を返す関数 `f` を適用し、その結果を連結したイテレータ。 | +| `it collect f` | `it` が返す全ての要素に対して部分関数 `f` が定義されている場合のみ適用し、その結果を集めたイテレータ。 | +| **変換演算:** | | +| `it.toArray` | `it` が返す要素を配列に集める。 | +| `it.toList` | `it` が返す要素をリストに集める。 | +| `it.toIterable` | `it` が返す要素を iterable に集める。 | +| `it.toSeq` | `it` が返す要素を列に集める。 | +| `it.toIndexedSeq` | `it` が返す要素を添字付き列に集める。 | +| `it.toStream` | `it` が返す要素をストリームに集める。 | +| `it.toSet` | `it` が返す要素を集合に集める。 | +| `it.toMap` | `it` が返すキー/値ペアをマップに集める。 | +| **コピー演算:** | | +| `it copyToBuffer buf` | `it` が返す要素をバッファ `buf` にコピーする。 | +| `it copyToArray(arr, s, n)`| `it` が返す最大 `n` 個の要素を配列 `arr` の添字 `s` より始まる位置にコピーする。最後の2つの引数は省略可能だ。 | +| **サイズ演算:** | | +| `it.isEmpty` | イテレータが空であるかどうかを調べる (`hasNext` の逆)。 | +| `it.nonEmpty` | イテレータに要素が含まれているかを調べる (`hasNext` の別名)。 | +| `it.size` | `it` が返す要素の数。注意: この演算の後、`it` は終端まで前進する! | +| `it.length` | `it.size` に同じ。 | +| `it.hasDefiniteSize` | `it` が有限数の要素を返すことが明らかな場合 true を返す (デフォルトでは `isEmpty` に同じ)。 | +| **要素取得演算・添字検索演算:**| | +| `it find p` | `it` が返す要素の中で条件関数 `p` を満たす最初の要素のオプション値、または条件を満たす要素が無い場合 `None`。注意: イテレータは探しだされた要素の次の要素、それが無い場合は終端まで前進する。 | +| `it indexOf x` | `it` が返す要素の中で `x` と等しい最初の要素の添字。注意: イテレータはこの要素の次の位置まで前進する。 | +| `it indexWhere p` | `it` が返す要素の中で条件関数 `p` を満たす最初の要素の添字、注意: イテレータはこの要素の次の位置まで前進する。 | +| **部分イテレータ演算:** | | +| `it take n` | `it` が返す最初の `n`個の要素を返すイテレータ。注意: `it` は、`n`個目の要素の次の位置、または`n`個以下の要素を含む場合は終端まで前進する。 | +| `it drop n` | `it` の `(n+1)`番目の要素から始まるイテレータ。注意: `it` も同じ位置まで前進する。| +| `it slice (m,n)` | `it` が返す要素の内、`m`番目から始まり `n`番目の一つ前で終わる切片を返すイテレータ。 | +| `it takeWhile p` | `it` が返す要素を最初から次々とみて、条件関数 `p` を満たす限り返していったイテレータ。 | +| `it dropWhile p` | `it` が返す要素を最初から次々とみて、条件関数 `p` を満たす限り飛ばしていき、残りを返すイテレータ。 | +| `it filter p` | `it` が返すの要素で条件関数 `p` を満たすものを返すイテレータ。 | +| `it withFilter p` | `it filter p` に同じ。イテレータが for 式で使えるように用意されている。 | +| `it filterNot p` |`it` が返すの要素で条件関数 `p` を満たさないものを返すイテレータ。 | +| **分割演算:** | | +| `it partition p` | `it` を2つのイテレータから成るペアに分割する。片方のイテレータは `it` が返す要素のうち条件関数 `p` を満たすものを返し、もう一方は `it` が返す要素のうち `p` を満たさないものを返す。 | +| **要素条件演算:** | | +| `it forall p` | `it` が返す全ての要素に条件関数 `p` が当てはまるかを示す boolean 値。 | +| `it exists p` | `it` が返す要素の中に条件関数 `p` を満たすものがあるかどうかを示す boolean 値。 | +| `it count p` | `it` が返す要素の中にで条件関数 `p` 満たすものの数。 | +| **fold 演算:** | | +| `(z /: it)(op)` | `z` から始めて、左から右へと `it` が返す隣接する要素に二項演算 `op` を次々と適用したもの。 | +| `(it :\ z)(op)` | `z` から始めて、右から左へと `it` が返す隣接する要素に二項演算 `op` を次々と適用したもの。 | +| `it.foldLeft(z)(op)` | `(z /: it)(op)` に同じ。 | +| `it.foldRight(z)(op)` | `(it :\ z)(op)` に同じ。 | +| `it reduceLeft op` | 左から右へと、空ではないイテレータ `it` が返す隣接する要素に二項演算 `op` を次々と適用したもの。 | +| `it reduceRight op` | 右から左へと、空ではないイテレータ `it` が返す隣接する要素に二項演算 `op` を次々と適用したもの。 | +| **特定 fold 演算:** | | +| `it.sum` | イテレータ `it` が返す数値要素の値の和。 | +| `it.product` | イテレータ `it` が返す数値要素の値の積。 | +| `it.min` | イテレータ `it` が返す順序付けされたの値の最小値。 | +| `it.max` | イテレータ `it` が返す順序付けされたの値の最大値。 | +| **zip 演算:** | | +| `it zip jt` | イテレータ `it` と `jt` が返す要素から対応したものペアにして返すイテレータ。 | +| `it zipAll (jt, x, y)` | イテレータ `it` と `jt` が返す要素から対応したものペアにして返すイテレータで、もし片方が短い場合は `x` か `y` を使って長いほうに合わせる。 | +| `it.zipWithIndex` | `it` が返す要素とその添字をペアにしたイテレータ。 | +| **更新演算:** | | +| `it patch (i, jt, r)` | `it` の、`i` から始まる `r`個の要素をパッチイテレータ `ji` が返す要素に置換したイテレータ。 | +| **比較演算:** | | +| `it sameElements jt` | イテレータ `it` と `jt` が同じ要素を同じ順序で返すかを調べる。注意: この演算の後、`it` の `jt` 少なくともどちらか一方は終端まで前進している。 | +| **文字列演算:** | | +| `it addString (b, start, sep, end)`| `it` が返す要素を `sep` で区切った後、`start` と `end` で挟んだ文字列を `StringBuilder` `b` に追加する。 `start`、`sep`、`end` は全て省略可能。 | +| `it mkString (start, sep, end)` | `it` が返す要素を `sep` で区切った後、`start` と `end` で挟んだ文字列に変換する。 `start`、`sep`、`end` は全て省略可能。 | + +### バッファ付きイテレータ + +イテレータを前進させずに次に返る要素を検査できるような「先読み」できるイテレータが必要になることがたまにある。例えば、一連の文字列を返すイテレータがあるとして、その最初の空白文字列を飛ばすという作業を考える。以下のように書こうと思うかもしれない。 + + def skipEmptyWordsNOT(it: Iterator[String]) = + while (it.next().isEmpty) {} + +しかし、このコードを慎重に見ると間違っていることが分かるはずだ。コードは確かに先頭の空白文字列の続きを読み飛ばすが、`it` は最初の非空白文字列も追い越してしまっているのだ。 + +この問題はバッファ付きイテレータを使うことで解決できる。[`BufferedIterator`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/BufferedIterator.html) トレイトは、`head` というメソッドを追加で提供する [`Iterator`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterator.html) の子トレイトだ。バッファ付きイテレータに対して `head` を呼び出すことで、イテレータを前進させずに最初の要素を返すことができる。バッファ付きイテレータを使うと、空白文字列を読み飛ばすのは以下のように書ける。 + + def skipEmptyWords(it: BufferedIterator[String]) = + while (it.head.isEmpty) { it.next() } + +`buffered` メソッドを呼ぶことで全てのイテレータはバッファ付きイテレータに変換できる。次に具体例で説明する: + + scala> val it = Iterator(1, 2, 3, 4) + it: Iterator[Int] = non-empty iterator + scala> val bit = it.buffered + bit: java.lang.Object with scala.collection. + BufferedIterator[Int] = non-empty iterator + scala> bit.head + res10: Int = 1 + scala> bit.next() + res11: Int = 1 + scala> bit.next() + res11: Int = 2 + +バッファ付きイテレータに対して `head` を呼び出してもイテレータ `bit` は前進しないことに注意してほしい。よって、後続の `bit.next()` の呼び出しは `bit.head` と同じ値を返す。 diff --git a/ja/overviews/collections/maps.md b/ja/overviews/collections/maps.md new file mode 100644 index 0000000000..731b6901fb --- /dev/null +++ b/ja/overviews/collections/maps.md @@ -0,0 +1,162 @@ +--- +layout: overview-large +title: マップ + +disqus: true + +partof: collections +num: 7 +language: ja +--- + +マップ ([`Map`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Map.html)) はキーと値により構成されるペアの [`Iterable`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterable.html) の一種で、**写像** (mapping) や**関連** (association) とも呼ばれる。Scala の [`Predef`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/Predef$.html) クラスは、ペアの `(key, value)` を `key -> value` と書けるような暗黙の変換を提供する。例えば、`Map("x" -> 24, "y" -> 25, "z" -> 26)` は `Map(("x", 24), ("y", 25), ("z", 26))` と全く同じことを意味するがより可読性が高い。 + +マップの基本的な演算は集合のものと似ている。それらは、以下の表にまとめられており、以下のカテゴリーに分類できる: + +* **検索演算** には `apply`、`get`、`getOrElse`、`contains`、および `isDefinedAt` がある。これらはマップをキーから値への部分関数に変える。マップの最も基本的な検索メソッドは `def get(key): Option[Value]` だ。"`m get key`" という演算はマップが `key` に関連する値があるかを調べる。もしあれば、マップはその関連する値を `Some` に包んで返す。`key` がマップ中に定義されていなければ `get` は `None` を返す。マップはまた、任意のキーに関連する値を `Option` に包まずに直接返す `apply` メソッドも定義する。マップにキーが定義されていない場合は、例外が発生する。 +* **加算と更新演算**である `+`、`++`、`updated` は、マップに新しい対応関係を追加するか、既存の対応関係を更新する。 +
  • 減算である --- は、対応関係をマップから削除する。
  • +* **サブコレクション取得演算**である `keys`、`keySet`、`keysIterator`、`values`、`valuesIterator` は、マップのキーや値を様々な形で別に返す。 +* **変換演算**である `filterKeys` と `mapValues` は、既存のマップの対応関係をフィルターしたり変換することで新たなマップを生成する。 + +### Map トレイトの演算 ### + +| 使用例 | 振る舞い| +| ------ | ------ | +| **検索演算:** | | +| `ms get k` |マップ `ms` 内のキー `k` に関連付けられた値のオプション値、もしくは、キーが見つからない場合、`None`。| +| `ms(k)` |(展開した場合、`ms apply k`) マップ `ms` 内のキー `k` に関連付けられた値、もしくは、キーが見つからない場合は例外。| +| `ms getOrElse (k, d)` |マップ `ms` 内のキー `k` に関連付けられた値、もしくは、キーが見つからない場合、デフォルト値 `d`。| +| `ms contains k` |`ms` がキー `k` への写像を含むかを調べる。| +| `ms isDefinedAt k` |`contains` に同じ。 | +| **加算と更新演算:**| | +| `ms + (k -> v)` |`ms` 内の全ての写像と、キー `k` から値 `v` への写像 `k -> v` を含むマップ。| +| `ms + (k -> v, l -> w)` |`ms` 内の全ての写像と、渡されたキーと値のペアを含むマップ。| +| `ms ++ kvs` |`ms` 内の全ての写像と、`kvs`内の全てのキーと値のペアを含むマップ。| +| `ms updated (k, v)` |`ms + (k -> v)` に同じ。| +| **減算:** | | +| `ms - k` |キー `k` からの写像を除く、`ms` 内の全ての写像。| +| `ms - (k, 1, m)` |渡されたキーからの写像を除く、`ms` 内の全ての写像。| +| `ms -- ks` |`ks`内のキーからの写像を除く、`ms` 内の全ての写像。| +| **サブコレクション取得演算:** | | +| `ms.keys` |`ms`内の全てのキーを含む iterable。| +| `ms.keySet` |`ms`内の全てのキーを含む集合。| +| `ms.keyIterator` |`ms`内の全てのキーを返すイテレータ。| +| `ms.values` |`ms`内のキーに関連付けられた全ての値を含む iterable。| +| `ms.valuesIterator` |`ms`内のキーに関連付けられた全ての値を返すイテレータ。| +| **変換演算:** | | +| `ms filterKeys p` |キーが条件関数 `p` を満たす `ms`内の写像のみを含むマップのビュー。| +| `ms mapValues f` |`ms`内のキーに関連付けられた全ての値に関数 `f` を適用して得られるマップのビュー。| + +可変マップ ([`mutable.Map`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/Map.html)) は他にも以下の表にまとめた演算をサポートする。 + +### mutable.Map トレイトの演算 ### + +| 使用例 | 振る舞い| +| ------ | ------ | +| **加算と更新演算:** | | +| `ms(k) = v` |(展開した場合、`ms.update(x, v)`)。マップ `ms` に副作用としてキー `k` から値 `v` への写像を加え、既に `k` からの写像がある場合は上書きする。| +| `ms += (k -> v)` |マップ `ms` に副作用としてキー `k` から値 `v` への写像を加え、`ms`自身を返す。| +| `ms += (k -> v, l -> w)` |マップ `ms` に副作用として渡された写像を加え、`ms`自身を返す。| +| `ms ++= kvs` |マップ `ms` に副作用として `kvs`内の全ての写像を加え、`ms`自身を返す。| +| `ms put (k, v)` |マップ `ms` にキー `k` から値 `v` への写像を加え、以前の `k` からの写像のオプション値を返す。| +| `ms getOrElseUpdate (k, d)`|マップ `ms`内にキー `k` が定義されている場合は、関連付けられた値を返す。定義されていない場合は、`ms` に写像 `k -> d` を加え、`d` を返す。| +| **減算:**| | +| `ms -= k` |マップ `ms` から副作用としてキー `k` からの写像を削除して、`ms`自身を返す。| +| `ms -= (k, l, m)` |マップ `ms` から副作用として渡されたキーからの写像を削除して、`ms`自身を返す。| +| `ms --= ks` |マップ `ms` から副作用として `ks`内の全てのキーからの写像を削除して、`ms`自身を返す。| +| `ms remove k` |マップ `ms` からキー `k` からの写像を削除して、以前の `k` からの写像のオプション値を返す。| +| `ms retain p` |`ms`内の写像でキーが条件関数 `p` を満たすものだけを残す。| +| `ms.clear()` |`ms` から全ての写像を削除する。| +| **変換演算:** | | +| `ms transform f` |マップ `ms`内の全ての関連付けされた値を関数 `f` を使って変換する。| +| **クローン演算:** | | +| `ms.clone` |`ms` と同じ写像を持つ新しい可変マップを返す。| + +マップの加算と減算は、集合のそれにならう。集合と同様、非破壊的な演算である `+`、`-`、と `updated` を提供するが、加算マップをコピーする必要があるため、これらはあまり使われることがない。そのかわり、可変マップは通常 `m(key) = value` か `m += (key -> value)` という2種類の更新演算を使って上書き更新される。さらに前に `key` から関連付けされていた値を +`Option`値で返すか、マップに `key` が無ければ `None` を返すというバリアントである `m put (key, value)` もある。 + +`getOrElseUpdate` はキャッシュとして振る舞うマップにアクセスするのに役立つ。例えば、関数 `f` により呼び出される時間のかかる計算があるとする: + + scala> def f(x: String) = { + println("taking my time."); sleep(100) + x.reverse } + f: (x: String)String + +さらに、`f` には副作用を伴わず、同じ引数で何回呼び出しても同じ戻り値が返ってくると仮定する。この場合、引数と以前の `f` 計算結果の対応関係をマップに格納して、引数がマップに無いときだけ `f` の結果を計算すれば時間を節約できる。この時、マップは関数 `f` の計算の**キャッシュ**であると言える。 + + val cache = collection.mutable.Map[String, String]() + cache: scala.collection.mutable.Map[String,String] = Map() + +これにより、より効率的な、キャッシュするバージョンの関数 `f` を作成することができる: + + scala> def cachedF(s: String) = cache.getOrElseUpdate(s, f(s)) + cachedF: (s: String)String + scala> cachedF("abc") + taking my time. + res3: String = cba + scala> cachedF("abc") + res4: String = cba + +`getOrElseUpdate` の第二引数は「名前渡し」(by-name) であるため、上の `f("abc")` は `getOrElseUpdate` が必要とする場合、つまり第一引数が `cache` に無い場合においてのみ計算されることに注意してほしい。 `cachedF` をより率直に、普通の map 演算を用いて実装することもできるが、コードは少し長くなる: + + def cachedF(arg: String) = cache get arg match { + case Some(result) => result + case None => + val result = f(x) + cache(arg) = result + result + } + +### 同期集合と同期マップ ### + +`SynchronizedMap` トレイトを好みのマップ実装にミックスインすることでスレッドセーフな可変マップを得ることができる。例えば、以下のコードが示すように、`HashMap` に `SynchronizedMap` をミックスインすることができる。この例は `Map` と `SynchronizedMap` の二つのトレイト、そして `HashMap` という一つのクラスを `scala.collection.mutable` パッケージからインポートすることから始まる。例の残りは `makeMap` というメソッドを宣言するシングルトンオブジェクト `MapMaker` を定義する。`makeMap` メソッドは戻り値の型を文字列をキーとして文字列を値とする可変マップだと宣言する。 + + import scala.collection.mutable.{Map, + SynchronizedMap, HashMap} + object MapMaker { + def makeMap: Map[String, String] = { + new HashMap[String, String] with + SynchronizedMap[String, String] { + override def default(key: String) = + "Why do you want to know?" + } + } + } + +`makeMap` 本文の第1ステートメントは `SynchronizedMap` トレイトをミックスインする新しい可変 `HashMap` を作成する: + + new HashMap[String, String] with + SynchronizedMap[String, String] + +このコードを与えられると、Scala コンパイラは `SynchronizedMap` をミックスインする `HashMap` の合成的な子クラスを生成し、そのインスタンスを作成する (そして、それを戻り値として返す)。この合成クラスは、以下のコードのため、`default` という名前のメソッドをオーバーライドする。: + + override def default(key: String) = + "何故知りたい?" + +通常は、ある特定のキーに対する値をマップに問い合わせて、そのキーからの写像が無い場合は`NoSuchElementException` が発生する。新たなマップのクラスを定義して `default` メソッドをオーバーライドした場合は、しかしながら、存在しないキーに対する問い合わせに対して、この新しいマップは `default` が返す値を返す。そのため、同期マップのコードでコンパイラに生成された `HashMap` の合成の子クラスは、存在しないキーに対する問い合わせに `"何故知りたい?"` と少々意地の悪い答えを返す。 + +`makeMap` メソッドが返す可変マップは `SynchronizedMap` トレイトをミックスインするため、複数のスレッドから同時に使うことができる。マップへのそれぞれのアクセスは同期化される。以下は、インタープリタ中で一つのスレッドから使用した例だ: + + scala> val capital = MapMaker.makeMap + capital: scala.collection.mutable.Map[String,String] = Map() + scala> capital ++ List("US" -> "Washington", + "Paris" -> "France", "Japan" -> "Tokyo") + res0: scala.collection.mutable.Map[String,String] = + Map(Paris -> France, US -> Washington, Japan -> Tokyo) + scala> capital("Japan") + res1: String = Tokyo + scala> capital("New Zealand") + res2: String = Why do you want to know? + scala> capital += ("New Zealand" -> "Wellington") + scala> capital("New Zealand") + res3: String = Wellington + +同期集合も同期マップと同じ要領で作成することができる。例えば、このように `SynchronizedSet` トレイトをミックスインすることで同期 `HashSet` を作ることができる: + + import scala.collection.mutable + val synchroSet = + new mutable.HashSet[Int] with + mutable.SynchronizedSet[Int] + +最後に、同期コレクションを使うことを考えているならば、`java.util.concurrent` の並行コレクションを使うことも考慮した方がいいだろう。 diff --git a/ja/overviews/collections/migrating-from-scala-27.md b/ja/overviews/collections/migrating-from-scala-27.md new file mode 100644 index 0000000000..6c2ab707a3 --- /dev/null +++ b/ja/overviews/collections/migrating-from-scala-27.md @@ -0,0 +1,44 @@ +--- +layout: overview-large +title: Scala 2.7 からの移行 + +disqus: true + +partof: collections +num: 18 +outof: 18 +language: ja +--- + +既存の Scala アプリケーションの新しいコレクションへの移植はほぼ自動的であるはずだ。問題となり得ることはいくつかしかない。 + +一般的論として、Scala 2.7 コレクションの古い機能はそのまま残っているはずだ。機能の中には廃止予定となったものもあり、それは今後のリリースで撤廃されるということだ。Scala 2.8 でそのような機能を使ったコードをコンパイルすると**廃止予定警告** (deprecation warning) が発生する。その意味や性能特性を変えて 2.8 に残った演算もあり、その場合は廃止予定にするのは無理だった。このような場合は 2.8 でコンパイルすると**移行警告** (migration warning) が出される。コードをどう変えればいいのかも提案してくれる完全な廃止予定警告と移行警告を得るには、`-deprecation` と `-Xmigration` フラグを `scalac` に渡す (`-Xmigration` は `X` で始まるため、拡張オプションであることに注意)。同じオプションを `scala` REPL に渡すことで対話セッション上で警告を得ることができる。具体例としては: + + >scala -deprecation -Xmigration + Welcome to Scala version 2.8.0.final + Type in expressions to have them evaluated. + Type :help for more information. + scala> val xs = List((1, 2), (3, 4)) + xs: List[(Int, Int)] = List((1,2), (3,4)) + scala> List.unzip(xs) + :7: warning: method unzip in object List is deprecated: use xs.unzip instead of List.unzip(xs) + List.unzip(xs) + ^ + res0: (List[Int], List[Int]) = (List(1, 3),List(2, 4)) + scala> xs.unzip + res1: (List[Int], List[Int]) = (List(1, 3),List(2, 4)) + scala> val m = xs.toMap + m: scala.collection.immutable.Map[Int,Int] = Map((1,2), (3,4)) + scala> m.keys + :8: warning: method keys in trait MapLike has changed semantics: + As of 2.8, keys returns Iterable[A] rather than Iterator[A]. + m.keys + ^ + res2: Iterable[Int] = Set(1, 3) + +旧ライブラリより完全に置き換えられ、廃止予定警告を出すのが無理だったものが 2つある。 + +1. 以前の `scala.collection.jcl` パッケージは撤廃された。 このパッケージは Scala 上で Java コレクションライブラリの設計を真似しようとしたが、それは多くの対称性を壊してしまった。Java コレクションが欲しい人の多くは `jcl` を飛ばして `java.util` を直接使用していた。Scala 2.8 は、`jcl` パッケージの代わりに、[`JavaConversions`](conversions-between-java-and-scala-collections.html) オブジェクトにて両方のライブラリ間の自動変換機構を提供する。 +2. 投射 (projection) は一般化され、きれいにされ、現在はビューとして提供される。投射はほとんど使われていなかったようなので、この変更に影響を受けるコードは少ないはずだ。 + +よって、`jcl` か投射を使っている場合は多少コードの書き換えが必要になるかもしれない。 diff --git a/ja/overviews/collections/overview.md b/ja/overviews/collections/overview.md new file mode 100644 index 0000000000..d34da77fd6 --- /dev/null +++ b/ja/overviews/collections/overview.md @@ -0,0 +1,101 @@ +--- +layout: overview-large +title: 可変コレクションおよび不変コレクション + +disqus: true + +partof: collections +num: 2 +language: ja +--- + +Scala のコレクションは、体系的に可変および不変コレクションを区別している。**可変** (mutable) コレクションは上書きしたり拡張することができる。これは副作用としてコレクションの要素を変更、追加、または削除することができることを意味する。一方、**不変** (immutable) コレクションは変わることが無い。追加、削除、または更新を模倣した演算は提供されるが、全ての場合において演算は新しいコレクションを返し、古いコレクションは変わることがない。 + +コレクションクラスの全ては `scala.collection` パッケージもしくは `mutable`、`immutable`、`generic` のどれかのサブパッケージに定義されている。クライアントコードに必要なコレクションのクラスのほどんどには可変性に関して異なる特性を持つ 3つの形態が定義されおり、ぞれぞれ `scala.collection`、`scala.collection.immutable`、か `scala.collection.mutable` のパッケージに存在する。 + +`scala.collection.immutable` パッケージのコレクションは、誰にとっても不変であることが保証されている。 +そのようなコレクションは作成後には一切変更されることがない。したがって、異なる時点で何回同じコレクションの値にアクセスしても常に同じ要素を持つコレクションが得られることに依存できる。 + +`scala.collection.mutable` パッケージのコレクションは、コレクションを上書き変更する演算がある。 +だから可変コレクションを扱うということは、どのコードが、何時どのコレクションを変更したのかということを理解する必要があることを意味する。 + +`scala.collection` パッケージのコレクションは、可変か不変かのどちらでもありうる。例えば [`collection.IndexedSeq[T]`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/IndexedSeq.html) +は、[`collection.immutable.IndexedSeq[T]`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/IndexedSeq.html) と [`collection.mutable.IndexedSeq[T]`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/IndexedSeq.html) 両方の親クラスだ。一般的に、`scala.collection`パッケージの基底コレクションは不変コレクションと同じインターフェイスを定義し、`scala.collection.mutable` パッケージ内の可変コレクションは、副作用を伴う変更演算を不変インターフェイスに加える。 + +基底コレクションと不変コレクションの違いは、不変なコレクションのクライアントは、他の誰もコレクションを変更しないという保証があるのに対し、基底コレクションのクライアントは自分ではコレクションを変更しなかったという約束しかできない。たとえ静的な型がコレクションを変更するような演算を提供していなくても、実行時の型は他のクライアントが手を加えることができる可変コレクションである可能性がある。 + +デフォルトでは Scala は常に不変コレクションを選ぶ。たとえば、`scala` パッケージのデフォルトのバインディングにより、なんの接頭辞や import もなくただ `Set` と書くと不変な集合 (set) が返ってき、`Iterable` と書くと不変で反復可能 (iterable)なコレクションが返ってくる。可変なデフォルト実装を取得するには、`collection.mutable.Set` +または `collection.mutable.Iterable` と明示的に記述する必要がある。 + +可変と不変の両方のバージョンのコレクションを使用する場合に便利な慣例は `collection.mutable` パッケージだけをインポートすることだ。 + + import scala.collection.mutable + +これにより、接頭辞なしの `Set` は不変なコレクションを参照するのに対し、`mutable.Set` は可変版を参照する。 + +コレクション階層内の最後のパッケージは `collection.generic` だ。 +このパッケージには、コレクションを実装するための基本的なパーツが含まれている。 +コレクションクラスがいくつかの演算を `generic` 内のクラスに委譲することはよくあるが、 フレームワークのユーザーが `generic` 内のクラスが必要になることは普通はありえない。 + +利便性と後方互換性のために、いつくかの重要な型は `scala` パッケージ内に別名を定義してあるため、インポート無しで単純な名前でコレクションを使うことができる。[`List`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/List.html) 型が良い例で、以下の名前でもアクセスすることができる + + scala.collection.immutable.List // 定義元 + scala.List // scala パッケージのエイリアス経由 + List // scala._ パッケージは + // 常に自動的にインポートされるため + +エイリアスされているその他の型は次のとおり: +[`Traversable`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Traversable.html)、[`Iterable`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterable.html)、[`Seq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Seq.html)、[`IndexedSeq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/IndexedSeq.html)、[`Iterator`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterator.html)、[`Stream`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Stream.html)、[`Vector`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Vector.html)、[`StringBuilder`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/mutable/StringBuilder.html)、[`Range`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Range.html)。 + +次の図は `scala.collection` パッケージ内の全てのコレクションを示す。 +これらはすべて、高レベルの抽象クラスやトレイトで一般に可変と不変の両方の実装を持っている。 + +[]({{ site.baseurl }}/resources/images/collections.png) + +次の図は `scala.collection.immutable` パッケージ内の全てのコレクションを示す。 + +[]({{ site.baseurl }}/resources/images/collections.immutable.png) + +そして、次の図は `scala.collection.mutable` パッケージ内の全てのコレクションを示す。 + +[]({{ site.baseurl }}/resources/images/collections.mutable.png) + +(以上三つ全ての図は decodified.com の Matthias によって生成された。) + +## コレクションAPIの概要 + +最も重要なコレクションクラスは上の図に示されている。 +これらの全てのクラスに共通な部分が沢山ある。 +例えば、全てのコレクションは、クラス名を書いた後で要素を書くという統一された構文で作成することができる: + + Traversable(1, 2, 3) + Iterable("x", "y", "z") + Map("x" -> 24, "y" -> 25, "z" -> 26) + Set(Color.red, Color.green, Color.blue) + SortedSet("hello", "world") + Buffer(x, y, z) + IndexedSeq(1.0, 2.0) + LinearSeq(a, b, c) + +特定のコレクションの実装にもこの原則が適用される: + + List(1, 2, 3) + HashMap("x" -> 24, "y" -> 25, "z" -> 26) + +これらのコレクションは、`toString` を呼び出すと上の表記方法で表示される。 + +すべてのコレクションが `Traversable` によって提供される API をサポートするが、理にかなうところでは型を特殊化している。 +たとえば、`Traversable` クラスの `map` メソッドは別の `Traversable` を戻り値として返すが、結果の型はサブクラスでオーバーライドされる。 +たとえば、`List` が `map` を呼び出しても再び `List` が返ってき、`Set` が `map` を呼び出すと `Set` が返ってくる、といういう具合だ。 + + scala> List(1, 2, 3) map (_ + 1) + res0: List[Int] = List(2, 3, 4) + scala> Set(1, 2, 3) map (_ * 2) + res0: Set[Int] = Set(2, 4, 6) + +コレクションライブラリ中のあらゆる所で実装されているこの振る舞いは**戻り値同型の原則** と呼ばれる。 + +コレクションの階層のクラスのほとんどは基底、不変、可変の3種類とも存在する。 +唯一の例外は、可変コレクションにのみ存在する `Buffer` トレイトだ。 + +これより、これらのクラスを一つづつ見ていく。 diff --git a/ja/overviews/collections/performance-characteristics.md b/ja/overviews/collections/performance-characteristics.md new file mode 100644 index 0000000000..e36506fcbd --- /dev/null +++ b/ja/overviews/collections/performance-characteristics.md @@ -0,0 +1,84 @@ +--- +layout: overview-large +title: 性能特性 + +disqus: true + +partof: collections +num: 12 +language: ja +--- + +これまでの説明で異なるコレクションが異なる性能特性 (performance characteristics) を持つことが分かった。性能特性はコレクション型を比較する第一の基準としてよく使われる。以下の 2つの表にコレクションの主な演算の性能特性をまとめた。 + +列型の性能特性: + +| | head | tail | apply | 更新 | 先頭に追加 | 最後に追加 | 挿入 | +| -------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | +| **不変** | | | | | | | | +| `List` | 定数 | 定数 | 線形 | 線形 | 定数 | 線形 | - | +| `Stream` | 定数 | 定数 | 線形 | 線形 | 定数 | 線形 | - | +| `Vector` |実質定数|実質定数|実質定数|実質定数| 実質定数 | 実質定数 | - | +| `Stack` | 定数 | 定数 | 線形 | 線形 | 定数 | 定数 | 線形 | +| `Queue` |ならし定数|ならし定数|線形| 線形 | 線形 | 定数 | - | +| `Range` | 定数 | 定数 | 定数 | - | - | - | - | +| `String` | 定数 | 線形 | 定数 | 線形 | 線形 | 線形 | - | +| **可変** | | | | | | | | +| `ArrayBuffer` | 定数 | 線形 | 定数 | 定数 | 線形 | ならし定数 | 線形 | +| `ListBuffer` | 定数 | 線形 | 線形 | 線形 | 定数 | 定数 | 線形 | +|`StringBuilder`| 定数 | 線形 | 定数 | 定数 | 線形 | ならし定数 | 線形 | +| `MutableList` | 定数 | 線形 | 線形 | 線形 | 定数 | 定数 | 線形 | +| `Queue` | 定数 | 線形 | 線形 | 線形 | 定数 | 定数 | 線形 | +| `ArraySeq` | 定数 | 線形 | 定数 | 定数 | - | - | - | +| `Stack` | 定数 | 線形 | 線形 | 線形 | 定数 | 線形 | 線形 | +| `ArrayStack` | 定数 | 線形 | 定数 | 定数 | ならし定数| 線形 | 線形 | +| `Array` | 定数 | 線形 | 定数 | 定数 | - | - | - | + +集合とマップ型の性能特性: + +| | 検索 | 追加 | 削除 | 最小 | +| -------- | ---- | ---- | ---- | ---- | +| **不変** | | | | | +| `HashSet`/`HashMap`| 実質定数| 実質定数| 実質定数 | 線形 | +| `TreeSet`/`TreeMap`| Log | Log | Log | Log | +| `BitSet` | 定数 | 線形 | 線形 | 実質定数1| +| `ListMap` | 線形 | 線形 | 線形 | 線形 | +| **可変** | | | | | +| `HashSet`/`HashMap`|実質定数 |実質定数|実質定数| 線形 | +| `WeakHashMap` |実質定数 |実質定数|実質定数| 線形 | +| `BitSet` | 定数 |ならし定数| 定数 |実質定数1| +| `TreeSet` | Log | Log | Log | Log | + +脚注: 1 ビットが密にパックされていることを前提にしている。 + +表の値を以下に説明する: + +| | | +| --- | ---- | +| **定数** | 演算は (高速な) 定数時間で完了する。| +| **実質定数** | 演算は実質定数時間で完了するが、ベクトルの最大長やハッシュキーの分布など何らかの前提に依存する。| +| **ならし定数** | 演算は「ならし定数時間」で完了する。演算の呼び出しの中には定数時間よりも長くかかるものもあるが、多くの演算の実行時間の平均を取った場合定数時間となる。 | +| **Log** | 演算はコレクションのサイズの対数に比例した時間で完了する。 | +| **線形** | 演算は線形時間、つまりコレクションのサイズに比例した時間で完了する。 | +| **-** | 演算はサポートされていない。 | + +最初の表は、不変と可変両方の列型の以下の演算の性能特性をまとめる: + +| | | +| --- | ---- | +| **head** | 列の最初の要素を選択する。 | +| **tail** | 最初の要素以外の全ての要素から成る新たな列を生成する。 | +| **apply** | 添字によるアクセス。 | +| **更新** | 不変列のときは (`updated` による) 関数型の更新、可変列のときは (`update` による) 副作用としての上書き更新。 | +| **先頭に追加**| 列の先頭への要素の追加。不変列のときは新たな列を生成する。可変列のときは現在の列を上書きする。 | +| **最後に追加** | 列の最後に要素を追加する。不変列のときは新たな列を生成する。可変列のときは現在の列を上書きする。 | +| **挿入** | 要素を列の任意の位置に挿入する。この演算は可変列にのみサポートされている。 | + +次の表は、不変と可変両方の集合とマップの以下の演算の性能特性をまとめる: + +| | | +| --- | ---- | +| **検索** | 要素が集合に含まれるかを調べるか、マップからキーに関連付けられた値を選択する。 | +| **追加** | 集合に新たな要素を追加するか、マップにキー/値ペアを追加する。 | +| **削除** | 集合から要素を削除するか、マップからキーを削除する。 | +| **最小** | 集合の最小要素かマップの最小キー。 | diff --git a/ja/overviews/collections/seqs.md b/ja/overviews/collections/seqs.md new file mode 100644 index 0000000000..b1d7c9f57a --- /dev/null +++ b/ja/overviews/collections/seqs.md @@ -0,0 +1,111 @@ +--- +layout: overview-large +title: 列トレイト Seq、IndexedSeq、および LinearSeq + +disqus: true + +partof: collections +num: 5 +language: ja +--- + +列 ([`Seq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Seq.html)) トレイトは、長さ (`length`) があり、それぞれの要素に `0` から数えられた固定された添字 (index) がある `Iterable` の一種だ。 + +以下の表にまとめられた列の演算は以下のカテゴリーに分けることができる: + +* **添字と長さの演算** `apply`、 `isDefinedAt`、 `length`、 `indices`、および `lengthCompare`。`Seq` では `apply` メソッドは添字の意味で使われるため、`Seq[T]`型の列は `Int` を引数 (添字) としてをとり、`T`型の要素を返す部分関数だ。つまり、`Seq[T]` は `PartialFunction[Int, T]` を継承する。列内の要素はゼロから列の長さ (`length`) − 1 まで添字付けられている。列の `length` メソッドは一般コレクションにおける `size` メソッドの別名だ。`lengthCompare` メソッドは、たとえどちらかの列が無限の長さを持っていても、二つの列の長さを比較することができる。 +* **添字検索演算**である `indexOf`、 `lastIndexOf`、 `indexofSlice`、 `lastIndexOfSlice`、 `indexWhere`、 `lastIndexWhere`、 `segmentLength`、 `prefixLength` は、渡された値もしくは条件関数に合致する要素の添字を返す。 +* **加算**である `+:`、`:+`、`padTo` は、列の先頭か最後に要素を追加した新しい列を返す。 +* **更新演算**である `updated`、`patch` は、元の列に何らかの要素を上書きした列を返す。 +* **並べ替え演算**である `sorted`、`sortWith`、`sortBy` は、列内の要素を何らかの基準に基づいて並べ替える。 +* **逆転演算**である `reverse`、`reverseIterator`、`reverseMap` は、列内の要素を逆順に返すか処理する。 +* **比較演算**である `startsWith`、 `endsWith`、 `contains`、 `containsSlice`、 `corresponds` は、二つの列を関連付けるか、列の中から要素を検索する。 +* **集合演算**である `intersect`、 `diff`、 `union`、 `distinct` は、二つの列間で集合演算のようなものを行うか、列内の要素の重複を削除する。 + +列が可変の場合は、追加で副作用のある `update` メソッドを提供し、列内の要素を上書きすることができる。 +Scala の他の構文の例にならって、`seq(idx) = elem` は `seq.update(idx, elem)` の略記法であるため、`update` によって便利な代入構文がただで手に入る。`update` と `updated` の違いに注意してほしい。 `update` は列内の要素を上書きし、可変列でのみ使用可能だ。 +`updated` は全ての列で使用可能であり、元の列は変更せずに常に新しい列を返す。 + +### Seq トレイトの演算 + +| 使用例 | 振る舞い | +| ------ | ------ | +| **添字と長さの演算:** | | +| `xs(i)` |(展開した場合、`xs apply i`)。`xs` の添字 `i` の位置の要素。| +| `xs isDefinedAt i` |`xs.indices` に `i` が含まれているか調べる。 | +| `xs.length` |列の長さ (`size` と同様)。 | +| `xs.lengthCompare ys` |`xs` が `ys` より短い場合は `-1`、長い場合は `+1`、同じ長さの場合は `0` を返す。いずれかの列が無限でも正常に作動する。| +| `xs.indices` |0 から `xs.length - 1` までの `xs` の添字の範囲。 | +| **添字検索演算:** | | +| `xs indexOf x` |`xs`内で `x` と等しい最初の要素の添字 (数種の別形がある)。| +| `xs lastIndexOf x` |`xs`内で `x` と等しい最後の要素の添字 (数種の別形がある)。| +| `xs indexOfSlice ys` |`xs` の添字で、それと後続の要素が、列 `ys` と同値になる最初のもの。| +| `xs lastIndexOfSlice ys` |`xs` の添字で、それと後続の要素が、列 `ys` と同値になる最後のもの。| +| `xs indexWhere p` |`xs`内で条件関数 `p` を満たす最初の要素の添字 (数種の別形がある)。| +| `xs segmentLength (p, i)`|全ての要素が途切れなく条件関数 `p` を満たし、`xs(i)` から始まる、最長の `xs` の切片の長さ。| +| `xs prefixLength p` |全ての要素が途切れなく条件関数 `p` を満たす、最長の `xs` の先頭切片の長さ。| +| **加算:** | | +| `x +: xs` |`xs` の要素の先頭に `x` を追加した、新しい列。 | +| `xs :+ x` |`xs` の要素の最後に `x` を追加した、新しい列。 | +| `xs padTo (len, x)` |`xs` の長さが `len` になるまで最後に値 `x` を追加していった列。| +| **更新演算:** | | +| `xs patch (i, ys, r)` |`xs`内の、`i` から始まる `r`個の要素をパッチ `ys`内の要素と置換した列。| +| `xs updated (i, x)` |`xs`の添字 `i` の要素を `x` に置換したコピー。 | +| `xs(i) = x` |(展開した場合、`xs.update(i, x)`、ただし可変列でのみ使用可能)。`xs`の添字 `i` の位置の要素を `x` と上書きする。| +| **並べ替え演算:** | | +| `xs.sorted` |`xs` の要素型の標準的な順序付けを用いて、`xs` の要素を並べ替えることによって得られる新しい列。| +| `xs sortWith lt` |比較関数 `lt` 用いて `xs` の要素を並べ替えることによって得られる新しい列。| +| `xs sortBy f` |`xs` の要素を並べ替えることによって得られる新しい列。二つの要素の比較は、両者を関数 `f` に適用してその結果を比較することによって行われる。| +| **逆転演算:** | | +| `xs.reverse` |`xs`内の要素を逆順にした列。 | +| `xs.reverseIterator` |`xs`内の全ての要素を逆順に返すイテレータ。 | +| `xs reverseMap f` |`xs`内の要素に逆順に関数 `f` を `map` して得られる列。| +| **比較演算:** | | +| `xs startsWith ys` |`xs` が列 `ys` から始まるかを調べる (数種の別形がある)。| +| `xs endsWith ys` |`xs` が列 `ys` で終わるかを調べる (数種の別形がある)。| +| `xs contains x` |`xs` が `x` と等しい要素を含むかを調べる。 | +| `xs containsSlice ys` |`xs` が `ys` と等しい連続した切片を含むかを調べる。 | +| `(xs corresponds ys)(p)` |`xs` と `ys` の対応した要素が、二項条件関数の `p` を満たすかを調べる。| +| **集合演算:** | | +| `xs intersect ys` |列 `xs` と `ys` の積集合で、`xs` における要素の順序を保ったもの。| +| `xs diff ys` |列 `xs` と `ys` の差集合で、`xs` における要素の順序を保ったもの。| +| `xs union ys` |和集合; `xs ++ ys` に同じ。 | +| `xs.distinct` |`xs` の部分列で要素の重複を一切含まないもの。 | + +[`Seq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Seq.html) トレイトには [`LinearSeq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/IndexedSeq.html) と [`IndexedSeq`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/IndexedSeq.html) +という二つの子トレイトがある。 +これらは新しい演算を定義しないが、それぞれ異なった性能特性をもつ。 +線形列 (linear sequence) は効率的な `head` と `tail` 演算を持ち、一方添字付き列 (indexed sequence) は効率的な`apply`、`length`、および (可変の場合) `update` 演算を持つ。 +よく使われる線形列の例に `scala.collection.immutable.List` と `scala.collection.immutable.Stream` がある。よく使われる添字付き列の例としては `scala.Array` と `scala.collection.mutable.ArrayBuffer` がある。 +`Vector` は添字付き列と線形列の間の興味深い折衷案だ。 +事実上定数時間のオーバーヘッドで添字アクセスと線形アクセスを提供するからだ。 +そのため、ベクトルは添字アクセスと線形アクセスの両方を混合して使用してるアクセスパターンにおける良い基盤となる。 +ベクトルに関しては、また[後ほど詳しくみていく](concrete-immutable-collection-classes.html#vectors)。 + +### バッファ ### + +可変列に分類されるものの中で重要なものに `Buffer` がある。バッファは既存の要素を上書きできるだけでなく、要素を挿入したり、削除したり、効率的にバッファの最後に新しい要素を追加したりできる。バッファがサポートする新しいメソッドの中で主要なものは、要素を最後に追加する `+=` と `++=`、先頭に追加する `+=:` と `++=:`、要素を挿入する `insert` と `insertAll`、そして要素を削除する `remove` と `-=` だ。以下の表にこれらの演算をまとめた。 + +よく使われるバッファの実装に `ListBuffer` と `ArrayBuffer` がある。名前が示すとおり、`ListBuffer` は `List` に支えられており、要素を効率的に `List` に変換できる。一方、`ArrayBuffer` は配列に支えられており、これも素早く配列に変換できる。 + +#### Buffer クラスの演算 #### + +| 使用例 | 振る舞い| +| ------ | ------ | +| **加算:** | | +| `buf += x` |バッファの最後に要素 `x` を追加し、`buf` 自身を戻り値として返す。| +| `buf += (x, y, z)` |渡された要素をバッファの最後に追加する。| +| `buf ++= xs` |`xs`内の全ての要素をバッファの最後に追加する。| +| `x +=: buf` |バッファの先頭に要素 `x` を追加する。| +| `xs ++=: buf` |`xs`内の全ての要素をバッファの先頭に追加する。| +| `buf insert (i, x)` |バッファの添字 `i` の位置に要素 `x` を挿入する。| +| `buf insertAll (i, xs)` |`xs`内の全ての要素をバッファの添字 `i` の位置に挿入する。| +| **減算:** | | +| `buf -= x` |バッファから要素 `x` を削除する。| +| `buf remove i` |バッファの添字 `i` の位置の要素を削除する。| +| `buf remove (i, n)` |バッファの添字 `i` の位置から始まる `n`個の要素を削除する。| +| `buf trimStart n` |バッファの先頭の要素 `n`個を削除する。| +| `buf trimEnd n` |バッファの最後の要素 `n`個を削除する。| +| `buf.clear()` |バッファの全ての要素を削除する。| +| **クローン演算:** | | +| `buf.clone` |`buf` と同じ要素を持った新しいバッファ。| diff --git a/ja/overviews/collections/sets.md b/ja/overviews/collections/sets.md new file mode 100644 index 0000000000..287a5b3141 --- /dev/null +++ b/ja/overviews/collections/sets.md @@ -0,0 +1,150 @@ +--- +layout: overview-large +title: 集合 + +disqus: true + +partof: collections +num: 6 +language: ja +--- + +集合 ([`Set`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Set.html)) は要素の重複の無い `Iterable` だ。集合一般に定義される演算は次の表にまとめてあり、可変集合に関してはその次の表も見てほしい。これらの演算は以下のカテゴリーに分類される: + +* **条件演算**である `contains`、`apply`、`subsetOf`。`contains` メソッドは集合が任意の要素を含むかを調べる。集合での `apply` メソッドは `contains` と同じであるため、`set(elem)` は `set contains elem` と同じだ。これは集合が要素を含んでいれば `true` を返す関数として使えることを意味する。 + +例えば、 + + + val fruit = Set("apple", "orange", "peach", "banana") + fruit: scala.collection.immutable.Set[java.lang.String] = + Set(apple, orange, peach, banana) + scala> fruit("peach") + res0: Boolean = true + scala> fruit("potato") + res1: Boolean = false + + +* **加算**である `+` と `++` は、単一もしくは複数の要素を集合に追加し、新たな集合を返す。 +* **減算**である `-` と `--` は、単一もしくは複数の要素を集合から削除し、新たな集合を返す。 +* **集合演算**である和集合、積集合、および差集合。これらの演算には文字形とシンボル形の二つの形がある。文字バージョンは `intersect`、`union`、および `diff` で、シンボルバージョンは `&`、`|`、と `&~` だ。`Set` が `Traversable` から継承する `++` は `union` と `|` の更なる別名だと考えることができるが、`++` は `Traversable` の引数を取るが、`union` と `|` は集合を取る。 + +### Set トレイトの演算 ### + +| 使用例 | 振る舞い| +| ------ | ------ | +| **条件演算:** | | +| `xs contains x` |`xs` が `x` を含むかを調べる。| +| `xs(x)` |`xs contains x` に同じ。 | +| `xs subsetOf ys` |`xs` が `ys` の部分集合であるかを調べる。| +| **加算:** | | +| `xs + x` |`xs`内の全ての要素および `x` を含んだ集合。| +| `xs + (x, y, z)` |`xs`内の全ての要素および渡された要素を含んだ集合。| +| `xs ++ ys` |`xs`内の全ての要素と `ys`内の全ての要素を含んだ集合。| +| **減算:** | | +| `xs - x` |`x` を除き、`xs`内の全ての要素を含んだ集合。| +| `xs - (x, y, z)` |渡された要素を除き、`xs`内の全ての要素を含んだ集合。| +| `xs -- ys` |`ys`内の要素を除き、`xs`内の全ての要素を含んだ集合。| +| `xs.empty` |`xs` と同じクラスの空集合。| +| **集合演算:** | | +| `xs & ys` |`xs` と `ys` の積集合。| +| `xs intersect ys` |`xs & ys` に同じ。 | +| `xs | ys` |`xs` と `ys` の和集合。| +| `xs union ys` |`xs | ys` に同じ。 | +| `xs &~ ys` |`xs` と `ys` の差集合。| +| `xs diff ys` |`xs &~ ys` に同じ。 | + +可変集合は、この表にまとめてあるとおり、加算、減算、更新演算などの新たなメソッドを追加する。 + +### mutable.Set トレイトの演算 ### + +| 使用例 | 振る舞い| +| ------ | ------ | +| **加算:** | | +| `xs += x` |集合 `xs` に副作用として要素 `x` を加え、`xs`自身を返す。| +| `xs += (x, y, z)` |集合 `xs` に副作用として渡された要素を加え、`xs`自身を返す。| +| `xs ++= ys` |集合 `xs` に副作用として `ys`内の全ての要素を加え、`xs`自身を返す。| +| `xs add x` |集合 `xs` に要素 `x` を加え、以前に集合に含まれていなければ `true` を返し、既に含まれていれば `false` を返す。| +| **減算:** | | +| `xs -= x` |集合 `xs` から副作用として要素 `x` を削除して、`xs`自身を返す。| +| `xs -= (x, y, z)` |集合 `xs` から副作用として渡された要素を削除して、`xs`自身を返す。| +| `xs --= ys` |集合 `xs` から副作用として `ys`内の全ての要素を削除して、`xs`自身を返す。| +| `xs remove x` |集合 `xs` から要素 `x` を削除、以前に集合に含まれていれば `true` を返し、含まれていなければ `false` を返す。| +| `xs retain p` |`xs`内の要素で条件関数 `p` を満たすものだけを残す。| +| `xs.clear()` |`xs` から全ての要素を削除する。| +| **更新演算:** | | +| `xs(x) = b` |(展開した場合、`xs.update(x, b)`)。ブーリアン値の引数 `b` が `true` ならば `xs` に `x` を加え、それ以外なら `xs` から `x` を削除する。| +| **クローン演算:** | | +| `xs.clone` |`xs` と同じ要素を持つ新しい可変集合。| + +不変集合と同様に、可変集合も要素追加のための `+` と `++` 演算、および要素削除のための `-` と `--` 演算を提供する。しかし、これらは集合をコピーする必要があるため可変集合ではあまり使われることがない。可変集合はより効率的な `+=` と `-=` という更新方法を提供する。`s += elem` という演算は、集合 `s` に副作用として `elem` を集合に加え、変化した集合そのものを戻り値として返す。同様に、`s -= elem` は集合から `elem` を削除して、変化した集合を戻り値として返す。`+=` と `-=` の他にも、traversable やイテレータの全ての要素を追加または削除する一括演算である `++=` と `--=` がある。 + +メソッド名として `+=` や `-=` が選ばれていることによって、非常に似たコードが可変集合と不変集合のどちらでも動くことを意味する。不変集合 `s` を使った次の REPL のやりとりを見てほしい: + + scala> var s = Set(1, 2, 3) + s: scala.collection.immutable.Set[Int] = Set(1, 2, 3) + scala> s += 4 + scala> s -= 2 + scala> s + res2: scala.collection.immutable.Set[Int] = Set(1, 3, 4) + +ここでは `immutable.Set`型の `var` に対して `+=` と `-=` を使った。`s += 4` のようなステートメントは、`s = s + 4` の略だ。つまり、これは集合 `s` に対して追加メソッドの `+` を呼び出して、結果を変数`s` に代入しなおしてる。次に、可変集合でのやりとりを見てほしい。 + + scala> val s = collection.mutable.Set(1, 2, 3) + s: scala.collection.mutable.Set[Int] = Set(1, 2, 3) + scala> s += 4 + res3: s.type = Set(1, 4, 2, 3) + scala> s -= 2 + res4: s.type = Set(1, 4, 3) + +結果は前回のやりとりと非常に似通ったものになった: `Set(1, 2, 3)` から始めて、最後に `Set(1, 3, 4)` +を得た。ステートメントは前回と同じに見えるが、実際には違うことを行っている。`s` `+=` `4` は今度は可変集合 `s` の `+=` メソッドを呼び出し、その場で集合を上書きしているのだ。同様に、`s -= 2` は同じ集合の `-=` メソッドを呼び出している。 + +この2つのやりとりの比較から重要な原則を導き出せる。`val` に格納された可変コレクションと、`var` に格納された不変コレクションは、大抵の場合にお互いを置換できるということだ。これはコレクションに対して上書きで更新されたのか新たなコレクションが作成されたのかを第三者が観測できるような別名の参照がない限り成り立つ原則だ。 + +可変集合は `+=` と `-=` の別形として `add` と `remove` を提供する。違いは `add` と `remove` は集合に対して演算の効果があったかどうかを示す `Boolean` の戻り値を返すことだ。 + +現在の可変集合のデフォルトの実装では要素を格納するのにハッシュテーブルを使っている。不変集合のデフォルトの実装は集合の要素数に応じて方法を変えている。空集合はシングルトンで表される。サイズが4つまでの集合は全要素をフィールドとして持つオブジェクトとして表される。それを超えたサイズの不変集合は[ハッシュトライ](#hash-tries)として表される。 + +このような設計方針のため、(例えば 4以下の) 小さいサイズの集合を使う場合は、通常の場合、可変集合に比べて不変集合の方が、よりコンパクトで効率的だ。集合のサイズが小さいと思われる場合は、不変集合を試してみてはいかがだろうか。 + +集合のサブトレイトとして `SortedSet` と `BitSet` の2つがある。 + +### 整列済み集合 ### + +整列済み集合は ([`SortedSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/SortedSet.html)) +は (集合の作成時に指定された) 任意の順序で要素を (`iterator` や `foreach` を使って) 返す事ができる集合だ。[`SortedSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/SortedSet.html) クラスのデフォルトの表現は、左の子ツリー内の全ての要素が右の子ツリーの全ての要素よりも小さいという恒常条件を満たす順序付けされた二分木だ。これにより、通りがけ順 (in-order) で探索するだけで、木の全ての要素を昇順に返すことができる。Scala の[`immutable.TreeSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/TreeSet.html) クラスは **赤黒木** を使ってこの恒常条件を実装している。また、この木構造は、**平衡木**であり、ルートから全て葉のまでの長さの違いは最大で1要素しかない。 + +空の [`TreeSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/TreeSet.html) を作成するには、まず順序付けを指定する: + + scala> val myOrdering = Ordering.fromLessThan[String](_ > _) + myOrdering: scala.math.Ordering[String] = ... + +次に、その順序付けの空の木集合を作成するには: + + scala> TreeSet.empty(myOrdering) + res1: scala.collection.immutable.TreeSet[String] = TreeSet() + +順序付けの引数を省略して、空集合の要素型を指定することもできる。その場合は、要素型のデフォルトの順序付けが使われる。 + + scala> TreeSet.empty[String] + res2: scala.collection.immutable.TreeSet[String] = TreeSet() + +(例えば連結やフィルターによって) 新たな木集合を作成した場合、それは元の集合と同じ順序付けを保つ。たとえば、 + + scala> res2 + ("one", "two", "three", "four") + res3: scala.collection.immutable.TreeSet[String] = TreeSet(four, one, three, two) + +整列済み集合は要素の範囲もサポートする。例えば、`range` メソッドは開始要素以上、終了要素未満の全ての要素を返す。また、`from` メソッドは開始要素以上の全ての要素を、集合の順序付けで返す。両方のメソッドの戻り値もまた整列済み集合だ。用例: + + scala> res3 range ("one", "two") + res4: scala.collection.immutable.TreeSet[String] = TreeSet(one, three) + scala> res3 from "three" + res5: scala.collection.immutable.TreeSet[String] = TreeSet(three, two) + + +### ビット集合 ### + +ビット集合 ([`BitSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/BitSet.html)) は非負整数の要素の集合で、何ワードかのパックされたビットにより実装されている。[`BitSet`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/BitSet.html) クラスは、内部で `Long` の配列を用いている。最初の `Long` は第0 〜 63 の要素を受け持ち、次のは第64 〜 127 の要素という具合だ。全ての `Long` の、それぞれの 64ビットは、対応する要素が集合に含まれる場合は 1 にセットされ、含まれない場合は 0 になる。このため、ビット集合のサイズは格納されている整数の最大値に依存する。`N` がその最大の整数値の場合、集合のサイズは `N/64` `Long` ワード、または `N/8` バイト、にステータス情報のためのバイトを追加したものだ。 + +このため、たくさんの小さい要素を含む場合、ビット集合は他の集合に比べてコンパクトである。ビット集合のもう一つの利点は `contains` を使った所属判定や、`+=` や `-=` を使った要素の追加や削除が非常に効率的であることだ。 diff --git a/ja/overviews/collections/strings.md b/ja/overviews/collections/strings.md new file mode 100644 index 0000000000..4089a1c233 --- /dev/null +++ b/ja/overviews/collections/strings.md @@ -0,0 +1,28 @@ +--- +layout: overview-large +title: 文字列 + +disqus: true + +partof: collections +num: 11 +language: ja +--- + +配列と同様、文字列 (`String`) は直接には列ではないが、列に変換することができ、また、文字列は全ての列演算をサポートする。以下に文字列に対して呼び出すことができる演算の具体例を示す。 + + scala> val str = "hello" + str: java.lang.String = hello + scala> str.reverse + res6: String = olleh + scala> str.map(_.toUpper) + res7: String = HELLO + scala> str drop 3 + res8: String = lo + scala> str slice (1, 4) + res9: String = ell + scala> val s: Seq[Char] = str + s: Seq[Char] = WrappedString(h, e, l, l, o) + +これらの演算は二つの暗黙の変換により実現されている。例えば、上記の最後の行で文字列が `Seq` に変換されている所では優先度の低い `String` から `WrappedString` への変換が自動的に導入されている (`WrappedString` は +`immutable.IndexedSeq` の子クラスだ)。一方、`reverse`、`map`、`drop`、および `slice` メソッドの呼び出しでは優先度の高い `String` から `StringOps` への変換が自動的に導入されており、これは全ての不変列のメソッドを文字列に追加する。 diff --git a/ja/overviews/collections/trait-iterable.md b/ja/overviews/collections/trait-iterable.md new file mode 100644 index 0000000000..2711c481f5 --- /dev/null +++ b/ja/overviews/collections/trait-iterable.md @@ -0,0 +1,76 @@ +--- +layout: overview-large +title: Iterable トレイト + +disqus: true + +partof: collections +num: 4 +language: ja +--- + +反復可能 ([`Iterable`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterable.html) トレイトはコレクション階層の上から2番目に位置する。このトレイトの全メソッドは、コレクション内の要素を1つづつ返す抽象メソッド `iterator` に基づいている。`Iterable` では、`Traversable` トレイトの `foreach` メソッドも `iterator`に基づいて実装されている。以下が実際の実装だ: + + def foreach[U](f: Elem => U): Unit = { + val it = iterator + while (it.hasNext) f(it.next()) + } + +多くの `Iterable` のサブクラスは、より効率的な実装を提供するため、上の `foreach` の標準実装をオーバーライドしている。 `foreach` は `Traversable` の全ての演算の基となっているため、効率的であることが重要なのだ。 + +`Iterable` にはイテレータを返すもう2つのメソッドがある: `grouped` と `sliding` だ。これらのイテレータは単一の要素を返すのではなく、元のコレクションの部分列を返す。これらのメソッドに渡された引数がこの部分列の最大サイズとなる。`grouped` メソッドは要素を n 個づつの「かたまり」にして返すのに対し、 `sliding` は n 個の要素から成る「窓」をスライドさせて返す。この二つのメソッドの違いは REPL でのやりとりを見れば明らかになるはずだ。: + + scala> val xs = List(1, 2, 3, 4, 5) + xs: List[Int] = List(1, 2, 3, 4, 5) + scala> val git = xs grouped 3 + git: Iterator[List[Int]] = non-empty iterator + scala> git.next() + res3: List[Int] = List(1, 2, 3) + scala> git.next() + res4: List[Int] = List(4, 5) + scala> val sit = xs sliding 3 + sit: Iterator[List[Int]] = non-empty iterator + scala> sit.next() + res5: List[Int] = List(1, 2, 3) + scala> sit.next() + res6: List[Int] = List(2, 3, 4) + scala> sit.next() + res7: List[Int] = List(3, 4, 5) + +`Iterable` トレイトは、 `Traversable` からのメソッドの他に、イテレータがあることで効率的に実装することができる他のメソッドを追加する。それらのメソッドを以下の表にまとめる。 + +### Iterable トレイトの演算 + +| 使用例 | 振る舞い | +| ------ | ------ | +| **抽象メソッド:** | | +| `xs.iterator` |`xs`内の全ての要素を `foreach` が走査するのと同じ順序で返すイテレータ。| +| **他のイテレータ:**   | | +| `xs grouped size` |このコレクション内の要素を固定サイズの「かたまり」にして返すイテレータ。| +| `xs sliding size` |このコレクション内の要素を固定サイズの「窓」をスライドさせて返すイテレータ。| +| **サブコレクション取得演算:** | | +| `xs takeRight n` |`xs` の最後の `n` 個の要素から成るコレクション +(順序が定義されていない場合は、任意の `n` 個の要素から成るコレクション)。| +| `xs dropRight n` |コレクションから `xs` `takeRight` `n` +を除いた残りの部分。| +| **zip 演算:** | | +| `xs zip ys` |`xs` と `ys` のそれぞれから対応する要素をペアにした `Iterable`。| +| `xs zipAll (ys, x, y)` |`xs` と `ys` のそれぞれから対応する要素をペアにした +`Iterable` で、もし片方が短い場合は `x` か `y` を使って長いほうに合わせる。| +| `xs.zipWithIndex` |`xs`内の要素とその添字をペアにした `Iterable`。| +| **比較演算:**    | | +| `xs sameElements ys` |`xs` と `ys` が同じ要素を同じ順序で格納しているかを調べる。| + +継承階層では `Iterable` 直下に [`Seq`](seqs.html)、[`Set`](sets.html)、[`Map`](maps.html) という三つのトレイトがある。 +この三つのトレイトに共通することは `apply` メソッドと `isDefinedAt` メソッドを持ったトレイト [` +PartialFunction`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/PartialFunction.html) を実装しているということだ。 +しかし、`PartialFunction` の実装方法は三者三様である。 + +列は `apply` を位置的な添字として用いられており、要素は常に `0` +から数えられる。だから、`Seq(1, 2, 3)(1)` は `2` を返す。 +集合では `apply` は所属を調べるのに用いられる。 +例えば、`Set('a', 'b', 'c')('b')` は `true` を返し、`Set()('a')` は `false` を返す。 +最後に、マップでは `apply` は要素の選択に用いられている。 +例えば、`Map('a' -> 1, 'b' -> 10, 'c' -> 100)('b')` は `10` を返す。 + +次に、この3つのコレクションをより詳しく説明しよう。 diff --git a/ja/overviews/collections/trait-traversable.md b/ja/overviews/collections/trait-traversable.md new file mode 100644 index 0000000000..4c6e6c7409 --- /dev/null +++ b/ja/overviews/collections/trait-traversable.md @@ -0,0 +1,118 @@ +--- +layout: overview-large +title: Traversable トレイト + +disqus: true + +partof: collections +num: 3 +language: ja +--- + +走査可能 ([`Traversable`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Traversable.html))トレイトはコレクション階層の最上位に位置する。訳注: 木構造などでノードを一つづつ走査することを traverse と言う。また、-able で終わるトレイトは名詞としても使われるため、「走査可能なもの」という意味だ。 `Traversable` の抽象的な演算は `foreach` のみだ: + + def foreach[U](f: Elem => U) + +`Traverable` を実装するコレクションクラスは、このメソッドを定義するだけでいい。逆に言うと、その他全てのメソッドは `Traversable` から継承することができる。 + +`foreach` メソッドは、コレクション中の全ての要素を走査して、渡された演算 `f` を各々の要素に適用することを意図している。 +この演算の型は `Elem => U` であり、`Elem` はコレクションの要素の型で、`U` は任意の戻り値型だ。 `f` の呼び出しはそれに伴う副作用のためだけに行われ、`f` の戻り値の全ては `foreach` によって破棄される。 + +`Traversable` が定義する全ての具象メソッド (concrete method) を次の表に列挙した。これらのメソッドは次のカテゴリに分類される: + +* **加算**である `++` は、2つの `Traversable` を連結するか、あるイテレータが返す全ての要素を `Traversable` に追加する。 +* **map 演算**である`map`、`flatMap`、及び `collect` はコレクションの要素に何らかの関数を適用して新しいコレクションを生成する。 +* **変換演算**である `toArray`、`toList`、`toIterable`、`toSeq`、`toIndexedSeq`、`toStream`、`toSet`、`toMap` は `Traversable` なコレクションを別のより特定のものに変える。実行時のコレクション型が既に要求されているコレクション型と一致する場合、これらの全ての変換は引数をそのまま返す。例えば、リストに `toList` を適用した場合、リストそのものを返す。 +* **コピー演算** `copyToBuffer` と `copyToArray`。名前のとおり、これらの演算はコレクションの要素をバッファまたは配列にコピーする。 +* **サイズ演算** `isEmpty`、`nonEmpty`、`size`、および `hasDefiniteSize`。 `Traversable` なコレクションは有限または無限のサイズを取りうる。無限の `Traversable` コレクションの例としては自然数のストリームである `Stream.from(0)` がある。 +`hasDefiniteSize` メソッドはコレクションが無限である可能性があるかを示す。`hasDefiniteSize` が `true` を返す場合、コレクション確実に有限だ。`false` を返す場合、コレクションはまだ完全に展開されていないことを示し、それは無限か有限のどちらである可能性もある。 +* **要素取得演算** `head`、`last`、`headOption`、`lastOption`、および `find`。これらはコレクションの最初または最後の要素、または他の条件に一致する最初の要素を選択する。しかし、全てのコレクションにおいて「最初」と「最後」の意味が明確に定義されているわけではないことに注意してほしい。たとえば、ハッシュ集合はハッシュキーの並びで要素を格納するかもしれないが、ハッシュキーは実行するたびに変わる可能性がある。その場合、ハッシュ集合の「最初」の要素はプログラムを実行するたびに異なるかもしれない。 +あるコレクションから常に同じ順序で要素を得られる場合、そのコレクションは**順序付け** (ordered) されているという。 +ほとんどのコレクションは順序付けされているが、(ハッシュ集合など)いくつかののコレクションは順序付けされていない ― +順序付けを省くことで多少効率が上がるのだ。順序付けは再現性のあるテストを書くのに不可欠であり、デバッグの役に立つ。 +そのため Scala のコレクションは、全てのコレクション型に対して順序付けされた選択肢を用意してある。 +例えば、`HashSet` に代わる順次付けされたものは `LinkedHashSet` だ。 +* **サブコレクション取得演算** `tail`、`init`、`slice`、`take`、`drop`、`takeWhile`、`dropWhile`、`filter`、`filterNot`、`withFilter`。 +これら全ての演算は添字の範囲や何らかの条件関数によって識別されたサブコレクションを返す。 +* **分割演算**である `splitAt`、`span`、`partition`、`groupBy` の全てはコレクションの要素をいくつかのサブコレクションに分割する。 +* **要素条件演算**である`exists`、`forall`、`count` は与えられた条件関数を使ってコレクションをテストする。 +* **fold 演算**である `foldLeft`、`foldRight`、`/:`、`:\`、`reduceLeft`、`reduceRight` は次々と二項演算を隣接する要素に適用していく。 +* **特定 fold 演算**である `sum`、`product`、`min`、`max` は特定の型(numeric か comparable)のコレクションでのみ動作する。 +* **文字列演算**である `mkString`、`addString`、`stringPrefix` はコレクションを文字列に変換する方法を提供する。 +* **ビュー演算**はオーバーロードされた二つの `view` メソッドによって構成される。 + ビューは遅延評価されたコレクションだ。ビューについての詳細は[後ほど](views.html)。 + +### Traversableトレイトの演算 + +| 使用例 | 振る舞い | +| ------ | ------ | +| **抽象メソッド:** | | +| `xs foreach f` |`xs` 内の全ての要素に対して関数 `f` を実行する。 | +| **加算:** | | +| `xs ++ ys` |`xs` と `ys` の両方の要素から成るコレクション。 `ys` は [`TraversableOnce`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/TraversableOnce.html) なコレクション、つまり [`Traversable`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Traversable.html) または [`Iterator`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterator.html) だ。| +| **map 演算:** | | +| `xs map f` |`xs` 内の全ての要素に関数 `f` を適用することによって得られるコレクション。| +| `xs flatMap f` |`xs` 内の全ての要素に対してコレクション値を返す関数 `f` を適用し、その結果を連結したコレクション。| +| `xs collect f` |`xs` 内の全ての要素に対して部分関数 `f` が定義されている場合のみ適応し、その結果を集めたコレクション。| +| **変換演算:** | | +| `xs.toArray` |コレクションを配列に変換する。 | +| `xs.toList` |コレクションをリストに変換する。 | +| `xs.toIterable` |コレクションを `Iterable` に変換する。 | +| `xs.toSeq` |コレクションを列に変換する。 | +| `xs.toIndexedSeq` |コレクションを添字付き列に変換する。 | +| `xs.toStream` |コレクションを遅延評価されたストリームに変換する。 | +| `xs.toSet` |コレクションを集合に変換する。 | +| `xs.toMap` |キー/値のペアを持つコレクションをマップに変換する。コレクションが要素としてのペアを持たない場合、この演算を呼び出すと静的型エラーがおこる。| +| **コピー演算:** | | +| `xs copyToBuffer buf` |コレクション内の全ての要素をバッファ `buf` にコピーする。| +| `xs copyToArray(arr, s, n)`|最大 `n` 個のコレクションの要素を配列 `arr` の添字 `s` より始まる位置にコピーする。最後の2つの引数は省略可能だ。| +| **サイズ演算:** | | +| `xs.isEmpty` |コレクションが空であるかどうかを調べる。 | +| `xs.nonEmpty` |コレクションに要素が含まれているかを調べる。 | +| `xs.size` |コレクション内の要素の数。 | +| `xs.hasDefiniteSize` |`xs` が有限のサイズであることが明らかな場合 true を返す。| +| **要素取得演算:** | | +| `xs.head` |コレクションの最初の要素 (順序が定義されていない場合は、任意の要素)。| +| `xs.headOption` |`xs` の最初の要素のオプション値、または `xs` が空の場合 `None`。| +| `xs.last` |コレクションの最後の要素 (順序が定義されていない場合は、任意の要素)。| +| `xs.lastOption` |`xs` の最後の要素のオプション値、または `xs` が空の場合 `None`。| +| `xs find p` |`xs` の中で条件関数 `p` を満たす最初の要素のオプション値、または条件を満たす要素が無い場合 `None`。| +| **サブコレクション取得演算:** | | +| `xs.tail` |コレクションから `xs.head` を除いた残りの部分。 | +| `xs.init` |コレクションから `xs.last` を除いた残りの部分。 | +| `xs slice (from, to)` |`xs` の一部の添字範囲内 (`from` 以上 `to` 未満) にある要素から成るコレクション。 | +| `xs take n` |`xs` の最初の `n` 個の要素から成るコレクション (順序が定義されていない場合は、任意の `n` 個の要素から成るコレクション)。| +| `xs drop n` |コレクションから `xs take n` を除いた残りの部分。 | +| `xs takeWhile p` |`xs` 内の要素を最初から次々とみて、条件関数 `p` を満たす限りつないでいったコレクション。| +| `xs dropWhile p` |`xs` 内の要素を最初から次々とみて、条件関数 `p`を満たす限り除いていったコレクション。| +| `xs filter p` |`xs` 内の要素で条件関数 `p` を満たすものから成るコレクション。| +| `xs withFilter p` |このコレクションを非正格 (non-strict) に filter したもの。後続の `map`, `flatMap`, `foreach`, および `withFilter` への呼び出しは `xs` の要素のうち条件関数 `p` が true に評価されるもののみに適用される。| +| `xs filterNot p` |`xs` 内の要素で条件関数 `p` を満たさないものから成るコレクション。| +| **分割演算:** | | +| `xs splitAt n` |`xs` を `n` の位置で二分して `(xs take n, xs drop n)` と同値のコレクションのペアを返す。| +| `xs span p` |`xs` を条件関数 `p` に応じて二分して `(xs takeWhile p, xs.dropWhile p)` と同値のペアを返す。| +| `xs partition p` |`xs` を条件関数 `p` を満たすコレクションと満たさないものに二分して `(xs filter p, xs.filterNot p)` と同値のペアを返す。| +| `xs groupBy f` |`xs` を判別関数 `f` に応じてコレクションのマップに分割する。| +| **要素条件演算:** | | +| `xs forall p` |`xs` 内の全ての要素に条件関数 `p` が当てはまるかを示す Boolean 値。| +| `xs exists p` |`xs` 内に条件関数 `p` を満たす要素があるかどうかを示す Boolean 値。| +| `xs count p` |`xs` 内の要素で条件関数 `p` 満たすものの数。| +| **fold 演算:** | | +| `(z /: xs)(op)` |`z` から始めて、左から右へと `xs` 内の隣接する要素に二項演算 `op` を次々と適用したもの。| +| `(xs :\ z)(op)` |`z` から始めて、右から左へと `xs` 内の隣接する要素に二項演算 `op` を次々と適用したもの。| +| `xs.foldLeft(z)(op)` |`(z /: xs)(op)` に同じ。 | +| `xs.foldRight(z)(op)` |`(xs :\ z)(op)` に同じ。 | +| `xs reduceLeft op` |左から右へと空ではないコレクション `xs` 内の隣接する要素に二項演算 `op` を次々と適用したもの。| +| `xs reduceRight op` |右から左へと空ではないコレクション `xs` 内の隣接する要素に二項演算 `op` を次々と適用したもの。| +| **特定 fold 演算:** | | +| `xs.sum` |コレクション `xs` 内の数値要素の値の和。 | +| `xs.product` |コレクション `xs` 内の数値要素の値の積。 | +| `xs.min` |コレクション `xs` 内の順序付けされたの値の最小値。 | +| `xs.max` |コレクション `xs` 内の順序付けされたの値の最大値。 | +| **文字列演算:** | | +| `xs addString (b, start, sep, end)`|`xs` 内の要素を `sep` で区切った後、`start` と `end` で挟んだ文字列を `StringBuilder b` に追加する。 `start`, `sep`, `end` は全て省略可能。| +| `xs mkString (start, sep, end)`|`xs` 内の要素を `sep` で区切った後、`start` と `end` で挟んだ文字列に変換する。 `start`, `sep`, `end` は全て省略可能。| +| `xs.stringPrefix` |`xs.toString` で返される文字列の先頭にあるコレクション名。| +| **ビュー演算:** | | +| `xs.view` |`xs` に対するビューを生成する。 | +| `xs view (from, to)` |`xs` の一部の添字範囲内を表すビューを生成する。 | diff --git a/ja/overviews/collections/views.md b/ja/overviews/collections/views.md new file mode 100644 index 0000000000..ecfafc7c58 --- /dev/null +++ b/ja/overviews/collections/views.md @@ -0,0 +1,130 @@ +--- +layout: overview-large +title: ビュー + +disqus: true + +partof: collections +num: 14 +language: ja +--- + +コレクションには新たなコレクションを構築するメソッドがたくさんある。例えば `map`、`filter`、`++` などがある。これらのメソッドは1つ以上のコレクションをレシーバとして取り、戻り値として別のコレクションを生成するため**変換演算子** +(transformer) と呼ばれる。 + +変換演算子を実装するには主に二つの方法がある。**正格** (strict) 法は変換演算子の戻り値として全ての要素を含む新たなコレクションを返す。非正格法、もしくは**遅延** (lazy) 法と呼ばれる方法は、結果のコレクションの代理のみを構築して返し、実際の要素は必用に応じて構築される。 + +非正格な変換演算子の具体例として、以下の遅延 map 演算の実装を見てほしい: + + def lazyMap[T, U](coll: Iterable[T], f: T => U) = new Iterable[T] { + def iterator = coll.iterator map f + } + +`lazyMap` は、渡されたコレクション `coll` の全要素を総なめすることなく新しい `Iterable` を構築していることに注意してほしい。代わりに、渡された関数の `f` が新しいコレクションの `iterator` に必要に応じて適用される。 + +全ての変換演算子を遅延実装している `Stream` を除いて、Scala のコレクションは全ての変換演算子をデフォルトで正格法で実装している。しかし、コレクションのビューにより、体系的に全てのコレクションを遅延したものに変え、また逆に戻すことができる。**ビュー** (view) は特殊なコレクションの一種で、何らかのコレクションに基づいているが全ての変換演算子を遅延実装してる。 + +あるコレクションからそのビューへと移行するには、そのコレクションに対して `view` メソッドを呼び出す。`xs` というコレクションがあるとすると、`xs.view` は変換演算子が遅延実装されている以外は同一のコレクションだ。ビューから正格なコレクションに戻るには `force` メソッドを使う。 + +具体例を見てみよう。2つの関数を続けて写像したい `Int`型のベクトルがあるとする: + + scala> val v = Vector(1 to 10: _*) + v: scala.collection.immutable.Vector[Int] = + Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + scala> v map (_ + 1) map (_ * 2) + res5: scala.collection.immutable.Vector[Int] = + Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22) + +最後のステートメントにおいて、式 `v map (_ + 1)` は新たなベクトルを構築し、それは第2の `map (_ * 2)` +の呼び出しによって3つ目のベクトルに変換される。多くの状況において、最初の `map` への呼び出しで中間結果のためだけのベクトルが構築されるのは無駄にしかならない。上記の例では、`(_ + 1)` と `(_ * 2)` の2つの関数を合成して `map` を1回だけ実行したほうが速くなるだろう。両方の関数が1ヶ所にあるならば手で合成することも可能だろう。しかし、しばしばデータ構造への連続した変換はプログラム内の別々のモジュールによって実行される。もしそうならば、これらの変換を融合することはモジュール性を犠牲してしまう。中間結果を回避する、より汎用性のある方法は、ベクトルをまずビューに変え全ての変換をビューに適用した後、ビューからベクトルに逆変換することだ: + + scala> (v.view map (_ + 1) map (_ * 2)).force + res12: Seq[Int] = Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22) + +同じ演算の手順をもう1度ひとつひとつ見てみよう: + + scala> val vv = v.view + vv: scala.collection.SeqView[Int,Vector[Int]] = + SeqView(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + +`v.view` を適用することで遅延評価される `Seq` である `SeqView` が得られる。`SeqView` には2つの型パラメータがある。第1の `Int` はビューの要素の型を示す。第2の `Vector[Int]` は `view` +を逆変換する時の型コンストラクタを示す。 + +最初の `map` を適用することでビューは以下を返す: + + scala> vv map (_ + 1) + res13: scala.collection.SeqView[Int,Seq[_]] = SeqViewM(...) + +`map` の戻り値は、`SeqViewM(...)` と表示された値だ。この値は本質的に、ベクトル `v` に対して関数 `(_ + 1)` を使って `map` を適用する必要があるということを記録するラッパーだ。しかし、ビューが強制実行 (`force`) されるまでは map 演算は適用されない。`SeqView` の後ろに付けられた「M」は、ビューが `map` 演算を表すことを示す。他の文字は別の遅延された演算を示す。例えば、「S」は遅延した `slice` 演算を示し、「R」は遅延した `reverse` 演算を示す。先ほどの結果に、次の `map` を適用しよう。 + + scala> res13 map (_ * 2) + res14: scala.collection.SeqView[Int,Seq[_]] = SeqViewMM(...) + +今度は2回の map 演算を含む `SeqView` が得られるため、「M」も二回表示される: `SeqViewMM(...)`。 最後に、先ほどの結果を逆変換すると: + + scala> res14.force + res15: Seq[Int] = Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22) + +格納されていた両方の関数は強制実行 (`force`) の一部として適用され、新しいベクトルが構築される。これにより、中間結果のデータ構造は必要なくなった。 + +1つ注意してほしいのは、最終結果の静的型が `Vector` ではなく `Seq` であるということだ。 型をさかのぼってみると、最初の遅延 map が適用された時点で既に戻り値の静的型が `SeqViewM[Int, Seq[_]]` 型であったことが分かる。つまり、ビューが特定の列型である `Vector` に適応されたという「知識」が失われたということだ。ビューの実装には多量のコードを要するものがあるため、Scala コレクションライブラリは汎用コレクション型にのみビューを提供するが、特定の実装にはビューは提供されないの。 + +ビューを使うことを考慮する二つの理由がある。第一は、パフォーマンスだ。コレクションをビューに切り替えることで中間結果の構築を避けれることを既に説明した。このような節約は時として大切な結果をもたらす。もう一つの具体例として、単語のリストから回文を探す問題を考える。回文とは前後のどちらから読んでも同じ語句だ。必要な定義を以下に示す: + + def isPalindrome(x: String) = x == x.reverse + def findPalidrome(s: Seq[String]) = s find isPalindrome + +ここで非常に長い `words` という列があり、列の最初の百万語の中から単一の回文を探したいと仮定する。`findPalidrome` の定義を再利用できるだろうか。当然、以下のように書くことはできる: + + findPalindrome(words take 1000000) + +これは、列の中から最初の百万語を選択するのと、単一の回文を探すという二つの側面をきれいに分担する。しかし、たとえ列の最初の語が回文であったとしても、百万語から成る中間結果の列を常に構築してしまうという欠点がある。つまり、999999語が中間結果にコピーされ、その後検査もされないということが起こりえる。ここで多くのプログラマは諦めて、先頭 n個の部分列に対して検索を行う特殊なバージョンの回文検索を書いてしまう。ビューがあれば、あなたはそんな事をしなくても済む。こう書けばいいからだ: + + findPalindrome(words.view take 1000000) + +関心事の分業を保ちつつも、これは百万要素の列の代わりに軽量なビューオブジェクトのみを構築する。これにより、パフォーマンスとモジュール性の択一をしなくても済む。 + +次の事例として、可変列に対するビューを見てみたい。可変列のビューにいくつかの変換関数を適用することで、元の列内の要素を選択的に更新する制限枠として機能することができる。ここに配列 `arr` があると仮定する: + + scala> val arr = (0 to 9).toArray + arr: Array[Int] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + +その配列への制限枠を作るのに、`arr` のビューのスライスを作ることができる: + + scala> val subarr = arr.view.slice(3, 6) + subarr: scala.collection.mutable.IndexedSeqView[ + Int,Array[Int]] = IndexedSeqViewS(...) + +これにより、配列 `arr` の 3〜5 の位置を参照するビュー `subarr` が得られる。ビューは要素をコピーせず、参照のみを提供する。ここで、列の要素を変更するメソッドがあると仮定する。例えば、以下の `negate` メソッドは、与えれれた整数列の全ての要素の符号を反転する: + + scala> def negate(xs: collection.mutable.Seq[Int]) = + for (i <- 0 until xs.length) xs(i) = -xs(i) + negate: (xs: scala.collection.mutable.Seq[Int])Unit + +配列 `arr` の 3〜5 の位置の要素の符号を反転したいとする。これに `negate` が使えるだろうか。 ビューを使えば、簡単にできる: + + scala> negate(subarr) + scala> arr + res4: Array[Int] = Array(0, 1, 2, -3, -4, -5, 6, 7, 8, 9) + +何が起こったかと言うと、`negate` は `subarr`内の全ての要素を変更したが、実際にはそれは `arr` +の要素のスライスだったというわけだ。ここでも、ビューがモジュール性を保つのに役立っているのが分かるだろう。上記のコードは、メソッドをどの添字の範囲に適用するのかという問題と、どのメソッドを適用するのかという問題を見事に切り離している。 + +これだけ粋なビューの使用例を見た後だと、なぜ正格コレクションがあるのか疑問に思うかもしれない。理由の一つとして、性能を比較すると遅延コレクションが常に正格コレクションに勝るとは限らないというものがある。コレクションのサイズが小さい場合、ビュー内でクロージャを作成し適用するためのオーバーヘッドが中間結果のためのデータ構造を回避するコストを上回ってしまうことが多いのだ。恐らくより重要な理由として、遅延した演算が副作用を伴う場合、ビューの評価が非常に混乱したものとなってしまうというものがある。 + +Scala 2.8 以前で多くのユーザが陥った失敗例を以下に示す。これらのバージョンでは `Range` 型が遅延評価されたため、実質的にビューのように振舞った。多くの人が以下のようにして複数の actor を作成しようとした: + + val actors = for (i <- 1 to 10) yield actor { ... } + +`actor` メソッドと後続の中括弧に囲まれたコードは actor を作成し始動するべきだが、驚いた事にどの actor も実行されていなかった。なぜ何も起こらなかったのかは、for 式が `map` の適用と等価であることを思い出せば説明がつく: + + val actors = (1 to 10) map (i => actor { ... }) + +`(1 to 10)` により生成された範囲はビューのように振舞ったため、`map` の戻り値もビューとなった。つまり、どの要素も計算されず、結果的にどの actor も作成されなかったのだ! 範囲の式全体を強制実行すれば actor は作成されただろうが、actor +を作動させるためにそれが必要なことは全く明白では無い。 + +このような不意打ちを避けるため、Scala 2.8 ではより規則的なルールを採用する。ストリームを除く、全てのコレクションは正格評価される。正格コレクションから遅延コレクションに移行する唯一の方法は `view` メソッドによる。戻る唯一の方法は `force` による。よって、Scala 2.8 では先程の `actors` の定義は期待した通りに 10個の actor を作成し始動する。以前のような、予期しない振る舞いをさせるには、明示的に `view` メソッドを呼び出す必要がある: + + val actors = for (i <- (1 to 10).view) yield actor { ... } + +まとめると、ビューは効率性の問題とモジュール性の問題を仲裁する強力な道具だ。しかし、遅延評価の面倒に巻き込まれないためには、ビューの使用は二つのシナリオに限るべきだ。一つは、ビューの適用を副作用を伴わない純粋関数型のコードに限ること。もしくは、明示的に全ての変更が行われる可変コレクションに適用することだ。避けたほうがいいのは、ビューと新たなコレクションを作成しつつ副作用を伴う演算を混合することだ。 diff --git a/ja/overviews/core/collections.md b/ja/overviews/core/collections.md new file mode 100644 index 0000000000..15219520aa --- /dev/null +++ b/ja/overviews/core/collections.md @@ -0,0 +1,7 @@ +--- +layout: overview +overview: collections +partof: collections +language: ja +title: Scala コレクションライブラリ +--- diff --git a/overviews/collections/arrays.md b/overviews/collections/arrays.md index 5959f844bc..ff8227b3c2 100644 --- a/overviews/collections/arrays.md +++ b/overviews/collections/arrays.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 10 +languages: [ja] --- [Array](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/Array.html) is a special kind of collection in Scala. On the one hand, Scala arrays correspond one-to-one to Java arrays. That is, a Scala array `Array[Int]` is represented as a Java `int[]`, an `Array[Double]` is represented as a Java `double[]` and a `Array[String]` is represented as a `Java String[]`. But at the same time, Scala arrays offer much more than their Java analogues. First, Scala arrays can be _generic_. That is, you can have an `Array[T]`, where `T` is a type parameter or abstract type. Second, Scala arrays are compatible with Scala sequences - you can pass an `Array[T]` where a `Seq[T]` is required. Finally, Scala arrays also support all sequence operations. Here's an example of this in action: diff --git a/overviews/collections/concrete-immutable-collection-classes.md b/overviews/collections/concrete-immutable-collection-classes.md index e829765a1a..aec20b9c91 100644 --- a/overviews/collections/concrete-immutable-collection-classes.md +++ b/overviews/collections/concrete-immutable-collection-classes.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 8 +languages: [ja] --- Scala provides many concrete immutable collection classes for you to choose from. They differ in the traits they implement (maps, sets, sequences), whether they can be infinite, and the speed of various operations. Here are some of the most common immutable collection types used in Scala. diff --git a/overviews/collections/concrete-mutable-collection-classes.md b/overviews/collections/concrete-mutable-collection-classes.md index bb98579650..e60459b334 100644 --- a/overviews/collections/concrete-mutable-collection-classes.md +++ b/overviews/collections/concrete-mutable-collection-classes.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 9 +languages: [ja] --- You've now seen the most commonly used immutable collection classes that Scala provides in its standard library. Take a look now at the mutable collection classes. diff --git a/overviews/collections/conversions-between-java-and-scala-collections.md b/overviews/collections/conversions-between-java-and-scala-collections.md index 9a7098f575..1517d31075 100644 --- a/overviews/collections/conversions-between-java-and-scala-collections.md +++ b/overviews/collections/conversions-between-java-and-scala-collections.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 17 +languages: [ja] --- Like Scala, Java also has a rich collections library. There are many similarities between the two. For instance, both libraries know iterators, iterables, sets, maps, and sequences. But there are also important differences. In particular, the Scala libraries put much more emphasis on immutable collections, and provide many more operations that transform a collection into a new one. diff --git a/overviews/collections/creating-collections-from-scratch.md b/overviews/collections/creating-collections-from-scratch.md index 24e8425e24..88e29af8b9 100644 --- a/overviews/collections/creating-collections-from-scratch.md +++ b/overviews/collections/creating-collections-from-scratch.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 16 +languages: [ja] --- You have syntax `List(1, 2, 3)` to create a list of three integers and `Map('A' -> 1, 'C' -> 2)` to create a map with two bindings. This is actually a universal feature of Scala collections. You can take any collection name and follow it by a list of elements in parentheses. The result will be a new collection with the given elements. Here are some more examples: diff --git a/overviews/collections/equality.md b/overviews/collections/equality.md index f080f009b3..8d36b26fa6 100644 --- a/overviews/collections/equality.md +++ b/overviews/collections/equality.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 13 +languages: [ja] --- The collection libraries have a uniform approach to equality and hashing. The idea is, first, to divide collections into sets, maps, and sequences. Collections in different categories are always unequal. For instance, `Set(1, 2, 3)` is unequal to `List(1, 2, 3)` even though they contain the same elements. On the other hand, within the same category, collections are equal if and only if they have the same elements (for sequences: the same elements in the same order). For example, `List(1, 2, 3) == Vector(1, 2, 3)`, and `HashSet(1, 2) == TreeSet(2, 1)`. diff --git a/overviews/collections/introduction.md b/overviews/collections/introduction.md index d087b307fd..ae57e0167f 100644 --- a/overviews/collections/introduction.md +++ b/overviews/collections/introduction.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 1 +languages: [ja] --- **Martin Odersky, and Lex Spoon** diff --git a/overviews/collections/iterators.md b/overviews/collections/iterators.md index e6229528c1..f5b8117e9b 100644 --- a/overviews/collections/iterators.md +++ b/overviews/collections/iterators.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 15 +languages: [ja] --- An iterator is not a collection, but rather a way to access the elements of a collection one by one. The two basic operations on an iterator `it` are `next` and `hasNext`. A call to `it.next()` will return the next element of the iterator and advance the state of the iterator. Calling `next` again on the same iterator will then yield the element one beyond the one returned previously. If there are no more elements to return, a call to `next` will throw a `NoSuchElementException`. You can find out whether there are more elements to return using [Iterator](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/Iterator.html)'s `hasNext` method. diff --git a/overviews/collections/maps.md b/overviews/collections/maps.md index 67842e5fa5..24d53a937b 100644 --- a/overviews/collections/maps.md +++ b/overviews/collections/maps.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 7 +languages: [ja] --- A [Map](http://www.scala-lang.org/api/current/scala/collection/Map.html) is an [Iterable](http://www.scala-lang.org/api/current/scala/collection/Iterable.html) consisting of pairs of keys and values (also named _mappings_ or _associations_). Scala's [Predef](http://www.scala-lang.org/api/current/scala/Predef$.html) class offers an implicit conversion that lets you write `key -> value` as an alternate syntax for the pair `(key, value)`. For instance `Map("x" -> 24, "y" -> 25, "z" -> 26)` means exactly the same as `Map(("x", 24), ("y", 25), ("z", 26))`, but reads better. diff --git a/overviews/collections/migrating-from-scala-27.md b/overviews/collections/migrating-from-scala-27.md index f3c366f561..b37fd5e2c5 100644 --- a/overviews/collections/migrating-from-scala-27.md +++ b/overviews/collections/migrating-from-scala-27.md @@ -7,6 +7,7 @@ disqus: true partof: collections num: 18 outof: 18 +languages: [ja] --- Porting your existing Scala applications to use the new collections should be almost automatic. There are only a couple of possible issues to take care of. diff --git a/overviews/collections/overview.md b/overviews/collections/overview.md index a39b2c5d3b..bb18ec2b17 100644 --- a/overviews/collections/overview.md +++ b/overviews/collections/overview.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 2 +languages: [ja] --- Scala collections systematically distinguish between mutable and diff --git a/overviews/collections/performance-characteristics.md b/overviews/collections/performance-characteristics.md index 3f59ec9657..057a5b8155 100644 --- a/overviews/collections/performance-characteristics.md +++ b/overviews/collections/performance-characteristics.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 12 +languages: [ja] --- The previous explanations have made it clear that different collection types have different performance characteristics. That's often the primary reason for picking one collection type over another. You can see the performance characteristics of some common operations on collections summarized in the following two tables. diff --git a/overviews/collections/seqs.md b/overviews/collections/seqs.md index f89e3eba66..0187b91106 100644 --- a/overviews/collections/seqs.md +++ b/overviews/collections/seqs.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 5 +languages: [ja] --- The [Seq](http://www.scala-lang.org/api/current/scala/collection/Seq.html) trait represents sequences. A sequence is a kind of iterable that has a `length` and whose elements have fixed index positions, starting from `0`. diff --git a/overviews/collections/sets.md b/overviews/collections/sets.md index 10199ade41..283a4dece6 100644 --- a/overviews/collections/sets.md +++ b/overviews/collections/sets.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 6 +languages: [ja] --- `Set`s are `Iterable`s that contain no duplicate elements. The operations on sets are summarized in the following table for general sets and in the table after that for mutable sets. They fall into the following categories: diff --git a/overviews/collections/strings.md b/overviews/collections/strings.md index e36031fd83..68a57ecc3b 100644 --- a/overviews/collections/strings.md +++ b/overviews/collections/strings.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 11 +languages: [ja] --- Like arrays, strings are not directly sequences, but they can be converted to them, and they also support all sequence operations on strings. Here are some examples of operations you can invoke on strings. diff --git a/overviews/collections/trait-iterable.md b/overviews/collections/trait-iterable.md index 9d3d3c26d5..0fb7ab53b2 100644 --- a/overviews/collections/trait-iterable.md +++ b/overviews/collections/trait-iterable.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 4 +languages: [ja] --- The next trait from the top in the collections hierarchy is `Iterable`. All methods in this trait are defined in terms of an an abstract method, `iterator`, which yields the collection's elements one by one. The `foreach` method from trait `Traversable` is implemented in `Iterable` in terms of `iterator`. Here is the actual implementation: diff --git a/overviews/collections/trait-traversable.md b/overviews/collections/trait-traversable.md index dc7abe31b3..09ae378b4b 100644 --- a/overviews/collections/trait-traversable.md +++ b/overviews/collections/trait-traversable.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 3 +languages: [ja] --- At the top of the collection hierarchy is trait `Traversable`. Its only abstract operation is `foreach`: diff --git a/overviews/collections/views.md b/overviews/collections/views.md index 727b4818cf..8304d0419d 100644 --- a/overviews/collections/views.md +++ b/overviews/collections/views.md @@ -6,6 +6,7 @@ disqus: true partof: collections num: 14 +languages: [ja] --- Collections have quite a few methods that construct new collections. Examples are `map`, `filter` or `++`. We call such methods transformers because they take at least one collection as their receiver object and produce another collection in their result.