Example8.1

「Example8.1」の編集履歴(バックアップ)一覧はこちら

Example8.1 - (2009/08/14 (金) 10:23:38) の最新版との変更点

追加された行は緑色になります。

削除された行は赤色になります。

** 8.1 Type Parameter Bounds Now that we know how to make classes generic it is natural to generalize some of the earlier classes we have written. For instance class IntSet could be generalized to sets with arbitrary element types. Let’s try. The abstract class for generic sets is easily written. クラスをジェネリックにする方法を知った今では、前に書いたクラスの中には一般化した方が自然なものがあります。例えばクラス IntSet は任意の要素型を設定出来る様に一般化出来ます。やってみましょう。ジェネリックな集合の為の抽象クラスは簡単に書く事が出来ます。 abstract class Set[A] { def incl(x: A): Set[A] def contains(x: A): Boolean } However, if we still want to implement sets as binary search trees, we encounter a problem. The contains and incl methods both compare elements using methods < and >. For IntSet this was OK, since type Int has these two methods. But for an arbitrary type parameter a, we cannot guarantee this. Therefore, the previous implementation of, say, contains would generate a compiler error. しかしもし二分木として実装し続けたいならば問題に直面します。contains と incl のメソッドはどちらも要素をメソッド < と > を用いて比較します。IntSet に対してこれは OK です。なぜなら Int はそれら二つのメソッドを持っているからです。しかし任意の型仮引数 A に対してこれを保証する事は出来ません。従って先の実装、例えば contains ではコンパイルエラーを生じるでしょう。 def contains(x: Int): Boolean = if (x < elem) left contains x ^ < not a member of type A. One way to solve the problem is to restrict the legal types that can be substituted for type A to only those types that contain methods < and > of the correct types. There is a trait Ordered[A] in the standard class library Scala which represents values which are comparable (via < and >) to values of type A. This trait is defined as follows: 問題を解決する一つの方法は型 A と置き換え可能な正当な型を正しい型のメソッド < と > を持つ型だけに制限することです。Scala 標準ライブラリには型 A の値と (< と > によって) 比較可能な値を表現するトレイト Ordered[A] があます。このトレイトは下記の様に定義されています。 /** A class for totally ordered data. 全順序データの為のクラス */ trait Ordered[A] { /** Result of comparing ‘this’ with operand ‘that’. * this' をオペランド 'that' と比較した結果。 * returns ‘x’ where * x < 0 iff this < that * x == 0 iff this == that * x > 0 iff this > that */ def compare(that: A): Int def < (that: A): Boolean = (this compare that) < 0 def > (that: A): Boolean = (this compare that) > 0 def <= (that: A): Boolean = (this compare that) <= 0 def >= (that: A): Boolean = (this compare that) >= 0 def compareTo(that: A): Int = compare(that) } We can enforce the comparability of a type by demanding that the type is a subtype of Ordered. This is done by giving an upper bound to the type parameter of Set: 型が Ordered のサブタイプであることを要求する事によって互換性を強制する事が出来ます。これは Set の型仮引数に上界を与える事でなされます。 trait Set[A <: Ordered[A]] { def incl(x: A): Set[A] def contains(x: A): Boolean } The parameter declaration A <: Ordered[A] introduces A as a type parameter which must be a subtype of Ordered[A], i.e. its values must be comparable to values of the same type. 仮引数宣言 A <: Ordered[A] は A を Ordered[A] のサブタイプで無くてはならない型仮引数として導入します。すなわち、その値は同じ型の値と比較可能でなくてはなりません。 With this restriction, we can now implement the rest of the generic set abstraction as we did in the case of IntSets before. この制限によってジェネリックな集合の抽象化を前に IntSet の場合と同様に実装出来ます。 class EmptySet[A <: Ordered[A]] extends Set[A] { def contains(x: A): Boolean = false def incl(x: A): Set[A] = new NonEmptySet(x, new EmptySet[A], new EmptySet[A]) } class NonEmptySet[A <: Ordered[A]] (elem: A, left: Set[A], right: Set[A]) extends Set[A] { def contains(x: A): Boolean = if (x < elem) left contains x else if (x > elem) right contains x else true def incl(x: A): Set[A] = if (x < elem) new NonEmptySet(elem, left incl x, right) else if (x > elem) new NonEmptySet(elem, left, right incl x) else this } Note that we have left out the type argument in the object creations new NonEmptySet(...). In the same way as for polymorphic methods, missing type arguments in constructor calls are inferred from value arguments and/or the expected result type. オブジェクト生成 new NonEmptySet(...) で型引数を除いている事に注目して下さい。多相型メソッドと同様にコンストラクタ呼び出しの存在しない型引数は値引数と期待される戻り型より推論されます。 Here is an example that uses the generic set abstraction. Let’s first create a subclass of Ordered, like this: 以下がジェネリックな集合の抽象化を使用する例です。最初に Ordered のサブクラスを下記の様に作りましょう。 case class Num(value: Double) extends Ordered[Num] { def compare(that: Num): Int = if (this.value < that.value) -1 else if (this.value > that.value) 1 else 0 } Then: すると val s = new EmptySet[Num].incl(Num(1.0)).incl(Num(2.0)) s.contains(Num(1.5)) This is OK, as type Num implements the trait Ordered[Num]. However, the following example is in error. これは OK です。なぜなら型 Num はトレイト Ordered[Num] を実装しているからです。しかし次の例はエラーになります。 val s = new EmptySet[java.io.File] ^ java.io.File does not conform to type parameter bound Ordered[java.io.File]. One probem with type parameter bounds is that they require forethought: if we had not declared Num a subclass of Ordered, we would not have been able to use Num elements in sets. By the same token, types inherited from Java, such as Int, Double, or String are not subclasses of Ordered, so values of these types cannot be used as set elements. 型仮引数境界 (? type parameter bound) の問題は予め準備する必要があることです。もし Num を Ordered のサブクラスとして宣言していなければ Num を集合の要素として使用出来ません。同様に Java から継承した Int, Double, String といった型は Ordered のサブクラスでは無く、それらの型の値は集合の要素として使用出来ません。 A more flexible design, which admits elements of these types, uses view bounds instead of the plain type bounds we have seen so far. The only change this entails in the example above is in the type parameters: それらの型を要素として許すもっと柔軟なデザインでは「view bound」を今までに見てきた単純な type bound の代わりに使用します。前述の例のこれによって起こる変更は単に型仮引数が次の様になることです。 trait Set[A <% Ordered[A]] ... class EmptySet[A <% Ordered[A]] ... class NonEmptySet[A <% Ordered[A]] ... View bounds <% are weaker than plain bounds <:: A view bounded type parameter clause [A <% T] only specifies that the bounded type A must be convertible to the bound type T, using an implicit conversion. View bound <% は通常の bound <: より弱いです。view bound 型仮引数節 [A <% T] は単に bounded type A が bound type T に暗黙の変換を用いて「変換可能」であることだけを限定します。 The Scala library predefines implicit conversions for a number of types, including the primitive types and String. Therefore, the redesign set abstraction can be instantiated with these types as well. More explanations on implicit conversions and view bounds are given in Section 15. Scala ライブラリはプリミティブ型や String を含む数多くの型に対する暗黙の変換を予め定義しています。従って再デザインされた集合の抽象化はこれらの型に対してもインスタンス化可能です。暗黙の変換と view bound に関する更なる説明は 15 章にあります。 ---- - bound, type bound, view bound の良い訳語が判らない...。 -- tmiya (2008-05-19 00:34:40) - 束縛、っていっちゃうとやっぱまずいですかね。「縛り」の方がまし? -- a (2009-07-25 03:51:29) #comment
#co(){ 8.1 Type Parameter Bounds Now that we know how to make classes generic it is natural to generalize some of the earlier classes we have written. For instance class IntSet could be generalized to sets with arbitrary element types. Let's try. The abstract class for generic sets is easily written. } #setmenu2(ex-r-menu) ** 8.1 型パラメータの境界 クラスをジェネリックにする方法を知った今では、前に書いたクラスの中には一般化した方が自然なものがあります。たとえばクラス IntSet は、任意の要素型を持てるように一般化できます。やってみましょう。ジェネリックな集合の抽象クラスは簡単に書けます。 abstract class Set[A] { def incl(x: A): Set[A] def contains(x: A): Boolean } #co(){ However, if we still want to implement sets as binary search trees, we encounter a problem. The contains and incl methods both compare elements using methods < and >. For IntSet this was OK, since type Int has these two methods. But for an arbitrary type parameter a, we cannot guarantee this. Therefore, the previous implementation of, say, contains would generate a compiler error. } しかしもし二分木として実装し続けたいなら、問題に直面します。メソッド contains と incl はどちらも、要素をメソッド < と > を使って比較します。IntSet については、これは OK です。なぜなら Int はそれら2つのメソッドを持っているからです。しかし任意の型パラメータ a に対しては、それは保証できません。したがって先の実装、たとえば contains ではコンパイルエラーが生じるでしょう。 def contains(x: Int): Boolean = if (x < elem) left contains x ^ < not a member of type A. #co(){ One way to solve the problem is to restrict the legal types that can be substituted for type A to only those types that contain methods < and > of the correct types. There is a trait Ordered[A] in the standard class library Scala which represents values which are comparable (via < and >) to values of type A. This trait is defined as follows: } 問題を解決する一つの方法は、型 A と置き換え可能な正当な型を、正しい型のメソッド < と > を持つ型だけに制限することです。Scala 標準ライブラリには、型 A の値と (< と > によって) 比較可能な値を表現する、トレイト Ordered[A] があります。このトレイトは次のように定義されています。 /** データを完全に順序づけるためのクラス */ trait Ordered[A] { /** this をオペランド that と比較した結果。 * returns 'x' where * x < 0 iff this < that * x == 0 iff this == that * x > 0 iff this > that */ def compare(that: A): Int def < (that: A): Boolean = (this compare that) < 0 def > (that: A): Boolean = (this compare that) > 0 def <= (that: A): Boolean = (this compare that) <= 0 def >= (that: A): Boolean = (this compare that) >= 0 def compareTo(that: A): Int = compare(that) } #co(){ We can enforce the comparability of a type by demanding that the type is a subtype of Ordered. This is done by giving an upper bound to the type parameter of Set: } 型が Ordered のサブタイプであることを要求することで、互換性を強制できます。これは Set の型パラメータに上界境界(upper bound)を与えることでなされます。 trait Set[A <: Ordered[A]] { def incl(x: A): Set[A] def contains(x: A): Boolean } #co(){ The parameter declaration A <: Ordered[A] introduces A as a type parameter which must be a subtype of Ordered[A], i.e. its values must be comparable to values of the same type. } パラメータ宣言 A <: Ordered[A] は型パラメータとしての A を、A は Ordered[A] のサブタイプでなくてはならない、として導入します。すなわち、その値は同じ型の値と比較可能でなくてはなりません。 #co(){ With this restriction, we can now implement the rest of the generic set abstraction as we did in the case of IntSets before. } この制限によって、ジェネリックな集合の抽象化を、前の IntSet の場合と同じように実装できます。 class EmptySet[A <: Ordered[A]] extends Set[A] { def contains(x: A): Boolean = false def incl(x: A): Set[A] = new NonEmptySet(x, new EmptySet[A], new EmptySet[A]) } class NonEmptySet[A <: Ordered[A]] (elem: A, left: Set[A], right: Set[A]) extends Set[A] { def contains(x: A): Boolean = if (x < elem) left contains x else if (x > elem) right contains x else true def incl(x: A): Set[A] = if (x < elem) new NonEmptySet(elem, left incl x, right) else if (x > elem) new NonEmptySet(elem, left, right incl x) else this } #co(){ Note that we have left out the type argument in the object creations new NonEmptySet(...). In the same way as for polymorphic methods, missing type arguments in constructor calls are inferred from value arguments and/or the expected result type. } オブジェクト生成 new NonEmptySet(...) において、型引数を書いていないことに注意して下さい。多相的メソッドと同様に、コンストラクタ呼び出しで型引数が書かれていない時、それは値引数と期待される結果型(戻り値型)から推論されます。 #co(){ Here is an example that uses the generic set abstraction. Let's first create a subclass of Ordered, like this: } 以下はジェネリックな集合の抽象化を使う例です。まづ、Ordered のサブクラスを次のように作りましょう。 case class Num(value: Double) extends Ordered[Num] { def compare(that: Num): Int = if (this.value < that.value) -1 else if (this.value > that.value) 1 else 0 } #co(){ Then: } すると val s = new EmptySet[Num].incl(Num(1.0)).incl(Num(2.0)) s.contains(Num(1.5)) #co(){ This is OK, as type Num implements the trait Ordered[Num]. However, the following example is in error. } これは OK です。なぜなら型 Num はトレイト Ordered[Num] を実装しているからです。しかし次の例はエラーになります。 val s = new EmptySet[java.io.File] ^ java.io.File does not conform to type parameter bound Ordered[java.io.File]. #co(){ One probem with type parameter bounds is that they require forethought: if we had not declared Num a subclass of Ordered, we would not have been able to use Num elements in sets. By the same token, types inherited from Java, such as Int, Double, or String are not subclasses of Ordered, so values of these types cannot be used as set elements. } 型パラメータ境界の問題は、前もって考えておくべきことです。もし Num を Ordered のサブクラスとして宣言していなければ、Num を集合の要素として使用できません。同様に、Java から継承した Int、Double、String といった型は Ordered のサブクラスではないので、それらの型の値は集合の要素として使用できません。 #co(){ A more flexible design, which admits elements of these types, uses view bounds instead of the plain type bounds we have seen so far. The only change this entails in the example above is in the type parameters: } それらの型を要素として許すもっと柔軟なデザインがあります。&bold(){可視境界}(view bound)を今までに見てきた単純な型境界の代わりに使うことです。先の例でこれによって起こる変更は、単に型パラメータが次のようになることです。 trait Set[A <% Ordered[A]] ... class EmptySet[A <% Ordered[A]] ... class NonEmptySet[A <% Ordered[A]] ... #co(){ View bounds <% are weaker than plain bounds <:: A view bounded type parameter clause [A <% T] only specifies that the bounded type A must be convertible to the bound type T, using an implicit conversion. } 可視境界 <% は通常の上限境界 <: より弱いです。可視境界をもつ型パラメータ節 [A <% T] は単に、境界づけられる型 A が境界となる型 T へ、暗黙の型変換を使って&bold(){変換可能}なことだけを指定します。 #co(){ The Scala library predefines implicit conversions for a number of types, including the primitive types and String. Therefore, the redesign set abstraction can be instantiated with these types as well. More explanations on implicit conversions and view bounds are given in Section 15. } Scala ライブラリは、プリミティブ型や String を含む数多くの型について、暗黙の型変換を事前に定義しています。したがって、再デザインされた集合の抽象化は、これらの型についてもインスタンス化できます。暗黙の変換と可視境界に関する更なる説明は第 15 章にあります。 #center(){[[前ページ>Chapter 8 Generic Types and Methods]] [[ 8 章>Chapter 8 Generic Types and Methods]] [[目次>ScalaByExample和訳]] [[次ページ>Example8.2]]} ---- - bound, type bound, view bound の良い訳語が判らない...。 -- tmiya (2008-05-19 00:34:40) - 束縛、っていっちゃうとやっぱまずいですかね。「縛り」の方がまし? -- a (2009-07-25 03:51:29) #comment

表示オプション

横に並べて表示:
変化行の前後のみ表示:
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。