-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcollections-impl_4.html
178 lines (178 loc) · 12.6 KB
/
collections-impl_4.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content="HTML Tidy for Mac OS X (vers 25 March 2009), see www.w3.org" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Scala コレクションのアーキテクチャ -- RNA メソッドの戻り値型の適応</title>
<link rel="stylesheet" type="text/css" href="guide.css" />
</head>
<body dir="ltr">
<table width="100%" cellpadding="0" cellspacing="2">
<tr>
<td bgcolor="#99CCFF"><a href="collections-impl_5.html"><img border="0" alt="map 類の取り扱い" src="next.png" /></a></td>
<td bgcolor="#99CCFF"><a href="collections-impl_0.html"><img border="0" alt="トップ" src="up.png" /></a></td>
<td bgcolor="#99CCFF"><a href="collections-impl_3.html"><img border="0" alt="新しいコレクションの参入" src="previous.png" /></a></td>
<td align="center" bgcolor="#99CCFF" width="100%"><b><tt>RNA</tt> メソッドの戻り値型の適応</b></td>
<td bgcolor="#99CCFF" align="center" class="tocref"><a href="collections-impl_8.html">目次</a></td>
</tr>
</table>
<h1><tt>RNA</tt> メソッドの戻り値型の適応</h1>
<p>以下に <tt>RNA1</tt> を使った具体例を示す:</p>
<div class="quote">
<table cellspacing="1" cellpadding="0">
<tr>
<td colspan="99" align="left"><tt>scala> rna1.length</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt>res2: <font color="#660099">Int</font> = <font color="#000000">5</font></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> </tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt>scala> rna1.last</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt>res3: <font color="#660099">Base</font> = C</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> </tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt>scala> rna1.take(<font color="#000000">3</font>)</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt>res4: <font color="#660099">IndexedSeq[Base]</font> = <font color="#660099">Vector</font>(A, U, G)</tt></td>
</tr>
</table>
</div>
<p>最初の二つの結果は予想通りかもしれないが、最後の <tt>rna1</tt> から先頭の三つの要素を take した結果は予想外だったかもしれない。事実、戻り値は静的型として <tt>IndexedSeq[Base]</tt> をとり、動的型として <tt>Vector</tt> をとる。<tt>RNA1</tt> 値を期待していたかもしれない。しかし、<a href="collections-impl_3.html#lst:rna1">RNA1 クラスのコード</a>で行ったのは <tt>RNA1</tt> を <tt>IndexedSeq</tt> から継承しただけだったので、それは不可能だ。一方 <tt>IndexedSeq</tt> クラスは <tt>IndexedSeq</tt> を返す <tt>take</tt> メソッドを持ち、 <tt>IndexedSeq</tt> のデフォルトの実装は <tt>Vector</tt> だ。 上記のやりとりの最後の行に出てくるベクトルはここからきている。</p>
<p><a name="lst:rna2" id="lst:rna2"> </a></p>
<div class="quote">
<table cellspacing="1" cellpadding="0">
<tr>
<td colspan="99" align="left"><tt> <font color="#0000E5">final</font> <font color="#0000E5">class</font> RNA2 <font color="#0000E5">private</font> (</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> <font color="#0000E5">val</font> groups: <font color="#660099">Array[Int]</font>,</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> <font color="#0000E5">val</font> length: <font color="#660099">Int</font></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> ) <font color="#0000E5">extends</font> <font color="#660099">IndexedSeq[Base]</font> <font color="#0000E5">with</font> <font color="#660099">IndexedSeqLike[Base, RNA2]</font> {</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> </tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> <font color="#0000E5">import</font> RNA2.</tt><tt>_</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> </tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> <font color="#0000E5">override</font> <font color="#0000E5">def</font> newBuilder: <font color="#660099">Builder[Base, RNA2]</font> = </tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> <font color="#0000E5">new</font> <font color="#660099">ArrayBuffer[Base]</font> mapResult fromSeq</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> </tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> <font color="#0000E5">def</font> apply(idx: <font color="#660099">Int</font>): <font color="#660099">Base</font> = <i>// 前と同じ</i></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> }</tt></td>
</tr>
</table>
</div>
<div align="center">RNA鎖クラス、第二版</div>
<p>現状の把握ができた所で、これを解決するために何を変更するべきかというのが次の問題だろう。一つの方法としては、<tt>RNA1</tt> クラスの <tt>take</tt> メソッドをオーバーライドしてしまうというのがある:</p>
<div class="quote">
<table cellspacing="1" cellpadding="0">
<tr>
<td colspan="99" align="left"><tt><font color="#0000E5">def</font> take(count: <font color="#660099">Int</font>): RNA1 = RNA1.fromSeq(<font color="#0000E5">super</font>.take(count))</tt></td>
</tr>
</table>
</div>
これは <tt>take</tt> に関しては解決する。しかし、<tt>drop</tt> や <tt>filter</tt> や <tt>init</tt> に関してはどうだろう。 事実、列には 50以上の列を再び返すメソッドがある。一貫性を持たせるためには、これら全てをオーバーライドする必要がある。この方法の魅力がどんどん色あせていくのが分かるだろう。幸い、同じ効果がある別のもっと簡単な方法がある。RNA クラスは、<tt>IndexedSeq</tt> だけでなく、その実装トレイトである <tt>IndexedSeqLike</tt> も継承すればいいのだ。これは、上記の RNA2 クラスのコードに示した。この新しい実装は、以前のものと比べて二つの側面においてしか違わない。第一に、<tt>RNA2</tt> クラスは <tt>IndexedSeqLike[Base,</tt> <tt>RNA2]</tt> を継承する。<tt>IndexedSeqLike</tt> トレイトは、<tt>IndexedSeq</tt> の全ての具象メソッド (concrete
method) を拡張可能な方法で実装する。例えば、<tt>take</tt>、<tt>drop</tt>、<tt>filter</tt>、や <tt>init</tt> のようなメソッドの戻り値の型は <tt>IndexedSeqLike</tt> クラスに渡された第二の型パラメータ、つまり <tt>RNA2</tt> 自身となる。
<p>これを実現するために <tt>IndexedSeqLike</tt> 自身が、正しい種類のビルダを作成する <tt>newBuilder</tt> 抽象体に基づいている。<tt>IndexedSeqLike</tt> トレイトの子クラスは自身の種類のコレクションを返すために <tt>newBuilder</tt> をオーバーライドしなくてはいけない。<tt>RNA2</tt> クラスでは <tt>newBuilder</tt> メソッドは <tt>Builder[Base,</tt> <tt>RNA2]</tt> 型のビルダを返す。</p>
<p>このビルダを構築するために、まず自身が <tt>Builder[Base,</tt> <tt>ArrayBuffer]</tt> でもある <tt>ArrayBuffer</tt> が作成される。次に、<tt>ArrayBuffer</tt> ビルダを <tt>mapResult</tt> を呼び出すことで <tt>RNA2</tt> ビルダに変換する。<tt>mapResult</tt> メソッドは、<tt>ArrayBuffer</tt> から <tt>RNA2</tt> への変換関数を引数として期待する。渡された関数は、任意の塩基列から <tt>RNA2</tt> に変換する <tt>RNA2.fromSeq</tt> だ(配列バッファは列の一種なので、<tt>RNA2.fromSeq</tt> を適用できる)。</p>
<p>もし <tt>newBuilder</tt> を定義しなかったとすると、以下のようなエラーが発生しただろう:</p>
<div class="quote">
<table cellspacing="1" cellpadding="0">
<tr>
<td colspan="99" align="left"><tt><i>RNA2.scala:5: error: overriding method newBuilder in trait</i></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><i>TraversableLike of type => scala.collection.mutable.</i></tt><tt><i>Builder[Base,RNA2];</i></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><i> method newBuilder in trait GenericTraversableTemplate of type</i></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><i> => scala.collection.mutable.</i></tt><tt><i>Builder[Base,IndexedSeq[Base]] has</i></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><i> incompatible type</i></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><i>class RNA2 private (val groups: Array[Int], val length: Int) </i></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> ^</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><i>one error found</i></tt></td>
</tr>
</table>
</div>
<p>込み入ったコレクションライブラリの仕組みを反映して、このエラーメッセージも非常に長く複雑なものになっている。メソッドの由来に関する情報は、この場合は、役に立つというよりは問題の核心から注意を逸らすことになるので無視することにする。残りは、戻り値型が <tt>Builder[Base,</tt> <tt>RNA2]</tt> である <tt>newBuilder</tt> メソッドが定義される必要があるが、戻り値型が <tt>Builder[Base,IndexedSeq[Base]]</tt> のメソッド <tt>newBuilder</tt> ということだ。後者は前者をオーバーライドしない。戻り値型が <tt>Builder[Base,</tt> <tt>RNA2]</tt> の最初のメソッドは、<a href="collections-impl_4.html#lst:rna2">RNA2 クラスのコード</a>において、<tt>IndexedSeqLike</tt> の型パラメータに <tt>RNA2</tt>
が渡されたことによってこの型でインスタンス化された抽象メソッドだ。戻り値型が <tt>Builder[Base,IndexedSeq[Base]]</tt> の二番目のメソッドは、継承した <tt>IndexedSeq</tt> クラスによって提供されているものだ。別の言い方をすると、最初の戻り値型の <tt>newBuilder</tt> 無しでは <tt>RNA2</tt> は無効ということだ。</p>
<p><a href="collections-impl_4.html#lst:rna2">RNA2 クラス</a>のより洗練された実装により、<tt>take</tt>、<tt>drop</tt>、<tt>filter</tt> などのメソッドは期待通り作動する:</p>
<div class="quote">
<table cellspacing="1" cellpadding="0">
<tr>
<td colspan="99" align="left"><tt>scala> <font color="#0000E5">val</font> rna2 = RNA2(A, U, G, G, T)</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><font color="#590000">rna2: RNA2 = RNA2(A, U, G, G, T)</font></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> </tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt>scala> rna2 take <font color="#000000">3</font></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><font color="#590000">res5: RNA2 = RNA2(A, U, G)</font></tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt> </tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt>scala> rna2 filter (U !</tt><tt>=)</tt></td>
</tr>
<tr>
<td colspan="99" align="left"><tt><font color="#590000">res6: RNA2 = RNA2(A, G, G, T)</font></tt></td>
</tr>
</table>
</div>
<p>続いては、<a href="collections-impl_5.html"><tt>map</tt> 類の取り扱い</a></p>
<hr />
<table width="100%" cellpadding="0" cellspacing="2">
<tr>
<td bgcolor="#99CCFF"><a href="collections-impl_5.html"><img border="0" alt="map 類の取り扱い" src="next.png" /></a></td>
<td bgcolor="#99CCFF"><a href="collections-impl_0.html"><img border="0" alt="トップ" src="up.png" /></a></td>
<td bgcolor="#99CCFF"><a href="collections-impl_3.html"><img border="0" alt="新しいコレクションの参入" src="previous.png" /></a></td>
<td align="center" bgcolor="#99CCFF" width="100%"><b><tt>RNA</tt> メソッドの戻り値型の適応</b></td>
<td bgcolor="#99CCFF" align="center" class="tocref"><a href="collections-impl_8.html">目次</a></td>
</tr>
</table>
</body>
</html>