ExampleChap6

「ExampleChap6」の編集履歴(バックアップ)一覧に戻る
ExampleChap6」を以下のとおり復元します。
#co(){
Chapter 6 Classes and Objects

Scala does not have a built-in type of rational numbers, but it is easy to define one, using a class. Here's a possible implementation. 
}

* 第 6 章 クラスとオブジェクト

Scala には組み込みの有理数型はありませんが、クラスを使って簡単に定義できます。次が実装例です。

 class Rational(n: Int, d: Int) { 
   private def gcd(x: Int, y: Int): Int = { 
     if (x == 0) y 
     else if (x < 0) gcd(-x, y) 
     else if (y < 0) -gcd(x, -y) 
     else gcd(y % x, x) 
   } 
   private val g = gcd(n, d) 
   val numer: Int = n/g 
   val denom: Int = d/g 
   def +(that: Rational) = 
     new Rational(numer * that.denom + that.numer * denom, 
     denom * that.denom) 
   def -(that: Rational) = 
   new Rational(numer * that.denom - that.numer * denom, 
     denom * that.denom) 
   def *(that: Rational) = 
     new Rational(numer * that.numer, denom * that.denom) 
   def /(that: Rational) = 
     new Rational(numer * that.denom, denom * that.numer) 
 }

#co(){
This defines Rational as a class which takes two constructor arguments n and d, containing the number's numerator and denominator parts. The class provides fields which return these parts as well as methods for arithmetic over rational numbers. Each arithmetic method takes as parameter the right operand of the operation. The left operand of the operation is always the rational number of which the method is a member. 
}

有理数を、分子 n と分母 d の2つのコンストラクタ引数をとるクラスとして定義しています。このクラスは有理数上の演算メソッドだけではなく、分子と分母を返すフィールドも提供します。各演算メソッドは操作の右オペランドを引数にとります。操作の左オペランドは、常にそのメソッドがメンバであるような有理数です。

#co(){
Private members. The implementation of rational numbers defines a private method gcd which computes the greatest common denominator of two integers, as well as a private field g which contains the gcd of the constructor arguments. These members are inaccessible outside class Rational. They are used in the implementation of the class to eliminate common factors in the constructor arguments in order to ensure that numerator and denominator are always in normalized form. 
}

&b(){プライベート・メンバ } 有理数の実装では、2つの整数の最大公約数を計算するプライベートなメソッド gcd を定義し、また、コンストラクタ引数の gcd を格納するプライベートなフィールド g も定義します。これらのメンバはクラス Rational の外からはアクセスできません。それらはクラスの実装において、コンストラクタ引数を公約数で割って、分子と分母を常に正規形(約分された形)にするために使います。

#co(){
Creating and Accessing Objects. As an example of how rational numbers can be used, here's a program that prints the sum of all numbers 1/i where i ranges from 1 to 10. 
}

&b(){オブジェクト生成とアクセス } 有理数の使い方の例として、i の範囲が 1 から 10 まで時に、1/i の和を印刷するプログラムを示します。

 var i = 1 
 var x = new Rational(0, 1) 
 while (i <= 10) { 
   x += new Rational(1, i) 
   i += 1 
 } 
 println("" + x.numer + "/" + x.denom) 

#co(){
The + takes as left operand a string and as right operand a value of arbitrary type. It returns the result of converting its right operand to a string and appending it to its left operand. 
}
+ は左オペランドに文字列を、右オペランドに任意の型の値をとります。右オペランドを文字列に変換し、それを左オペランドに追加した結果を返します。

#co(){
Inheritance and Overriding. Every class in Scala has a superclass which it extends. If a class does not mention a superclass in its definition, the root type scala.AnyRef is implicitly assumed (for Java implementations, this type is an alias for java.lang.Object. For instance, class Rational could equivalently be defined as 
}

&b(){継承とオーバーライド } Scala のすべてのクラスは親クラスを持ち、それを継承しています。クラス定義の中で親クラスが指定されていない場合、ルート型 scala.AnyRef が暗黙のうちに仮定されます (Java の実装でいうと、この型は java.lang.Object のエイリアスです)。 たとえばクラス Rational を次のように定義しても同じことです。

 class Rational(n: Int, d: Int) extends AnyRef { 
   ... // as before 
 }
 
#co(){
A class inherits all members from its superclass. It may also redefine (or: override) some inherited members. For instance, class java.lang.Object defines a method toString which returns a representation of the object as a string: 
}

クラスは親クラスのメンバをすべて継承します。継承したメンバを再定義 (つまり、&bold(){オーバーライド}) できます。たとえばクラス java.lang.Object は、オブジェクトの文字列表現を返すメソッド toString を定義しています。

 class Object { 
   ... 
   def toString: String = ... 
 }

#co(){
The implementation of toString in Object forms a string consisting of the object's class name and a number. It makes sense to redefine this method for objects that are rational numbers: 
}

Object での toString は、オブジェクトのクラス名と数からなる文字列として実装されています。このメソッドを有理数のオブジェクト用に再定義すると役に立ちます。

 class Rational(n: Int, d: Int) extends AnyRef { 
   ... // as before 
   override def toString = "" + numer + "/" + denom 
 }

#co(){
Note that, unlike in Java, redefining definitions need to be preceded by an override modifier.

If class A extends class B , then objects of type A may be used wherever objects of type B are expected. We say in this case that type A conforms to type B . For instance, Rational conforms to AnyRef, so it is legal to assign a Rational value to a variable of type AnyRef: 
}

注意すべき点は、Java とは異なり、定義の再定義では手前に override 修飾子が必要なことです。
 
もしクラス A がクラス B を継承しているならば、型 A のオブジェクトは、型 B のオブジェクトが期待される所ではどこでも使用できます。このような場合、型 A は型 B に&bold(){適合する}(conform)、といいます。たとえば Rational は AnyRef に適合します。したがって Rational 値を AnyRef 型の変数に代入できます。

 var x: AnyRef = new Rational(1, 2) 

#co(){
Parameterless Methods. Unlike in Java, methods in Scala do not necessarily take a parameter list. An example is the square method below. This method is invoked by simply mentioning its name. 
}

&b(){引数なしのメソッド } Java とは異なり、Scala のメソッドは引数リストを必ずしも必要としません。次の square メソッドがその例です。このメソッドは単に名前を書くだけで呼び出されます。

 class Rational(n: Int, d: Int) extends AnyRef { 
   ... // as before 
   def square = new Rational(numer*numer, denom*denom) 
 } 
 val r = new Rational(3, 4) 
 println(r.square) // prints''9/16''* 

#co(){
That is, parameterless methods are accessed just as value fields such as numer are. The difference between values and parameterless methods lies in their definition. The right-hand side of a value is evaluated when the object is created, and the value does not change afterwards. A right-hand side of a parameterless method, on the other hand, is evaluated each time the method is called. The uniform access of fields and parameterless methods gives increased flexibility for the implementer of a class. Often, a field in one version of a class becomes a computed value in the next version. Uniform access ensures that clients do not have to be rewritten because of that change. 
}

これは、引数なしメソッドは numer のような値フィールドと同じようにアクセスされるということです。値と引数なしメソッドの違いはそれらの定義にあります。値の右辺はオブジェクトが作成された時に評価され、以後は値が変化しません。その一方で、引数なしメソッドの右辺はメソッドが呼ばれる度に評価されます。フィールドと引数なしメソッドのアクセス方法が統一されているのでクラス実装者の自由度が増します。しばしば、あるバージョンではフィールドだったものが次のバージョンでは計算された値だったりします。統一的なアクセスのおかげで、クライアントは変更による書き直しが不要になります。

#co(){
Abstract Classes. Consider the task of writing a class for sets of integer numbers with two operations, incl and contains. (s incl x) should return a new set which contains the element x together with all the elements of set s. (s contains x) should return true if the set s contains the element x, and should return false otherwise. The interface of such sets is given by:
}

&b(){抽象クラス } 整数の集合に対して、2つの操作 incl と contains を持つクラスを書く課題について考えてみましょう。(s incl x) は集合 s の要素すべてと、要素 x を持つ新しい集合を返すべきです。(s contains x) は、集合 s が要素 x を含む時は true を、さもなければ false を返すべきです。そのような集合のインターフェイスは次のようになります。

 abstract class IntSet { 
   def incl(x: Int): IntSet 
   def contains(x: Int): Boolean 
 } 

#co(){
IntSet is labeled as an abstract class. This has two consequences. First, abstract classes may have deferred members which are declared but which do not have an implementation. In our case, both incl and contains are such members. Second, because an abstract class might have unimplemented members, no objects of that class may be created using new. By contrast, an abstract class may be used as a base class of some other class, which implements the deferred members. 
}

IntSet は&bold(){抽象クラス}と分類されます。これは2つの結果をもたらします。一つは、抽象クラスは&bold(){延期された}メンバを持ち、それらは宣言はあっても実装はありません。この場合、incl と contains の両方がそのようなメンバです。二つ目は、抽象クラスには未実装のメンバがあるかもしれないので、そのクラスのオブジェクトを new で生成できません。その一方で、抽象クラスは他のクラスの基底クラスとして使うことができ、継承したクラスでは延期されたメンバを実装します。

#co(){
Traits. Instead of abstract class one also often uses the keyword trait in Scala. Traits are abstract classes that are meant to be added to some other class. This might be because a trait adds some methods or fields to an unknown parent class. For instance, a trait Bordered might be used to add a border to a various graphical components. Another usage scenario is where the trait collects signatures of some functionality provided by different classes, much in the way a Java interface would work. 

Since IntSet falls in this category, one can alternatively define it as a trait: 
}

&b(){トレイト } Scala では、abstract class の代わりにキーワード trait をよく使います。トレイトは、他のクラスに付け加えるための抽象クラスです。これはトレイトが未知の親クラスにメソッドやフィールドをいくつか追加するからです。たとえば、トレイト Bordered は様々なグラフィカルコンポーネントに縁を追加するのに使われるでしょう。別の使い方としては、様々なクラスによって提供される機能のシグネチャをトレイトが集約するという、Java のインターフェイスがするようなやり方です。

IntSet はこの分類に入るので、trait としても定義できます。

 trait IntSet { 
   def incl(x: Int): IntSet 
   def contains(x: Int): Boolean 
 } 

#co(){
Implementing Abstract Classes. Let's say, we plan to implement sets as binary trees. There are two possible forms of trees. A tree for the empty set, and a tree consisting of an integer and two subtrees. Here are their implementations. 
}

&b(){抽象クラスの実装 } たとえば集合を二分木で実装することにしましょう。木の形には2つの可能性があります。空集合を表す木と、整数一つと2つのサブ木からなる木です。次がその実装です。

 class EmptySet extends IntSet { 
   def contains(x: Int): Boolean = false 
   def incl(x: Int): IntSet = new NonEmptySet(x, new EmptySet, new EmptySet) 
 } 
 
 class NonEmptySet(elem: Int, left: IntSet, right: IntSet) extends IntSet { 
   def contains(x: Int): Boolean = 
     if (x < elem) left contains x 
     else if (x > elem) right contains x 
     else true 
   def incl(x: Int): IntSet = 
     if (x < elem) new NonEmptySet(elem, left incl x, right) 
     else if (x > elem) new NonEmptySet(elem, left, right incl x) 
     else this
 }

#co(){
Both EmptySet and NonEmptySet extend class IntSet. This implies that types EmptySet and NonEmptySet conform to type IntSet - a value of type EmptySet or NonEmptySet may be used wherever a value of type IntSet is required. 
}

EmptySet と NonEmptySet の両方とも IntSet を継承します。これは、型 EmptySet と NonEmptySet が型 IntSet に適合すること --- 型 EmptySet や NonEmptySet の値は、型 IntSet の値が必要なところではどこでも使えることを意味します。

#co(){
Exercise 6.0.1 Write methods union and intersection to form the union and intersection between two sets. 

Exercise 6.0.2 Add a method 

 def excl(x: Int) 

to return the given set without the element x. To accomplish this, it is useful to also implement a test method 

 def isEmpty: Boolean 

for sets. 
}

&b(){演習 6.0.1 } メソッド union と intersection を2つの集合の結びと交わりとなるように書きなさい。

&b(){演習 6.0.2 } 要素 x を取り除いた集合を返すメソッド

 def excl(x: Int) 

を追加しなさい。そうするために便利なテスト用メソッド

 def isEmpty: Boolean 

も、集合に対して実装しなさい。

#co(){
Dynamic Binding. Object-oriented languages (Scala included) use dynamic dispatch for method invocations. That is, the code invoked for a method call depends on the run-time type of the object which contains the method. For example, consider the expression s contains 7 where s is a value of declared type s: IntSet. Which code for contains is executed depends on the type of value of s at run-time. If it is an EmptySet value, it is the implementation of contains in class EmptySet that is executed, and analogously for NonEmptySet values. This behavior is a direct consequence of our substitution model of evaluation. For instance,
}

&b(){動的束縛 } (Scala を含む) オブジェクト指向言語はメソッド呼び出しのときに&bold(){動的ディスパッチ}を用います。これは、メソッド呼び出しで起動されるコードは、メソッドを含むオブジェクトの実行時型に依存するということです。たとえば式 s contains 7、ただし s は宣言された型 s: IntSet の値だとします。もしそれが EmptySet の値なら、実行されるのはクラス EmptySet の contains の実装であり、NonEmptySet の値の場合も同様です。この振る舞いは評価の置き換えモデルの直接的な帰結です。たとえば

   (new EmptySet).contains(7) 
 ->      (by replacing contains by its body in class EmptySet) 
   false 

   (new EmptySet).contains(7) 
 ->      (クラス EmptySet の contains 本体で置き換えることで)
   false 


#co(){
Or, 
}

あるいは

   new NonEmptySet(7, new EmptySet, new EmptySet).contains(1) 
 ->      (by replacing contains by its body in class NonEmptySet) 
   if (1 < 7) new EmptySet contains 1 
   else if (1 > 7) new EmptySet contains 1 
   else true 
 ->      (by rewriting the conditional)
   new EmptySet contains 1 
 ->      (by replacing contains by its body in class EmptySet) 
   false .



   new NonEmptySet(7, new EmptySet, new EmptySet).contains(1) 
 ->      (クラス NonEmptySet の contains 本体で置き換えることで) 
   if (1 < 7) new EmptySet contains 1 
   else if (1 > 7) new EmptySet contains 1 
   else true 
 ->      (条件式を書き換えて)
   new EmptySet contains 1 
 ->     (クラス EmptySet の contains 本体で置き換えることで)
   false .

復元してよろしいですか?

ツールボックス

下から選んでください:

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