16. Working with Lists
Listはscalaで一番良く使うデータ型。
多くの一般的な操作をList上で実行することが出来る。
更に、Listを扱う上での重要な設計テクニックについても学ぶ。
16.1 List literals
ListはArrayと似ているが、違う点もある。
Listはimmutable, Arrayはmutable
Listはrecursive structure, Arrayはflat
i.e., a linked list
?? ArrayもArrayの中にArrayをnest出来るが、それでは再帰構造とは言えない??
16.2 The List type
Listはhomogeneous
Arrayと同じ。
各要素が同じ型。
List[T]でTが型
val l: List[List[Int]] = List(List(1, 2, 3), List(4,4))
** 要素数と型は無関係。
scalaではListの型はcovariant(共変)
SがTのsubtypeならList[S]もList[T]のsubtypeになる
ex. List[String]はList[Object]のsubtype
空のList()はList[Nothing]型
Nothingは全ての型のサブタイプ。Section 11.3
List[Nothing]も全ての型のサブタイプ。
List()は全てのList型の変数に代入可能。
val xs: List[String] = List()
16.3 Constructing lists
全てのListは二つの基礎的な構成要素から出来ている。
Nil -> 空リスト
:: -> cons、x :: xsでリストxsの先頭に要素xを付け加えたリストを表す。
List() == Nil
List(1, 2, 3) == 1 :: (2 :: (3 :: Nil)) == 1 :: 2 :: 3 :: Nil
::は:で終わる演算子なので右結合なので()を省略しても同じ意味。
?? これが再帰的構造と言う意味か ??
List(...)は::とNilの組み合わせのwrapperに過ぎない。
16.4 Basic operations on lists
Listの操作は下記の3つに集約される。
head, tail, isEmpty
headとtailを空のListに適用すると例外が投げがれる。
scala> Nil.head
java.util.NoSuchElementException: head of empty list
scala> Nil.tail
java.lang.UnsupportedOperationException: tail of empty list
16.5 List patterns
Listの要素を取り出すのにpattern matchingが使える。
val l = List(1, 2, 3)
val List(a, b, c) = l // a=1, b=2, c=3
val a :: b :: rest = l // a=1, b=2, c: List[Int] = List(3)
val x :: xs = l // x = l.head, xs = l.tail
head, tail, isEmptyを使うよりもpattern matchingを使うことを推奨。
より簡素で分かりやすくなる場合が多い。
** CourseraのWeek1の課題もpattern matchingを使えば簡素化出来る!!
About pattern matching on Lists
List(...)も::もChapter 15の可能なパターンに入っていない。
実際はextractorライブラリで定義されているinstanceのパターン。
x :: xsは::(x, xs)でpattern matchingの場合はconstructorとして扱われる。
::はclass scala.::のconstructorとclass Listのmethodの両方に定義されている。
Listの詳しい実装はChapter 22, extractorはChapter 24参照。
16.6 First-order methods on class List
first-order methods
関数を引数に取らないmethod。
高階method(higher-order methods)の逆。
Concatenating lists
::: -> List同士の結合
scala> List(1, 2) ::: List(3, 4, 5)
res0: List[Int] = List(1, 2, 3, 4, 5)
:で終わるので右結合
The Divide and Conquer principle
分割統治法
Listのような再帰的なデータ構造を扱う場合に良く使われる戦略
最初にpattern matchを使ってより単純なケースに入力を分割する。
各分割の演算結果を再帰呼び出しを使って結合する。
def append[T](xs: List[T], ys: List[T]): List[T] = xs match {
case List() => ys
case x :: xs1 => x :: append(xs1, ys)
}
xs, ysどちらを分割するかは、::で生成を考えるとxsで決まり。
分割の仕方は、空の場合とそれ以外に分けるパターンが多い。
リストを生成する場合、何がheadで何がtail化かを考えると良い。
Taking the length of a list: length
Listのlengthを求めるのは要素数に比例したコストが掛かる。
要素を走査する必要があるため。
Arrayの場合は不要。コストが低い。
isEmptyの変わりにlength == 0を使うべきではない。
Accessing the end of a list: init and last
last -> 最後の要素を返す。headの反対。
init -> 最後の要素を除いたリストを返す。tailの反対。
head, tailは固定時間で終わるが、last, initは要素数に比例した時間が掛かる。
リストの前の方にアクセスが集中する様なデータ設計が望ましい。
Reversing lists: reverse
reverseに関しては下記の推論が成り立つので、プログラムを簡略化に使える。
xs.reverse.reverse equals xs
xs.reverse.init equals xs.tail.reverse
xs.reverse.tail equals xs.init.reverse
xs.reverse.head equals xs.last
xs.reverse.last equals xs.head
reverseは下記で実装できるが、別の実装で性能を改善出来る。
この実装だとorder(n*n)。これをorder(n)に改善する。Section 16.7
def rev[T](xs: List[T]): List[T] = xs match {
case List() => xs
case x :: xs1 => rev(xs1) ::: List(x)
}
Prefixes and suffixes: drop, take and splitAt
xs take n -> 最初のn個の要素を含むリスト
xs drop n -> 最初のn個を取り除いたリスト
n > xs.length でも n < 0 でもOKで例外は投げない。
xs splitAt n equals (xs take n, xs drop n)
n個目の要素の前後でリストを分割する。
Element selection: apply and indices
Listのapplyはn番目の要素を返す。
abcde apply 2 // -> c
abcde(2) // -> c
scalaではArrayと違ってあまり使われない。
要素にアクセスするのに捜査が必要でnに比例した時間が掛かるから。
実装は下記と同じ。
xs apply n equals (xs drop n).head
indicesでインデックスのリストが得られる。
scala> abcde.indices
res13: List[Int] = List(0, 1, 2, 3, 4)
Zipping lists: zip
二つのリストから要素のタプルのリストを作成する。** 縦横変換。
scala> val zipped = abcde zip List(1, 2, 3)
zipped: List[(Char, Int)] = List((a,1), (b,2), (c,3))
長さが異なる時は短い方に丸められる。
使用頻度の高い特別なケースが値とindexを組みにする事。zipWithIndex
scala> abcde.zipWithIndex
res15: List[(Char, Int)] = List((a,0), (b,1), (c,2), (d,3), (e,4))
Displaying lists: toString and mkString
xs mkString (pre, sep, post)
scala> abcde mkString ("[", ",", "]")
res17: String = [a,b,c,d,e]
xs mkString sep equals xs mkString ("", sep, "")
xs mkString equals xs mkString ""
xs toString equals xs mkString ("List(", ", ", ")")
scala> abcde.toString
res16: String = List(a, b, c, d, e)
xs addString (buf, pre, sep, post)
?? 文字列そのもではなくてStringBuilder objectに付け加える ??
Listはtraite Iterableを継承しているので、Iterableな事に適用可能。
Converting lists: elements, toArray, copyToArray
toArray, toListでList<->Array間の変換が出来る。
xs copyToArray (arr, start)
arrのstart番目からxsの要素をすべてコピーする。
arrは使う側が十分な大きさを確保しておく必要がある。
xs.elements でiteratorにアクセスできる。
it = xs.elements; it.next; it.next...
Example: Merge sort
分割統治のカリー化の例としてmerge sortを扱う。
またアルゴリズムの複雑度(order)についても議論する。
merge sortのorderはn*log(n)で入力データによらない。
最悪値も平均値もorder(n*log(n))になる。
これがmerge sortの利点
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {
def merge(xs: List[T], ys: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys
case (_, Nil) => xs
case (x :: xs1, y :: ys1) =>
if (less(x, y)) x :: merge(xs1, ys)
else y :: merge(xs, ys1)
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(less)(ys), msort(less)(zs))
}
}
カリー化された関数から部分適用を実施した例
scala> val intSort = msort((x: Int, y: Int) => x < y) _
intSort: (List[Int]) => List[Int] = <function>
scala> val reverseIntSort = msort((x: Int, y: Int) => x > y) _
reverseIntSort: (List[Int]) => List[Int] = <function>
16.7 Higher-order methods on class List
多くのリスト操作は同じような構造を持っている。
幾つかのパターンが何回も出てくる。
javaならforやwhileのidiomで書く
scalaならhigher-order operatorsを使ってもっと簡素に直接的に書ける。
Mapping over lists: map, flatMap and foreach
xs map f
リストの全ての要素にある関数を適用した結果をリスト化する。
val f = (x: T) => {...}: U
List[T](a, b, ...) map f equals List[U](f(a), f(b), ...)
scala> List(1, 2, 3) map (_ + 1)
res29: List[Int] = List(2, 3, 4)
xs flatMap f
map適用後のリストの要素(要素もリスト)を ::: で結合する。
scala> List.range(1, 5) flatMap (i => List.range(1, i) map (j => (i, j)))
res34: List[(Int, Int)] = List((2,1), (3,1), (3,2), (4,1), (4,2), (4,3))
上記はforでも同様に書ける。
for (i <- List.range(1, 5); j <- List.range(1, i)) yield (i, j)
xs foreach f
適用する関数も、foreach自体も値を返さないUnit型。
List(1, 2, 3, 4, 5) foreach (sum += _)
?? あまり関数型的ではない??
Filtering lists: filter, partition, find, takeWhile, dropWhile, and span
p は T => Boolean
xs filter p
xsの要素xのうちp(x) == trueのものだけ残す。
xs partition p equals (xs filter p, xs filter (!p(_)))
filterした要素のリストとfilterされた要素のリストを返す。
xs find p equals
(xs filter p) match {case Nil => None; case x :: xs => Some(x)}
filter結果が無ければNone, あればその先頭の要素のSome()を返す。
Option type
xs takeWhile p
先頭から連続してpを満たす要素のリスト。
scala> List(1, 2, 3, -4, 5) takeWhile (_ > 0)
res42: List[Int] = List(1, 2, 3)
xs dropWhile p
先頭から連続してpを満たす要素を取り除いたリスト。
scala> List(1, 2, 3, -4, 5) dropWhile (_ > 0)
res42: List[Int] = List(-4, 5)
xs span p equals (xs takeWhile p, xs dropWhile p)
先頭から連続してpを満たす要素のリストとその残りのリスト。
Predicates over lists: forall and exists
p は T => Boolean
xs forall p
xsの全ての要素がpを満たせばtrue
xs exists p
xsの要素のうち一つでもpを満たせばtrue
Folding lists: /: and :\
畳み込み(fold) ?? reduceとの差は??
foldLeft or /:
opは (U, T) => U
(z /: List(a, b, c)) (op) equals op(op(op(z, a), b), c)
foldRight or :\
opは (T, U) => U
(List(a, b, c) :\ z) (op) equals op(a, op(b, op(c, z)))
空リストList()に対してはzをそのまま返す。
/:, :\だと読み難いという場合のためにfoldLeft,foldRightの名前も定義あり。
foldLeft, foldRight自体の効率は同じだが、opにより異なる場合あり。
def flattenLeft[T](xss: List[List[T]]) = (List[T]() /: xss) (_ ::: _)
def flattenRight[T](xss: List[List[T]]) = (xss :\ List[T]()) (_ ::: _)
上記の場合は:::の性質からRightの方が効率が良い。
List[T]()の型指定はscalaでは必要。NilやList()ではNG
scalaの型推論の制限。Chapter 22
?? 再帰だと型推論が上手く出来ない?? 戻り値、引数の型が必須なのと同様??
?? 細かくvalに代入すると名前をつけるのが大変?? 短い名前でも良い??
Example: List reversal using fold
** foldや再帰を使うときの考え方の流れも説明しようとしている。
まず、foldLeftを使ったらどうなるかを考える。
def reverseLeft[T](xs: List[T]) = (startvalue /: xs)(operation)
このstartvalueとoperationを決められれば良い。
一番簡単な空リストxs = List()の反転がList()になるようにする。
置き換え(substitution)を使って実行結果を追っかける。
(startvalue /: List())(operation) // reverseLeftの定義から
== startvalue // /:の定義から
== List()
これでstartvalueはList()に決められる。
次に簡単な一要素のリストList(x)の反転がList(x)になるようにする。
(List() /: List(x))(operation) // reverseLeftの定義から
== operation(List(), x) // /:の定義から
== List(x) // 満たすべき結果から
== x :: List() // List(x)の別の表現
== {(ys, y) => y :: ys} (List(), x) // ここは思いつくかどうか?
これでoperationを{(ys, y) => y :: ys}に決められる。
これはconsの反対なのでsnocと呼ばれることがある。
soncがorder(1)なのでreverseLeftはorder(n)の計算量。
** 連立方程式を解くときや、検算をする時に似ている。
** なるべく簡単な制約条件を考えてそれを満たすようにすれば良い。
Sorting lists: sort
xs sort before ?? sortと言うmethodはない?? sortWithになった??
xsをbeforeの順にソートしたリストを返す。
before: (x: T, y: T) => Boolean
xがyより前に配置されるべき要素ならtrueとなるfunction
マージソートと同様のアルゴリズムを使っている。
class Listのmethodとして定義されている。
16.8 Methods of the List object
class Listのcompanion objectに定義されているoperation
factory methodsと特定の形式のリストに使うmethods
Creating lists from their elements: List.apply
List.apply(1, 2, 3) equals List(1, 2, 3)
Creating a range of numbers: List.range
List.range(from, until)
List.range(from, until, step)
scala> List.range(9, 1, -3)
res53: List[Int] = List(9, 6, 3)
fromは含む。untilは含まない。マイナス値でもOK。
Creating uniform lists: List.make
List.make(num, x)
scala> List.make(5, 'a')
res54: List[Char] = List(a, a, a, a, a)
要素xをnum個含むリスト。num == 0でも良い。
Unzipping lists: List.unzip
List.unzip(List((x1, y1), (x2, y2), ...))
equals (List(x1, x2), List(y1, y2), ...)
ペアになっているリストにしか適用できないのでインスタンスのmethodに出来ない。
scalaの型システムはインスタンスのmethodはその型なら全てに適用可能を要求。
?? 例外を投げれば良い気もするが、実行時に検知も出来ないと言う事??
Concatenating lists: List.flatten, List.concat
List.flatten(List(List((a, b), (c, d), ...))) equals List(a, b, c, d, ...)
List.concat(List((a, b), (c, d), ...)) equals List(a, b, c, d, ...)
concatは任意の数のリストを引数にとって結合する。
flattenはリストのリストを引数にとって結合する。
インスタンスのmethodに出来ないのはunzipと一緒。
Mapping and testing pairs of lists: List.map2, List.forall2, List.exists2
List.map2(List(a1, a2, ...), List(b1, b2, ...))(op: (a, b) => c)
equals List(op(a1, b1), op(a2, b2), ...)
List.forall2(List(a1, a2, ...), List(b1, b2, ...))(op: (a, b) => Boolean)
equals op(a1, b1) && op(a2, b2) && ...
List.exists2(List(a1, a2, ...), List(b1, b2, ...))(op: (a, b) => Boolean)
equals op(a1, b1) || op(a2, b2) || ...
二つのリストの各要素の対にopを適用した結果map, forall, exists相当。
16.9 Understanding Scala's type inference algorithm
scalaの型推論について
local, flow-based
MLやhaskell
global Hindley-Milner style
object-oriented subtypingと一緒に使う際はHindley-Milner styleより優れている。
制限はあるがコーナーケースで型annotationを追加すれば簡単に対応できる。
型annotation追加はデバッグの時も有用。
polymorphic methodsで分からない型エラーが出た場合。
自分の正しいと思う型annotationを追加すれば問題の原因を早くつかめる。
m(args)が適用された場合、最初にmethod mの型が確定しているかをチェックする。
val abcde = "abcde".toList
abcde sortWith (_ > _)
sortWithの型シグネチャは((T, T) => Boolean) => List[T]
適用するオブジェクトabcdeがList[Char]なのでAはCharで確定。
sortWithの型も確定。
パラメータ関数値は(Char, Char) => Boolean型に確定する。
msort(_ > _)(abcde)
msortの型シグネチャは((T, T) => Boolean)(List[T]) => List[T]
Tの型が分からないのでmsortの型は決まらない。
methodの型が決まらない場合は第一引数リストの型をチェックする。
第一引数リストの(_ > _)も型情報が無いので何も決まらない。ここで失敗。
** 第一引数の事ではないので注意!! カリー化の時の最初の引数リスト
第二引数リスト以降は型推論に使わない。
?? 部分適用の際に省略されるかもしれないから??
回避策
型を指定する。
msort[Char](_ > _)(abcde), msort((_: Char) > (_: Char))(abcde)
第一引数リストにnon-function argumentsを持ってくる。
def msortSwapped[T](xs: List[T])(op: (T, T) => Boolean): List[T] = ...
カリー化する際には第一引数リストにnon-function argumentsを持って来た方が良い
(xss :\ List()) (_ ::: _) の場合
:\の型シグネチャは (B)((A, B) => B) => B
Bはxssの入力値からList[T]に確定したとする。List()はList[Nothing]。
第二引数リストの型が(List[T], List[Nothing]) => List[Nothing]
(_ ::: _)の型と一致しないのでエラーになる。
?? Catch-22 situation 何の事 ??
これを防ぐために List[Char]() 等とBの型を指定する必要がある。
?? List[Nothing]は任意のList[T]とマッチするからエラーにしなくても良い?
?? エラーにしないと型が分からないから実行できなくなる?
16.10 Conclusion
basic, first-orde, higher-order operations, utility methods, type inference
Listはscalaでは非常に良く使うので、詳しく使い方を知っている必要ある。
Listを含むcollectionについては次章。
2014年4月29日火曜日
2014年4月28日月曜日
Programming in Scala, First Edition Chapter 15
15. Case Classes and Pattern Matching
pattern matchingについて説明する。
case classを使えば簡単にpattern matchingを使える。
木構造の様な再帰的なデータ構造にも使える。
sealed classes, Option type等pattern matchingの応用についても説明する。
最後に大規模な現実的な使用例も紹介する。
15.1 A simple example
例として算術を表現するライブラリを作る場合を考える
例えば、DSL(domain-specific language)の一部として設計している場合。
abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String,
left: Expr, right: Expr) extends Expr
Varは x, Numberは 1.0, UnOpは +,-, BinOpは+,-等になる。
実装が無いクラス定義では{}は省略できる。
abstract class Expr {} と同じ意味
Case classes
case修飾子を付けたクラス。
pattern matchingのmatch expressionとして使える!!
compilerが自動的に機能を付け加えてくれる。
factory methodの追加
newなしでVar("x")と出来る。
?? 自動的にcompanion objectを生成している??
constructorのparameterが自動的にval付き扱いになる。
自動的にfieldとして追加されると言う事。
toString, hashCode, equalsの自動追加。
equalsが追加されるとそれを呼び出す==の振る舞いも定義される。
?? "natural" implementationsとはどう言う事??
** 型だけを定義するような実装の少ないクラスを書くときに特に便利。
Pattern matching
def simplifyTop(expr: Expr): Expr = expr match {
case UnOp("-", UnOp("-", e)) => e // Double negation
case BinOp("+", e, Number(0)) => e // Adding zero
case BinOp("*", e, Number(1)) => e // Multiplying by one
case _ => expr
}
Javaのswitch文と似ている。
caseに定数だけではなくて任意のexpressionを使える。
?? 任意と言うよりは case classで定義されたconstructorのみ?
UnOp("-", UnOp("-", e))の様な再帰表現も使える。
?? C++のテンプレートに似ている? ループになる事は??
複数matchする場合は上が適用される。
?? 評価は平行して行われるかもしれない??
_ は全てにmatchする。defaultと同じ。一番最後に書く。
=> の後に何も書かないと何も実行しない。値は()になる。
pattern matching全体として値を返す。ifやforと同様。
fall throughしない
どれにもmatchしないとMatchErrorの例外が投げられる。
通常は最後にcase _ => を入れて例外を投げないようにすることが多い。
15.2 Kinds of patterns
パターンは定義と透過的で見たままで分かる。
どういうパターンがあるかについて気をつけるのみで良い。
Wildcard patterns
_は任意のオブジェクトにマッチさせられる。
case BinOp(_, _, _) => println(expr +"is a binary operation")
Constant patterns
それ自身とのみマッチする。定数やvalやsingleton object。
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list" // singleton object
Variable patterns
_と同様に任意のオブジェクトにマッチさせられる。
マッチしたオブジェクトをvariableとしてbindして後で使える。
** perlの正規表現の$1,$2...と同じように強力。
case somethingElse => "not zero: "+ somethingElse
Variable or constant?
小文字始まりだとパターン変数、それ以外は定数とみなされる!!
this.piや`pi`とすると小文字始まりも定数とみなされる。
``は予約語を普通の識別子と認識させる場合にも使える。
Constructor patterns
case classのconstructorとマッチさせる。とても強力。
constructorのパラメータにもパターンマッチが再帰的に適用される。
パラメータが自分自身のConstructor patternでもOK。
case BinOp("+", e, Number(0)) => println("a deep match")
BinOpクラスのコンストラクタとマッチする。
"+": 定数として同じオブジェクトとマッチする。
e: 変数として任意のオブジェクトとマッチして、bindされる。
Number(0): Numberクラスのコンストラクタとマッチする。
0: 定数として同じオブジェクトとマッチする。
Sequence patterns
List or Arrayの様なsequence typesもcase classと同様にマッチさせられる。
case List(0, _, _) => println("found it")
0個以上の任意のエントリのときは_*を指定する。
case List(0, _*) => println("found it")
?? _*以外に、0*で0の任意回の繰り返しとか、_+で1個以上とか出来ない??
Tuple patterns
tupleもsequenceと同様。
case (a, b, c) => println("matched "+ a + b + c)
Typed patterns
selectorのtypeとマッチするかを判定できる。
case s: String => s.length
selectorはAnyだがsはString。selectorをcastしている。
?? case s: _とか、case s: xとかで型を取り出したりは出来ない??
The operators isInstanceOf and asInstanceOfを使えば似たようなことが出来る。
if (x.isInstanceOf[String]) {
val s = x.asInstanceOf[String]
s.length
} else ...
上記のtype test & castは冗長。
pattern matchに誘導するために意図的にそう言語設計している。
case m: Map[_, _] => m.size
_は任意の型にマッチするワイルドカード。
(小文字始まりの!!)型変数を使える。
?? bindは出来ない?? 後で型引数として使う事も出来ない??
Type erasure
$ scala -uncheckedのオプションで上げて下記を実行
scala> def isIntIntMap(x: Any) = x match { case m: Map[Int, Int] => true }
<console>:5: warning: non variable type-argument Int in
type pattern is unchecked since it is eliminated by erasure
case m: Map[Int, Int] => true
scalaはjavaと同様にerasure modelを採用している。
実行時には型情報が分からない。
Map[]が[Int, Int]だと言う事が分からない。
Arrayは例外で型情報を実行時も保持している。
Variable binding
@を使ってマッチしたパターンの一部を変数に束縛出来る。
case UnOp("abs", e @ UnOp("abs", _)) => e
全体がマッチした時に、eにはUnOp("abs", _)の実体が束縛される。
15.3 Pattern guards
patternの後ろにifを付けてguardする事が出来る。
パターンにマッチして更にifが真ならマッチ。
case BinOp("+", x, x) => BinOp("*", x, Number(2))
これはエラーになる。パターン変数を一回しか書けないため。
case BinOp("+", x, y) if x == y => BinOp("*", x, Number(2))
これでx==yの時のみマッチする。
case n: Int if 0 < n => ...
Intで正の時のみマッチ。
case s: String if s(0) == 'a' => ...
文字列で一文字目が'a'の時のみマッチ。
15.4 Pattern overlaps
複数caseにマッチするような場合は上のcaseが適用される。
到達しないcaseがある場合はコンパイルエラーになる。
15.5 Sealed classes
scalaではコンパイル時にcaseに漏れがある事を検出出来ない。
部分コンパイルなので、一緒にコンパイルされてない部分での追加が分からない。
sealed classにすれば同一ファイル以外でのサブクラスの追加を禁止できる。
sealed abstract class Expr
case classを作る時はsealed出来ないか検討すべき。
sealedすればコンパイラがcase漏れを警告してくれる。
逆にコンテクストから入らないcaseがある事が分かっている場合の警告の抑制。
@unchecked anotationを使う。Chapter 25
(e: @unchecked) match {...}
15.6 The Option type
Optionと言う標準の型がある。
Some(x): 実際の値がxの場合。
None: 値が無い場合。
collectionに対する操作の結果として使われる場合が多い。
Mapのgetメソッド等。
pattern matchを使って値を取り出す。
x match {
case Some(s) => s
case None => "?"
}
Option TypeはScalaでは良くつかわれる。
javaだと値が無いときはnullを返す物が多い。
何がnullを返すのかを知っている必要がある。
nullチェックを忘れるリスクが高い。
nullを返す場合が少ないとnullチェックの漏れをテストでも検出出来ない。
scalaだとnullをvalue typeの型には返せない。
scalaだとOption typeを使う事を推奨。
値が返らない可能性がある事を型から把握できる。
nullチェックを忘れるとコンパイルが通らなくなるで直ぐに分かる。
15.7 Patterns everywhere
match以外にも色々な場所でpatternを使える。
Patterns in variable definitions
valやvarで値を定義するとき。
val myTuple = (123, "abc")
val (number, string) = myTuple // tupleを分解
val exp = new BinOp("*", Number(5), Number(1)) // case classを分解
val BinOp(op, left, right) = exp
Case sequences as partial functions
{}内のcaseの並びは関数リテラルになっている。
エントリポイント(パラメータリスト)と実体の組が複数ある。
val withDefault: Option[Int] => Int = {
case Some(x) => x
case None => 0
}
一部のパターンにのみ対応したpartial functionsにも出来る。
val second: PartialFunction[List[Int],Int] = {
case x :: y :: _ => y
}
PartialFunction型にしてコンパイラにPartialFunctionを示す。
isDefinedAtメソッドでcaseの定義済、未定義をチェックできる。
一般的には可能なら全てのcaseを網羅したfull関数を作ろうとするべき。
partialにする場合
来ないcaseに確信がある場合
isDefinedAtメソッドで定義済かを事前に調べる場合
Patterns in for expressions
for (Some(fruit) <- results) println(fruit)
for式の値の抽出に使える。
Some(fruit)にマッチしなかったら捨てられる。filterと同じ動き。
None以外だけを処理することが出来る。
15.8 A larger example
大規模で現実的なpattern matchingの応用として、数式の表現を考える。
Chapter 10で作成したlayout libraryも活用する。
内部表現はこの章で作成したExprクラスを活用する。
逆ポーランド記法に似た内部表現になる。
()についても省略出来るところは省略するようにする。
ExprFormatter: formatter class
((a / (b * c) + 1 / n) / 3) これを下記の様に表示する。
BinOp("/", BinOp("+", BinOp("/", Var("a"),
BinOp("*", Var("b"), Var("c")),
BinOp("/", Number(1),
Var("n")),
Number(3))
a 1
----- + -
b * c n
---------
3
括弧を付ける条件を検討する。
2項演算子の優先順位を定義する。
"/"については今回の表記では括弧を気にする必要はない。常に不要。
Mapで手動で定義する方法もあるが、定数を自分でincrementするのがイマイチ。
Map("|" -> 0, "||" -> 0, "&" -> 1, "&&" -> 1, ...)
優先度が同じグループ単位に配列に登録して、そこから自動生成する。
private val opGroups = Array(Set("|", "||"), Set("&", "&&"), ...)
private val precedence = {
val assocs = for {
i <- 0 until opGroups.length
op <- opGroups(i)
} yield op -> i
Map() ++ assocs
}
private val unaryPrecedence = opGroups.length
private val fractionPrecedence = -1
"yield op -> i"は"(op, i)"と同じ意味。
assocsはIndexedSeq[(String, Int)]だがMap()と++すると暗黙変換でMapになる
"opGroups.length"で最大優先度より一つ高い優先度が得られる。
"/"のfractionPrecedenceは特別な値にする。
pattern matchingを使って各演算子を再帰的に処理する。
private def format(e: Expr, enclPrec: Int): Element =
e match {
case Var(name) => elem(name)
case Number(num) =>
def stripDot(s: String) =
if (s endsWith ".0") s.substring(0, s.length - 2)
else s
elem(stripDot(num.toString))
case UnOp(op, arg) => elem(op) beside format(arg, unaryPrecedence)
case BinOp("/", left, right) =>
val top = format(left, fractionPrecedence)
val bot = format(right, fractionPrecedence)
val line = elem('-', top.width max bot.width, 1)
val frac = top above line above bot
if (enclPrec != fractionPrecedence) frac
else elem(" ") beside frac beside elem(" ")
case BinOp(op, left, right) =>
val opPrec = precedence(op)
val l = format(left, opPrec)
val r = format(right, opPrec + 1)
val oper = l beside elem(" "+ op +" ") beside r
if (enclPrec <= opPrec) oper
else elem("(") beside oper beside elem(")")
}
def format(e: Expr): Element = format(e, 0)
}
"enclPrec"はenclosing the expressionで一個外側のoperatorの優先度。
2項演算時に外側より内側の方が優先度が低かったら括弧が必要。
fraction,"/"が連続する場合は演算順序が分かるようにする必要がある。
後から割る方の横線を長くする。
先に割る方を返すときにスペースで膨らませることで実現。
"2.0"等の".0"を削除する処理も実装。
Elementクラスがセンタリングするように実装されているためそのまま使える。
** 凄い!!
15.9 Conclusion
case classesとpattern matchingを使う事で普通のオブジェクト指向言語より簡素に書ける。
scalaのpattern matchingはここで説明したものよりもっと強力。
自分のクラスに自分はpattern matchingを使いたいが、外部には公開したくない場合
extractors described in Chapter 24が使える。
pattern matchingについて説明する。
case classを使えば簡単にpattern matchingを使える。
木構造の様な再帰的なデータ構造にも使える。
sealed classes, Option type等pattern matchingの応用についても説明する。
最後に大規模な現実的な使用例も紹介する。
15.1 A simple example
例として算術を表現するライブラリを作る場合を考える
例えば、DSL(domain-specific language)の一部として設計している場合。
abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String,
left: Expr, right: Expr) extends Expr
Varは x, Numberは 1.0, UnOpは +,-, BinOpは+,-等になる。
実装が無いクラス定義では{}は省略できる。
abstract class Expr {} と同じ意味
Case classes
case修飾子を付けたクラス。
pattern matchingのmatch expressionとして使える!!
compilerが自動的に機能を付け加えてくれる。
factory methodの追加
newなしでVar("x")と出来る。
?? 自動的にcompanion objectを生成している??
constructorのparameterが自動的にval付き扱いになる。
自動的にfieldとして追加されると言う事。
toString, hashCode, equalsの自動追加。
equalsが追加されるとそれを呼び出す==の振る舞いも定義される。
?? "natural" implementationsとはどう言う事??
** 型だけを定義するような実装の少ないクラスを書くときに特に便利。
Pattern matching
def simplifyTop(expr: Expr): Expr = expr match {
case UnOp("-", UnOp("-", e)) => e // Double negation
case BinOp("+", e, Number(0)) => e // Adding zero
case BinOp("*", e, Number(1)) => e // Multiplying by one
case _ => expr
}
Javaのswitch文と似ている。
caseに定数だけではなくて任意のexpressionを使える。
?? 任意と言うよりは case classで定義されたconstructorのみ?
UnOp("-", UnOp("-", e))の様な再帰表現も使える。
?? C++のテンプレートに似ている? ループになる事は??
複数matchする場合は上が適用される。
?? 評価は平行して行われるかもしれない??
_ は全てにmatchする。defaultと同じ。一番最後に書く。
=> の後に何も書かないと何も実行しない。値は()になる。
pattern matching全体として値を返す。ifやforと同様。
fall throughしない
どれにもmatchしないとMatchErrorの例外が投げられる。
通常は最後にcase _ => を入れて例外を投げないようにすることが多い。
15.2 Kinds of patterns
パターンは定義と透過的で見たままで分かる。
どういうパターンがあるかについて気をつけるのみで良い。
Wildcard patterns
_は任意のオブジェクトにマッチさせられる。
case BinOp(_, _, _) => println(expr +"is a binary operation")
Constant patterns
それ自身とのみマッチする。定数やvalやsingleton object。
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list" // singleton object
Variable patterns
_と同様に任意のオブジェクトにマッチさせられる。
マッチしたオブジェクトをvariableとしてbindして後で使える。
** perlの正規表現の$1,$2...と同じように強力。
case somethingElse => "not zero: "+ somethingElse
Variable or constant?
小文字始まりだとパターン変数、それ以外は定数とみなされる!!
this.piや`pi`とすると小文字始まりも定数とみなされる。
``は予約語を普通の識別子と認識させる場合にも使える。
Constructor patterns
case classのconstructorとマッチさせる。とても強力。
constructorのパラメータにもパターンマッチが再帰的に適用される。
パラメータが自分自身のConstructor patternでもOK。
case BinOp("+", e, Number(0)) => println("a deep match")
BinOpクラスのコンストラクタとマッチする。
"+": 定数として同じオブジェクトとマッチする。
e: 変数として任意のオブジェクトとマッチして、bindされる。
Number(0): Numberクラスのコンストラクタとマッチする。
0: 定数として同じオブジェクトとマッチする。
Sequence patterns
List or Arrayの様なsequence typesもcase classと同様にマッチさせられる。
case List(0, _, _) => println("found it")
0個以上の任意のエントリのときは_*を指定する。
case List(0, _*) => println("found it")
?? _*以外に、0*で0の任意回の繰り返しとか、_+で1個以上とか出来ない??
Tuple patterns
tupleもsequenceと同様。
case (a, b, c) => println("matched "+ a + b + c)
Typed patterns
selectorのtypeとマッチするかを判定できる。
case s: String => s.length
selectorはAnyだがsはString。selectorをcastしている。
?? case s: _とか、case s: xとかで型を取り出したりは出来ない??
The operators isInstanceOf and asInstanceOfを使えば似たようなことが出来る。
if (x.isInstanceOf[String]) {
val s = x.asInstanceOf[String]
s.length
} else ...
上記のtype test & castは冗長。
pattern matchに誘導するために意図的にそう言語設計している。
case m: Map[_, _] => m.size
_は任意の型にマッチするワイルドカード。
(小文字始まりの!!)型変数を使える。
?? bindは出来ない?? 後で型引数として使う事も出来ない??
Type erasure
$ scala -uncheckedのオプションで上げて下記を実行
scala> def isIntIntMap(x: Any) = x match { case m: Map[Int, Int] => true }
<console>:5: warning: non variable type-argument Int in
type pattern is unchecked since it is eliminated by erasure
case m: Map[Int, Int] => true
scalaはjavaと同様にerasure modelを採用している。
実行時には型情報が分からない。
Map[]が[Int, Int]だと言う事が分からない。
Arrayは例外で型情報を実行時も保持している。
Variable binding
@を使ってマッチしたパターンの一部を変数に束縛出来る。
case UnOp("abs", e @ UnOp("abs", _)) => e
全体がマッチした時に、eにはUnOp("abs", _)の実体が束縛される。
15.3 Pattern guards
patternの後ろにifを付けてguardする事が出来る。
パターンにマッチして更にifが真ならマッチ。
case BinOp("+", x, x) => BinOp("*", x, Number(2))
これはエラーになる。パターン変数を一回しか書けないため。
case BinOp("+", x, y) if x == y => BinOp("*", x, Number(2))
これでx==yの時のみマッチする。
case n: Int if 0 < n => ...
Intで正の時のみマッチ。
case s: String if s(0) == 'a' => ...
文字列で一文字目が'a'の時のみマッチ。
15.4 Pattern overlaps
複数caseにマッチするような場合は上のcaseが適用される。
到達しないcaseがある場合はコンパイルエラーになる。
15.5 Sealed classes
scalaではコンパイル時にcaseに漏れがある事を検出出来ない。
部分コンパイルなので、一緒にコンパイルされてない部分での追加が分からない。
sealed classにすれば同一ファイル以外でのサブクラスの追加を禁止できる。
sealed abstract class Expr
case classを作る時はsealed出来ないか検討すべき。
sealedすればコンパイラがcase漏れを警告してくれる。
逆にコンテクストから入らないcaseがある事が分かっている場合の警告の抑制。
@unchecked anotationを使う。Chapter 25
(e: @unchecked) match {...}
15.6 The Option type
Optionと言う標準の型がある。
Some(x): 実際の値がxの場合。
None: 値が無い場合。
collectionに対する操作の結果として使われる場合が多い。
Mapのgetメソッド等。
pattern matchを使って値を取り出す。
x match {
case Some(s) => s
case None => "?"
}
Option TypeはScalaでは良くつかわれる。
javaだと値が無いときはnullを返す物が多い。
何がnullを返すのかを知っている必要がある。
nullチェックを忘れるリスクが高い。
nullを返す場合が少ないとnullチェックの漏れをテストでも検出出来ない。
scalaだとnullをvalue typeの型には返せない。
scalaだとOption typeを使う事を推奨。
値が返らない可能性がある事を型から把握できる。
nullチェックを忘れるとコンパイルが通らなくなるで直ぐに分かる。
15.7 Patterns everywhere
match以外にも色々な場所でpatternを使える。
Patterns in variable definitions
valやvarで値を定義するとき。
val myTuple = (123, "abc")
val (number, string) = myTuple // tupleを分解
val exp = new BinOp("*", Number(5), Number(1)) // case classを分解
val BinOp(op, left, right) = exp
Case sequences as partial functions
{}内のcaseの並びは関数リテラルになっている。
エントリポイント(パラメータリスト)と実体の組が複数ある。
val withDefault: Option[Int] => Int = {
case Some(x) => x
case None => 0
}
一部のパターンにのみ対応したpartial functionsにも出来る。
val second: PartialFunction[List[Int],Int] = {
case x :: y :: _ => y
}
PartialFunction型にしてコンパイラにPartialFunctionを示す。
isDefinedAtメソッドでcaseの定義済、未定義をチェックできる。
一般的には可能なら全てのcaseを網羅したfull関数を作ろうとするべき。
partialにする場合
来ないcaseに確信がある場合
isDefinedAtメソッドで定義済かを事前に調べる場合
Patterns in for expressions
for (Some(fruit) <- results) println(fruit)
for式の値の抽出に使える。
Some(fruit)にマッチしなかったら捨てられる。filterと同じ動き。
None以外だけを処理することが出来る。
15.8 A larger example
大規模で現実的なpattern matchingの応用として、数式の表現を考える。
Chapter 10で作成したlayout libraryも活用する。
内部表現はこの章で作成したExprクラスを活用する。
逆ポーランド記法に似た内部表現になる。
()についても省略出来るところは省略するようにする。
ExprFormatter: formatter class
((a / (b * c) + 1 / n) / 3) これを下記の様に表示する。
BinOp("/", BinOp("+", BinOp("/", Var("a"),
BinOp("*", Var("b"), Var("c")),
BinOp("/", Number(1),
Var("n")),
Number(3))
a 1
----- + -
b * c n
---------
3
括弧を付ける条件を検討する。
2項演算子の優先順位を定義する。
"/"については今回の表記では括弧を気にする必要はない。常に不要。
Mapで手動で定義する方法もあるが、定数を自分でincrementするのがイマイチ。
Map("|" -> 0, "||" -> 0, "&" -> 1, "&&" -> 1, ...)
優先度が同じグループ単位に配列に登録して、そこから自動生成する。
private val opGroups = Array(Set("|", "||"), Set("&", "&&"), ...)
private val precedence = {
val assocs = for {
i <- 0 until opGroups.length
op <- opGroups(i)
} yield op -> i
Map() ++ assocs
}
private val unaryPrecedence = opGroups.length
private val fractionPrecedence = -1
"yield op -> i"は"(op, i)"と同じ意味。
assocsはIndexedSeq[(String, Int)]だがMap()と++すると暗黙変換でMapになる
"opGroups.length"で最大優先度より一つ高い優先度が得られる。
"/"のfractionPrecedenceは特別な値にする。
pattern matchingを使って各演算子を再帰的に処理する。
private def format(e: Expr, enclPrec: Int): Element =
e match {
case Var(name) => elem(name)
case Number(num) =>
def stripDot(s: String) =
if (s endsWith ".0") s.substring(0, s.length - 2)
else s
elem(stripDot(num.toString))
case UnOp(op, arg) => elem(op) beside format(arg, unaryPrecedence)
case BinOp("/", left, right) =>
val top = format(left, fractionPrecedence)
val bot = format(right, fractionPrecedence)
val line = elem('-', top.width max bot.width, 1)
val frac = top above line above bot
if (enclPrec != fractionPrecedence) frac
else elem(" ") beside frac beside elem(" ")
case BinOp(op, left, right) =>
val opPrec = precedence(op)
val l = format(left, opPrec)
val r = format(right, opPrec + 1)
val oper = l beside elem(" "+ op +" ") beside r
if (enclPrec <= opPrec) oper
else elem("(") beside oper beside elem(")")
}
def format(e: Expr): Element = format(e, 0)
}
"enclPrec"はenclosing the expressionで一個外側のoperatorの優先度。
2項演算時に外側より内側の方が優先度が低かったら括弧が必要。
fraction,"/"が連続する場合は演算順序が分かるようにする必要がある。
後から割る方の横線を長くする。
先に割る方を返すときにスペースで膨らませることで実現。
"2.0"等の".0"を削除する処理も実装。
Elementクラスがセンタリングするように実装されているためそのまま使える。
** 凄い!!
15.9 Conclusion
case classesとpattern matchingを使う事で普通のオブジェクト指向言語より簡素に書ける。
scalaのpattern matchingはここで説明したものよりもっと強力。
自分のクラスに自分はpattern matchingを使いたいが、外部には公開したくない場合
extractors described in Chapter 24が使える。
2014年4月24日木曜日
Programming in Scala, First Edition Chapter 14
14. Assertions and Unit Testing
14.1 Assertions
assert()メソッドを使って書く。
assert(condition)
conditionが偽になるとAssertionError例外を投げる。
assert(condition, explanation)
AssertionErrorにexplanationからtoStringで取得した文字列を入れて投げる。
explanationはAny。scalaでは全ての型にtoStringが実装されている。
ensuring (w <= _.width)
assert()は何処に入れても良いが、良くあるのはreturnの直前。
この場合、assert()の後で保持したreturn値を返す様に書く必要がある。
ensuring()を使えばこれが簡単に出来る。
戻り値を引数に取ってBooleanを返すチェック関数を引数に取る。
関数の戻り値をチェックに渡してtrueなら戻り値を関数の戻り値として返す。
def function(): type = { ... } ensuring ((x: type): => {...}: Boolean)
?? ensuringに渡されているのは戻り値のimplicitly converted??
assersionやensuringはJVMの-ea,-daのcommand-line flagsで有効化できる。
有効にしたまま動かせば、実動作中に値の正常性をチェックできる。
ただし、この章ではテストデータを使った外部のユニットテストを扱う。
?? assert()を使うのは関数型の処理順非依存に反しない??
14.2 Unit testing in Scala
javaのunit test toolが使える。
JUnit, TestNG
scala用に作られたtoolもある
ScalaTest, specs, ScalaCheck
?? 2008年の段階なので今はもっと良いものがあるかも??
ここで扱うのはScalaTest
ScalaTestでも幾つかのテスト方法がある。
一番簡単なのはorg.scalatest.Suiteをmix-inしたクラスにtest methodsを定義する。
import org.scalatest.Suite
import Element.elem
class ElementSuite extends Suite {
def testUniformElement() {
val ele = elem('x', 2, 3)
assert(ele.width == 2)
}
}
インタープリタからも実行できる。
scala> (new ElementSuite).execute()
ScalaTestではsubtypeでexecuteをoverrideして他のスタイルを提供する。
関数値を使ったFunSuite
import org.scalatest.FunSuite
import Element.elem
class ElementSuite extends FunSuite {
test("elem result should have passed width") {
val ele = elem('x', 2, 3)
assert(ele.width == 2)
}
}
testと言うメソッドがprimary constructorから呼び出される。
{} はテスト用の関数値(by-name parameter)で登録されて後で実行される。
テスト関数の名前を付ける必要が無い。
テストの説明を記載出来る。
14.3 Informative failure reports
assert(ele.width == 2)
単に例外が投げられるだけでどんな値だったかは分からない。
assert(ele.width === 2)
"3 did not equal 2"等と出る。
expect(2) {
ele.width
}
"Expected 2, but got 3"等と出る。
intercept[IllegalArgumentException] {
elem('x', -2, 3)
}
IllegalArgumentExceptionが投げられないとNGになるテスト。
14.4 Using JUnit and TestNG
JUnit
Kent Beck and Erich Gammaが作ったもっともポピュラーなフレームワーク
JUnit3と4がある。
junit.framework.TestCaseをextendsすればscalaでも書ける。
書いたテストケースはJunit上で実行出来る。
org.scalatest.junit.JUnit3SuiteをextendsすればScalaTestのassertが使える。
書いたテストケースはJunit上でもScalaTest上でも実行出来る。
JUnit4はSection 29.2参照。
JUnitWrapperSuite
javaで書かれたJunitのテストをScalaTestで実行可能。
TestNG
Cedric Beust and Alexandru Popescu作のオープンソース
Junitと同様にscalaで書いたりjavaで書いたものをScalaTest上で実行出来る。
14.5 Tests as specifications
behavior-driven development (BDD)
コードの振る舞いを人が読める仕様記載する事が強調されている。
記載されている振る舞いを確かめるためのテストを付随させる。
ScalaTestにはSpecというtraitでこれをサポートしている。
import org.scalatest.Spec
class ElementSpec extends Spec {
describe("A UniformElement") {
it("should have a width equal to the passed value") {
val ele = elem('x', 2, 3)
assert(ele.width === 2)
}
it("should throw an IAE if passed a negative width") {
...
describeにsubject、テストの主題を記載して、
itにbehavior、個々の振る舞いを記載する。
テストの実行結果も可読性があるものになっている。
The specs testing framework
Eric Torreborre作のオープンソースで、違うBDDスタイルをサポートしている。
import org.specs._
object ElementSpecification extends Specification {
"A UniformElement" should {
"have a width equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.width must be_==(2)
}
"throw an IAE if passed a negative width" in {
elem('x', -2, 3) must
throwA[IllegalArgumentException]
...
"must be_==", "must throwA"の様なmatcherを色々用意されている。
自作のmatcherを定義する事も出来る。
trait org.specs.SpecsMatchersをmix-inすればScalaTest,JUnit,TestNGでも使える。
specsは単独でも使えるし、ScalaTest, JUnitから実行する事も出来る。
14.6 Property-based testing
ScalaCheck
Rickard Nilsso作のオープンソース
特定のpropertyがある条件でテストを満たすかを確認できる。
import org.scalatest.prop.FunSuite
import org.scalacheck.Prop._
import Element.elem
class ElementSuite extends FunSuite {
test("elem result should have passed width", (w: Int) =>
w > 0 ==> (elem('x', w, 3).width == w)
)
...
implication operator, ==> 包含演算子
w > 0 ==> (elem('x', w, 3).width == w)
==>がポイント。左項が真なら右項も真になるはずと言う意味。
ScalaCheckが実際は数百のパターンを生成して試験を行ってくれる!!
?? パターン生成方法と数は??
ScalaTest's Checkers trait
import org.scalatest.junit.JUnit3Suite
import org.scalatest.prop.Checkers
import org.scalacheck.Prop._
import Element.elem
class ElementSuite extends JUnit3Suite with Checkers {
def testUniformElement() {
check((w: Int) => w > 0 ==> (elem('x', w, 3).width == w))
check((h: Int) => h > 0 ==> (elem('x', 2, h).height == h))
...
checkメソッドを使えば複数propertyを使ったテストが出来る?
?? 上の例では複数propertyのは使っていない??
14.7 Organizing and running tests
この章で紹介したフレームワークはテストを組織化する方法を提供している。
ScalaTestについてちょっと触れてみる。詳しくはScalaTestのドキュメント参照。
ScalaTestではSuiteを多段にネストして大規模なテストを構成する。
Suiteはツリー構造になってrootを指定すると配下全てが実施される。
ネスト登録は手動でも自動でも出来る。
ネストされるクラスのメソッドを上書きする。
登録用のSuperSuiteクラスのconstructorに渡す。
package nameをScalaTest's Runnerに登録する。
自動的にSuiteを探してroot Suite配下にネストする。
ScalaTest's Runnerをコマンドラインから実行してテストを実施できる。
antのtaskとしても実施出来る。
antと言うのはjavaのアプリケーションをビルドするためのツール。
Suiteの名前を指定する。
prefixを指定するとRunnerが自動的にSuiteを探して実行する。
オプションでテストのjarファイルのrunpathを指定できる。
結果の表示をコントロールするreporterを一つもしくは複数指定できる。
ScalaTestにはScalaTest自体の実装をテストするSuiteSuiteがある。
$ scala -cp scalatest-0.9.4.jar org.scalatest.tools.Runner
-p "scalatest-0.9.4-tests.jar" -s org.scalatest.SuiteSuite
-cpはScalaTestのjarのクラスパス
org.scalatest.tools.RunnerはRunner appのfully qualified name
-pはテストSuiteのjarのrunpath
-sは実行するてすとSuite
14.8 Conclusion
2パターンのテスト方法
実働コードにassertionを直接混ぜるケース
別途テストSuiteを作成するケース
javaのJUnitやTestNGも使えるし、ScalaTest,ScalaCheck,specsも使える。
scalaの言語仕様から少し離れても説明べき重要な技術と感じている。
14.1 Assertions
assert()メソッドを使って書く。
assert(condition)
conditionが偽になるとAssertionError例外を投げる。
assert(condition, explanation)
AssertionErrorにexplanationからtoStringで取得した文字列を入れて投げる。
explanationはAny。scalaでは全ての型にtoStringが実装されている。
ensuring (w <= _.width)
assert()は何処に入れても良いが、良くあるのはreturnの直前。
この場合、assert()の後で保持したreturn値を返す様に書く必要がある。
ensuring()を使えばこれが簡単に出来る。
戻り値を引数に取ってBooleanを返すチェック関数を引数に取る。
関数の戻り値をチェックに渡してtrueなら戻り値を関数の戻り値として返す。
def function(): type = { ... } ensuring ((x: type): => {...}: Boolean)
?? ensuringに渡されているのは戻り値のimplicitly converted??
assersionやensuringはJVMの-ea,-daのcommand-line flagsで有効化できる。
有効にしたまま動かせば、実動作中に値の正常性をチェックできる。
ただし、この章ではテストデータを使った外部のユニットテストを扱う。
?? assert()を使うのは関数型の処理順非依存に反しない??
14.2 Unit testing in Scala
javaのunit test toolが使える。
JUnit, TestNG
scala用に作られたtoolもある
ScalaTest, specs, ScalaCheck
?? 2008年の段階なので今はもっと良いものがあるかも??
ここで扱うのはScalaTest
ScalaTestでも幾つかのテスト方法がある。
一番簡単なのはorg.scalatest.Suiteをmix-inしたクラスにtest methodsを定義する。
import org.scalatest.Suite
import Element.elem
class ElementSuite extends Suite {
def testUniformElement() {
val ele = elem('x', 2, 3)
assert(ele.width == 2)
}
}
インタープリタからも実行できる。
scala> (new ElementSuite).execute()
ScalaTestではsubtypeでexecuteをoverrideして他のスタイルを提供する。
関数値を使ったFunSuite
import org.scalatest.FunSuite
import Element.elem
class ElementSuite extends FunSuite {
test("elem result should have passed width") {
val ele = elem('x', 2, 3)
assert(ele.width == 2)
}
}
testと言うメソッドがprimary constructorから呼び出される。
{} はテスト用の関数値(by-name parameter)で登録されて後で実行される。
テスト関数の名前を付ける必要が無い。
テストの説明を記載出来る。
14.3 Informative failure reports
assert(ele.width == 2)
単に例外が投げられるだけでどんな値だったかは分からない。
assert(ele.width === 2)
"3 did not equal 2"等と出る。
expect(2) {
ele.width
}
"Expected 2, but got 3"等と出る。
intercept[IllegalArgumentException] {
elem('x', -2, 3)
}
IllegalArgumentExceptionが投げられないとNGになるテスト。
14.4 Using JUnit and TestNG
JUnit
Kent Beck and Erich Gammaが作ったもっともポピュラーなフレームワーク
JUnit3と4がある。
junit.framework.TestCaseをextendsすればscalaでも書ける。
書いたテストケースはJunit上で実行出来る。
org.scalatest.junit.JUnit3SuiteをextendsすればScalaTestのassertが使える。
書いたテストケースはJunit上でもScalaTest上でも実行出来る。
JUnit4はSection 29.2参照。
JUnitWrapperSuite
javaで書かれたJunitのテストをScalaTestで実行可能。
TestNG
Cedric Beust and Alexandru Popescu作のオープンソース
Junitと同様にscalaで書いたりjavaで書いたものをScalaTest上で実行出来る。
14.5 Tests as specifications
behavior-driven development (BDD)
コードの振る舞いを人が読める仕様記載する事が強調されている。
記載されている振る舞いを確かめるためのテストを付随させる。
ScalaTestにはSpecというtraitでこれをサポートしている。
import org.scalatest.Spec
class ElementSpec extends Spec {
describe("A UniformElement") {
it("should have a width equal to the passed value") {
val ele = elem('x', 2, 3)
assert(ele.width === 2)
}
it("should throw an IAE if passed a negative width") {
...
describeにsubject、テストの主題を記載して、
itにbehavior、個々の振る舞いを記載する。
テストの実行結果も可読性があるものになっている。
The specs testing framework
Eric Torreborre作のオープンソースで、違うBDDスタイルをサポートしている。
import org.specs._
object ElementSpecification extends Specification {
"A UniformElement" should {
"have a width equal to the passed value" in {
val ele = elem('x', 2, 3)
ele.width must be_==(2)
}
"throw an IAE if passed a negative width" in {
elem('x', -2, 3) must
throwA[IllegalArgumentException]
...
"must be_==", "must throwA"の様なmatcherを色々用意されている。
自作のmatcherを定義する事も出来る。
trait org.specs.SpecsMatchersをmix-inすればScalaTest,JUnit,TestNGでも使える。
specsは単独でも使えるし、ScalaTest, JUnitから実行する事も出来る。
14.6 Property-based testing
ScalaCheck
Rickard Nilsso作のオープンソース
特定のpropertyがある条件でテストを満たすかを確認できる。
import org.scalatest.prop.FunSuite
import org.scalacheck.Prop._
import Element.elem
class ElementSuite extends FunSuite {
test("elem result should have passed width", (w: Int) =>
w > 0 ==> (elem('x', w, 3).width == w)
)
...
implication operator, ==> 包含演算子
w > 0 ==> (elem('x', w, 3).width == w)
==>がポイント。左項が真なら右項も真になるはずと言う意味。
ScalaCheckが実際は数百のパターンを生成して試験を行ってくれる!!
?? パターン生成方法と数は??
ScalaTest's Checkers trait
import org.scalatest.junit.JUnit3Suite
import org.scalatest.prop.Checkers
import org.scalacheck.Prop._
import Element.elem
class ElementSuite extends JUnit3Suite with Checkers {
def testUniformElement() {
check((w: Int) => w > 0 ==> (elem('x', w, 3).width == w))
check((h: Int) => h > 0 ==> (elem('x', 2, h).height == h))
...
checkメソッドを使えば複数propertyを使ったテストが出来る?
?? 上の例では複数propertyのは使っていない??
14.7 Organizing and running tests
この章で紹介したフレームワークはテストを組織化する方法を提供している。
ScalaTestについてちょっと触れてみる。詳しくはScalaTestのドキュメント参照。
ScalaTestではSuiteを多段にネストして大規模なテストを構成する。
Suiteはツリー構造になってrootを指定すると配下全てが実施される。
ネスト登録は手動でも自動でも出来る。
ネストされるクラスのメソッドを上書きする。
登録用のSuperSuiteクラスのconstructorに渡す。
package nameをScalaTest's Runnerに登録する。
自動的にSuiteを探してroot Suite配下にネストする。
ScalaTest's Runnerをコマンドラインから実行してテストを実施できる。
antのtaskとしても実施出来る。
antと言うのはjavaのアプリケーションをビルドするためのツール。
Suiteの名前を指定する。
prefixを指定するとRunnerが自動的にSuiteを探して実行する。
オプションでテストのjarファイルのrunpathを指定できる。
結果の表示をコントロールするreporterを一つもしくは複数指定できる。
ScalaTestにはScalaTest自体の実装をテストするSuiteSuiteがある。
$ scala -cp scalatest-0.9.4.jar org.scalatest.tools.Runner
-p "scalatest-0.9.4-tests.jar" -s org.scalatest.SuiteSuite
-cpはScalaTestのjarのクラスパス
org.scalatest.tools.RunnerはRunner appのfully qualified name
-pはテストSuiteのjarのrunpath
-sは実行するてすとSuite
14.8 Conclusion
2パターンのテスト方法
実働コードにassertionを直接混ぜるケース
別途テストSuiteを作成するケース
javaのJUnitやTestNGも使えるし、ScalaTest,ScalaCheck,specsも使える。
scalaの言語仕様から少し離れても説明べき重要な技術と感じている。
2014年4月23日水曜日
2014/4/22(火)
夜中に食べた自家製マフィンが美味しかった。
久々にshell scriptを使えた。sh,bash,ksh,csh,tcsh,zshとshellだけでも奥が深い。
emacsのshellも低機能なCLIにアクセスするときはヒストリも使えて便利。
gnu-packのemacsでediffがエラーになった。(setenv "LANG" "C")で解決。
ensimeインストール。設定はこれから。
sbt-modeインストール。2/6ファイルコンパイルエラーになったが使える??
scala-mode2をversionアップ。
久々にshell scriptを使えた。sh,bash,ksh,csh,tcsh,zshとshellだけでも奥が深い。
emacsのshellも低機能なCLIにアクセスするときはヒストリも使えて便利。
gnu-packのemacsでediffがエラーになった。(setenv "LANG" "C")で解決。
ensimeインストール。設定はこれから。
sbt-modeインストール。2/6ファイルコンパイルエラーになったが使える??
scala-mode2をversionアップ。
2014/4/21(月)
エンジョイシンプルイングリッシュは結構聴きやすそう!
忙しい中Programming in Scala, First EditionのChapter13を完了!
久々に一人でのびのび寝られた!
忙しい中Programming in Scala, First EditionのChapter13を完了!
久々に一人でのびのび寝られた!
Programming in Scala, First Edition Chapter 13
13. Packages and Imports
他の部分への影響を最小にすることが重要。
モジュラースタイルがその一つの解決策。
小さい部品に分割する。
内部(implementation)と外部(interface)がある。
内部の変更は同じモジュールを開発している人にのみ影響する。
外部の変更は他のモジュールを開発している人にも影響すr。
packageの配置、importによる名前の可視化、access modifierのる可視性の制御。
Javaと同様の部分も多いが、より一貫性を強化した部分もある。
13.1 Packages
パッケージはJavaと同様に階層構造になっている。
これまでの例はパッケージ名未指定のunnamed package内として扱われていた。
Javaと同様の定義方法。
package bobsrockets.navigation
class Navigator
ファイル全体が同一のパッケージになる。
C#風の定義方法。
package bobsrockets.navigation {
class Navigator
package tests {
class NavigatorSuite
...
一つのファイルに複数のパッケージを定義。
ネスト構造が取れる。
パッケージの名前はjavaと同じreverse-domain-nameに従ったほうが良い。
そうすると、com.bobsrockets.navigationになるかもしれない。
本書では簡単にするためにcom.は省略する。
パッケージも他の名前と同様に同じ名前があると内側が優先して参照される。
外側を参照したいときは親のパッケージ名も指定すれば良い。
最も外側を指定したいときは__root__.を親として指定する。
13.2 Imports
import節を使えばprefixを省略して名前にアクセス出来る。
import bobsdelights.Fruit
Fruitをprefix無しに使える。javaのsingle type import
import bobsdelights._
bobsdelights内の全ての名前にアクセス出来る。javaのon-demand import
javaだと*を使うがscalaでは*は記述子に使える普通の文字だから使えない。
import bobsdelights.Fruits._
Fruitのメンバへのアクセス。javaのimport of static class fields
先頭だけではなくてどこにでも宣言できる。
ブロックの内側でも宣言出来る。
パッケージやsingleton以外の任意の値の名前についてimport出来る。
関数の引数のメンバをimportすることも出来る。
オブジェクトをモジュールとして使うときに便利。Chapter 27
パッケージ自体の中でも自分をimport出来る。
選択的にimport出来る。selector clause
import Fruits.{Apple, Orange}
Apple, Orangeだけをimport
import Fruits.{Apple => McIntosh, Orange}
AppleをMcIntoshと言う名前に変えてimport、Orangeをimport
import Fruits.{Apple => McIntosh, _}
AppleをMcIntoshと言う名前に変えてimport、それ以外全てをimport
import Fruits.{Apple => _, _}
Appleを名無しにして隠して、それ以外全てをimport
selector clauseは左優先で解釈される。
_は残り全てを吸い込むので一番右に書く必要がある。
import Fruits.Apple は import Fruits.{Apple} の省略形。
13.3 Implicit imports
暗黙的に下記の3つが全ての.scalaにimportされている。
import java.lang._ // everything in the java.lang package
import scala._ // everything in the scala package
import Predef._ // everything in the Predef object
?? scalaとPredefを分けたのは何故??
shadowに注意。scalaとjava.langで同名の場合はscalaにアクセスする事になる。
?? scalaのJVMベースの代わりに、.NETベースの実装もある??
13.4 Access modifiers
Private members
クラスやオブジェクト定義の内側からしかアクセスできない。javaと同じ。
インナークラスのprivate memberは外側のクラスからアクセス出来ない。
更に内側のクラスからならアクセス可能。
javaと異なる。javaはインナークラスのprivate menberへのアクセス可能。
Protected members
定義しているクラスのサブクラスからしかアクセス出来ない。javaより厳密。
同じパッケージ内でもサブクラス以外からはアクセス出来ない。javaはできる。
Public members
何もつけなければpublic。どこ彼でもアクセスできる。
Scope of protection
scalaのAccess modifiersはqualifiersと一緒に使うことが出来る。
private[X]
アクセス可能な範囲をXにする。
パッケージ全体も可能。
一番外側のクラスにすればjavaのインナークラスのprivatreと同じ。
private[X]だと同じインスタンスからしかアクセス出来ない。object-private
?? documentationに便利??
?? general variance annotations, Section 19.7で使う??
protected[X]
サブクラスからのアクセス+private[X]のアクセスと同等。
パッケージ全体にするとjavaのprotectedと同じ。
Visibility and companion objects
companion objectsとclass間ではお互いのprivateメンバにもアクセスできる。
?? ここだけ例外的??
companion objectsのprotectedメンバは意味がない。
対のクラスのサブクラスからアクセス出来ないから。
javaのprotected staticメンバであればアクセスできる。
?? 同じ事をしたくても不可能??
13.5 Conclusion
Chapter 27でより単なる分割を超える柔軟なモジュールシステムを説明。
モジュールのパラメータ化や継承についても学ぶ。
他の部分への影響を最小にすることが重要。
モジュラースタイルがその一つの解決策。
小さい部品に分割する。
内部(implementation)と外部(interface)がある。
内部の変更は同じモジュールを開発している人にのみ影響する。
外部の変更は他のモジュールを開発している人にも影響すr。
packageの配置、importによる名前の可視化、access modifierのる可視性の制御。
Javaと同様の部分も多いが、より一貫性を強化した部分もある。
13.1 Packages
パッケージはJavaと同様に階層構造になっている。
これまでの例はパッケージ名未指定のunnamed package内として扱われていた。
Javaと同様の定義方法。
package bobsrockets.navigation
class Navigator
ファイル全体が同一のパッケージになる。
C#風の定義方法。
package bobsrockets.navigation {
class Navigator
package tests {
class NavigatorSuite
...
一つのファイルに複数のパッケージを定義。
ネスト構造が取れる。
パッケージの名前はjavaと同じreverse-domain-nameに従ったほうが良い。
そうすると、com.bobsrockets.navigationになるかもしれない。
本書では簡単にするためにcom.は省略する。
パッケージも他の名前と同様に同じ名前があると内側が優先して参照される。
外側を参照したいときは親のパッケージ名も指定すれば良い。
最も外側を指定したいときは__root__.を親として指定する。
13.2 Imports
import節を使えばprefixを省略して名前にアクセス出来る。
import bobsdelights.Fruit
Fruitをprefix無しに使える。javaのsingle type import
import bobsdelights._
bobsdelights内の全ての名前にアクセス出来る。javaのon-demand import
javaだと*を使うがscalaでは*は記述子に使える普通の文字だから使えない。
import bobsdelights.Fruits._
Fruitのメンバへのアクセス。javaのimport of static class fields
先頭だけではなくてどこにでも宣言できる。
ブロックの内側でも宣言出来る。
パッケージやsingleton以外の任意の値の名前についてimport出来る。
関数の引数のメンバをimportすることも出来る。
オブジェクトをモジュールとして使うときに便利。Chapter 27
パッケージ自体の中でも自分をimport出来る。
選択的にimport出来る。selector clause
import Fruits.{Apple, Orange}
Apple, Orangeだけをimport
import Fruits.{Apple => McIntosh, Orange}
AppleをMcIntoshと言う名前に変えてimport、Orangeをimport
import Fruits.{Apple => McIntosh, _}
AppleをMcIntoshと言う名前に変えてimport、それ以外全てをimport
import Fruits.{Apple => _, _}
Appleを名無しにして隠して、それ以外全てをimport
selector clauseは左優先で解釈される。
_は残り全てを吸い込むので一番右に書く必要がある。
import Fruits.Apple は import Fruits.{Apple} の省略形。
13.3 Implicit imports
暗黙的に下記の3つが全ての.scalaにimportされている。
import java.lang._ // everything in the java.lang package
import scala._ // everything in the scala package
import Predef._ // everything in the Predef object
?? scalaとPredefを分けたのは何故??
shadowに注意。scalaとjava.langで同名の場合はscalaにアクセスする事になる。
?? scalaのJVMベースの代わりに、.NETベースの実装もある??
13.4 Access modifiers
Private members
クラスやオブジェクト定義の内側からしかアクセスできない。javaと同じ。
インナークラスのprivate memberは外側のクラスからアクセス出来ない。
更に内側のクラスからならアクセス可能。
javaと異なる。javaはインナークラスのprivate menberへのアクセス可能。
Protected members
定義しているクラスのサブクラスからしかアクセス出来ない。javaより厳密。
同じパッケージ内でもサブクラス以外からはアクセス出来ない。javaはできる。
Public members
何もつけなければpublic。どこ彼でもアクセスできる。
Scope of protection
scalaのAccess modifiersはqualifiersと一緒に使うことが出来る。
private[X]
アクセス可能な範囲をXにする。
パッケージ全体も可能。
一番外側のクラスにすればjavaのインナークラスのprivatreと同じ。
private[X]だと同じインスタンスからしかアクセス出来ない。object-private
?? documentationに便利??
?? general variance annotations, Section 19.7で使う??
protected[X]
サブクラスからのアクセス+private[X]のアクセスと同等。
パッケージ全体にするとjavaのprotectedと同じ。
Visibility and companion objects
companion objectsとclass間ではお互いのprivateメンバにもアクセスできる。
?? ここだけ例外的??
companion objectsのprotectedメンバは意味がない。
対のクラスのサブクラスからアクセス出来ないから。
javaのprotected staticメンバであればアクセスできる。
?? 同じ事をしたくても不可能??
13.5 Conclusion
Chapter 27でより単なる分割を超える柔軟なモジュールシステムを説明。
モジュールのパラメータ化や継承についても学ぶ。
2014年4月21日月曜日
2014/4/20(日)
先版は久々に11:30位に寝て、朝まだのんびり寝られた。サン、あんずに起される。
#今晩はすでに3時を過ぎてしまったが。。。
あんずのプリキュアのダンスがますます上手になっている。
サンはタブレットのピアノゲームが上達している。
ピアノの練習としては微妙だが、マリンバの練習にはなるかもしれない。
そういえば木琴にはマリンバとシロフォンと2種類あるそうだ。
見た目はあまり変わらないが、マリンバの様が柔らかくて長く響き、オーケストラと相性が良い。
シロフォンの方が固く自己主張が強い響きになっている様だ。
マリンバが偶数倍音、シロフォンが奇数倍音で調律されていて、それが音の違いになっているとのこと。
GitHubのアカウント作成をして、IntelliJ IDEAから使えるようにした。別エントリに記載。
GitHubのクライアントをインストールした際にWindowsのPowerShellのコマンドも一緒に入っていた。
powershellは、.netベースのスクリプト実行環境で、最近のWindowsの管理機能はPowerShell上に構築されているらしい。
GUIから呼び出しているPowerShellのコマンドがわかれば、管理機能のスクリプト化が可能と言うこと。
codecademy 30 streak 達成!
#今晩はすでに3時を過ぎてしまったが。。。
あんずのプリキュアのダンスがますます上手になっている。
サンはタブレットのピアノゲームが上達している。
ピアノの練習としては微妙だが、マリンバの練習にはなるかもしれない。
そういえば木琴にはマリンバとシロフォンと2種類あるそうだ。
見た目はあまり変わらないが、マリンバの様が柔らかくて長く響き、オーケストラと相性が良い。
シロフォンの方が固く自己主張が強い響きになっている様だ。
マリンバが偶数倍音、シロフォンが奇数倍音で調律されていて、それが音の違いになっているとのこと。
GitHubのアカウント作成をして、IntelliJ IDEAから使えるようにした。別エントリに記載。
GitHubのクライアントをインストールした際にWindowsのPowerShellのコマンドも一緒に入っていた。
powershellは、.netベースのスクリプト実行環境で、最近のWindowsの管理機能はPowerShell上に構築されているらしい。
GUIから呼び出しているPowerShellのコマンドがわかれば、管理機能のスクリプト化が可能と言うこと。
codecademy 30 streak 達成!
GitHubをIntelliJ ideaから使う
GithubをIntelliJ ideaから使ってみた。以下は設定などのログ。
色々ごちゃごちゃやったので、多少抜けや順番違いがあるかも知れない。。。
(0) cはインストール済み。Windows8.0環境。
(1) GitHubにアカウント作成
https://github.com/
にアクセスしてアカウントを作成するだけ。特に難しい所はない。
無料だとプライベートなリポジトリが作れないが、とりあえずそれで十分。
(2) Gitクライアントインストール
とりあえず公式サイトで勧められていたフリーの下記をインストールしてみた。
GitHub for Windows
今の所普通のGitの方を入れるべきだった気がしている。。。
(3) IntelliJ ideaの設定
File -> Settings から git を検索(IntelliJのこれらの検索機能は便利)
IDE Settings の Plugins で Git Integration と GitHub が有効になっている事を確認
Project Settings の Version Control の GitHub と Gitを設定
GitHubのhostは github.com にする。
Test ボタンを押すと上手く接続出来るか確認できる。
Gitの方はgitコマンドのフルパスを設定する必要ある。
ここでGUIの GitHub for Windows しか入れていないのでコマンドの場所が下記になってしまった。。。
C:\Users\ユーザ名\AppData\Local\GitHub\PortableGit_054f2e797ebafd44a30203088cd3d58663c627ef\cmd\git.exe
今のところ他に良い指定方法がわからない。
ショートカットを使ったり、ClickOnceアプリを使ったりも出来るかもしれない。
(4) IntelliJ ideaからcommit
プロジェクトのtop directoryを選択して
VCS -> Git -> Add で必要なファイルをAddしてから
VCS -> Git -> Commit Directory でまとめてcommit
(5) Git Shellからremote repositoryを指定
(4)の状態だとpushが出来ない。これを出来るようにするにはコマンドラインからgitコマンドで設定する必要ある。
GitHub for Windows と一緒にインストールされた Git Shellを実行。
DefaultだとWindowsのPowerShellが起動する。
C:\Users\ユーザ名\IdeaProjects\プロジェクト名 に移動。下記を実行。
git remote add origin https://github.com/ユーザ名/プロジェクト名.git
これでIntelliJからpushやpullが出来るようになるはず。
実際は先に場所を間違ってremote addしてしまったので、
git remote set-url https://github.com/ユーザ名/プロジェクト名.git
で上書きしている。
(6) .gitignore の作成
C:\Users\ユーザ名\IdeaProjects\プロジェクト名 に下記の内容で .gitignore を作成。
下記のファイルはIntelliJでcommit対象になっていない。
out/
.idea/workspace.xml
.gitignore
(7) README.md の作成
GitHubの自分のページに行ってリポジトリを見てみると、「README.mdを作成して」言われる。
C:\Users\ユーザ名\IdeaProjects\プロジェクト名 の直下に README.md を作成する。
内容はなんでも良い様子。Add, Commit, Pushしてremote repogitoryにも反映させる。
(8) Markdown plugin のインストール
IntelliJ上でREADME.mdを開くと、プラグインをインストールする様に言われる。
GitHubのREAMEにも使えそうなのでインストール。
(その他)
GitHub for WindowsのGit Shellは使っているが、GUIの方はあまり使いこなせていない。
プロジェクト単位にローカルのディレクトリを変更したいのだが、やり方がイマイチ分からない。
色々ごちゃごちゃやったので、多少抜けや順番違いがあるかも知れない。。。
(0) cはインストール済み。Windows8.0環境。
(1) GitHubにアカウント作成
https://github.com/
にアクセスしてアカウントを作成するだけ。特に難しい所はない。
無料だとプライベートなリポジトリが作れないが、とりあえずそれで十分。
(2) Gitクライアントインストール
とりあえず公式サイトで勧められていたフリーの下記をインストールしてみた。
GitHub for Windows
今の所普通のGitの方を入れるべきだった気がしている。。。
(3) IntelliJ ideaの設定
File -> Settings から git を検索(IntelliJのこれらの検索機能は便利)
IDE Settings の Plugins で Git Integration と GitHub が有効になっている事を確認
Project Settings の Version Control の GitHub と Gitを設定
GitHubのhostは github.com にする。
Test ボタンを押すと上手く接続出来るか確認できる。
Gitの方はgitコマンドのフルパスを設定する必要ある。
ここでGUIの GitHub for Windows しか入れていないのでコマンドの場所が下記になってしまった。。。
C:\Users\ユーザ名\AppData\Local\GitHub\PortableGit_054f2e797ebafd44a30203088cd3d58663c627ef\cmd\git.exe
今のところ他に良い指定方法がわからない。
ショートカットを使ったり、ClickOnceアプリを使ったりも出来るかもしれない。
(4) IntelliJ ideaからcommit
プロジェクトのtop directoryを選択して
VCS -> Git -> Add で必要なファイルをAddしてから
VCS -> Git -> Commit Directory でまとめてcommit
(5) Git Shellからremote repositoryを指定
(4)の状態だとpushが出来ない。これを出来るようにするにはコマンドラインからgitコマンドで設定する必要ある。
GitHub for Windows と一緒にインストールされた Git Shellを実行。
DefaultだとWindowsのPowerShellが起動する。
C:\Users\ユーザ名\IdeaProjects\プロジェクト名 に移動。下記を実行。
git remote add origin https://github.com/ユーザ名/プロジェクト名.git
これでIntelliJからpushやpullが出来るようになるはず。
実際は先に場所を間違ってremote addしてしまったので、
git remote set-url https://github.com/ユーザ名/プロジェクト名.git
で上書きしている。
(6) .gitignore の作成
C:\Users\ユーザ名\IdeaProjects\プロジェクト名 に下記の内容で .gitignore を作成。
下記のファイルはIntelliJでcommit対象になっていない。
out/
.idea/workspace.xml
.gitignore
(7) README.md の作成
GitHubの自分のページに行ってリポジトリを見てみると、「README.mdを作成して」言われる。
C:\Users\ユーザ名\IdeaProjects\プロジェクト名 の直下に README.md を作成する。
内容はなんでも良い様子。Add, Commit, Pushしてremote repogitoryにも反映させる。
(8) Markdown plugin のインストール
IntelliJ上でREADME.mdを開くと、プラグインをインストールする様に言われる。
GitHubのREAMEにも使えそうなのでインストール。
(その他)
GitHub for WindowsのGit Shellは使っているが、GUIの方はあまり使いこなせていない。
プロジェクト単位にローカルのディレクトリを変更したいのだが、やり方がイマイチ分からない。
2014年4月19日土曜日
2014/4/19(土)
今日はいちごの授業参観日だった。
朝8:45スタートと早かったので、平日と同じ時間にあんずが起してくれた。あまりのんびり寝られなかった。
新校舎には今回初めて入ったが、立派なので驚いた。
教室はオープン教室になっていてドアはなくて、移動可能なパーティションがあるだけ。
授業参観中も廊下の音がかなり煩かったが。。。
授業の内容は自己紹介の作文の発表。
おばあちゃんちが好きだとか、いちごが好きだとか、ピアノが得意だとか、中々良く出来ていた。
授業の後は一時間位サンとあんずといちごとみんなで遊べた。
サンの連続逆上がりがさらに上達していた。
帰宅後は少し昼寝出来た。ここ一週間は毎日睡眠時間が短かったので助かった。
いちごは宿題の後友達の家に遊びに行って、サンは自称コンピュータを工作していた。
夕方にはBSのパラボラの向きを調整して映る様にした。
向きを変えても変わらなくて、一度外してつけたら映るようになった。どこかが断線しかけている可能性が高い。
取りあえず今晩の「ごちそうさん」のスペシャルは入れれそうなので良しとする。
NHKラジオ英語番組の「エンジョイシンプルイングリッシュ」を聞き始めた。
毎日5分だけど続けるのは頑張りが必要そう。物語中心なので内容を楽しめれば続けられそうだが。。。
scalaにの方はsbtをインストールした、Dispatchを入れてbootcampのダウンロードするソフトを書きたいのだが、中々そこまでたどり着けない。。。
今日は書くことがあまり無い気がしていたが、時間を追って書いてみると結構色々あることを再認識出来た。
他にもLang-8, studypuls, サンといちごのピアノ、あんずのお喋り等書けるネタはまだまだある。。。
朝8:45スタートと早かったので、平日と同じ時間にあんずが起してくれた。あまりのんびり寝られなかった。
新校舎には今回初めて入ったが、立派なので驚いた。
教室はオープン教室になっていてドアはなくて、移動可能なパーティションがあるだけ。
授業参観中も廊下の音がかなり煩かったが。。。
授業の内容は自己紹介の作文の発表。
おばあちゃんちが好きだとか、いちごが好きだとか、ピアノが得意だとか、中々良く出来ていた。
授業の後は一時間位サンとあんずといちごとみんなで遊べた。
サンの連続逆上がりがさらに上達していた。
帰宅後は少し昼寝出来た。ここ一週間は毎日睡眠時間が短かったので助かった。
いちごは宿題の後友達の家に遊びに行って、サンは自称コンピュータを工作していた。
夕方にはBSのパラボラの向きを調整して映る様にした。
向きを変えても変わらなくて、一度外してつけたら映るようになった。どこかが断線しかけている可能性が高い。
取りあえず今晩の「ごちそうさん」のスペシャルは入れれそうなので良しとする。
NHKラジオ英語番組の「エンジョイシンプルイングリッシュ」を聞き始めた。
毎日5分だけど続けるのは頑張りが必要そう。物語中心なので内容を楽しめれば続けられそうだが。。。
scalaにの方はsbtをインストールした、Dispatchを入れてbootcampのダウンロードするソフトを書きたいのだが、中々そこまでたどり着けない。。。
今日は書くことがあまり無い気がしていたが、時間を追って書いてみると結構色々あることを再認識出来た。
他にもLang-8, studypuls, サンといちごのピアノ、あんずのお喋り等書けるネタはまだまだある。。。
2014/4/18(金)
あんずが朝パンを半分くれた。
英語で読む村上春樹「踊る小人」第2回完了!
今週は忙しかったが何とか乗り切れた。
最近寝不足が続いている。今週は大体3時過ぎにねて7時20分位に起きている。
一日や二日なら何とかなるが、一週間続くと体力的に限界を感じる。
明日はいちごの授業参観なのでそろそろ寝ないと。。。
[Programming in Scala, First Edition]
2014/4/18 150 chapter12 3-8/8
Scalaの特徴であるtraitについて学んだ。
英語で読む村上春樹「踊る小人」第2回完了!
今週は忙しかったが何とか乗り切れた。
最近寝不足が続いている。今週は大体3時過ぎにねて7時20分位に起きている。
一日や二日なら何とかなるが、一週間続くと体力的に限界を感じる。
明日はいちごの授業参観なのでそろそろ寝ないと。。。
[Programming in Scala, First Edition]
2014/4/18 150 chapter12 3-8/8
Scalaの特徴であるtraitについて学んだ。
Programming in Scala, First Edition Chapter 12
12. Traits
traitはscalaのコード再利用の基礎単位。
メソッドとフィールド定義をカプセル化する。
クラスとmixingすることで再利用できる。
クラスは複数のtratをmix-inできる。
scalaでは継承は一つのスーパークラスからのみ。
他の言語では多重継承をサポートしているものもある。C++等
二つの代表的な利用方法
widening thin interfaces
stackable modifications
12.1 How traits work
traitの定義
キーワードclassのtraitを使う。他はクラス定義と同じ。
trait TRAITNAME extends SUPERCLASSNAME { ... }
class parameterは使えない。Section 20.5
fields, concrete methodsも定義できる。
javaのインタフェースと異なる。
スーパークラスを指定しない場合はAnyRefを継承する。クラスと同じ。
クラスにmix-inして使用
キーワードextendsを使う
class CLASSNAME(...) extends TRAITNAME { ... }
キーワードwithを使う
class CLASSNAME(...) extends SUPERCLASSNAME with TRAIT1NAME with TRAIT2NAME ... { ... }
withを複数使って複数のtraitをmix-in出来る。
多重継承とは異なるので、継承ではなくてmix-inと言う。
super callがdynamically bound => Section 12.6
クラスではstatically bound
stackable modificationsが出来るようになる。Section 12.5
12.2 Thin versus rich interfaces
traitの主な使用方法の一つはクラスの持っているメソッドに自動的にメソッドを加える事。
richにするかthinにするかはオブジェクト指向設計によくあるtrade-off
richにすると使うときは便利
javaのインタフェースはthinになる傾向がある。
そのインタフェースを実装する人は皆、沢山のメソッドの実装を要求される。
scalaのtraitなら具象メソッドを実装出来るのでそれを再利用できる。
scalaのtairtはrichになる傾向がある。
traitをクラスにmix-inして少ない部分を実装するだけでrichなクラスを作れる。
12.3 Example: Rectangular objects
四角を表す色々なクラスを作る時に、基本部分をtraitにして再利用する。
traitに具象メソッドを実装することで、クラスの実装を減らしてrichAPIに出来る。
12.4 The Ordered trait
trait Ordered[T] {
def compare(that: T): Int
def <(that: T): Boolean = (this compare that) < 0
def >(that: T): Boolean = (this compare that) > 0
def <=(that: T): Boolean = (this compare that) <= 0
def >=(that: T): Boolean = (this compare that) >= 0
}
class Rational(n: Int, d: Int) extends Ordered[Rational] {
// ...
def compare(that: Rational) =
(this.numer * that.denom) - (that.numer * this.denom)
}
RationalにOrdered traitをmix-inする。
少ない抽象メソッドをクラス内で実装して、それをtraitの具象メソッドが利用する。
trait内で実装されている具象メソッドの動作を抽象メソッドでカスタマイズする。
[]は型パラメータ
def compare()はtraitのabstract method
これを実装すると<,>,<=,>=の比較演算子が使えるようになる。
演算子は具象クラスで実装されているのでRationalでは実装不要。
equalsはtrait内では定義出来ない。Chapter 28
渡された引数の型チェックが必要なため。
downcastが必要になってしまう。
12.5 Traits as stackable modifications
trait Doubling extends IntQueue {
abstract override def put(x: Int) { super.put(2 * x) }
}
あるスーパークラスを継承したtraitは同じスーパークラスのサブクラスにしかmix-inできない。
trait内からスーパークラスの実装されていないabstract methodを呼べる。
traitのメッソッド定義にabstractキーワードをつける。
普通のクラスでは出来ない。
traitは動的束縛で実行時に実装があれば良いから可能。
他のtraitやクラスが実装を定義した後でmix-inすればOK。
?? この後と言うのはどういうタイミング? メソッドが実際に呼ばれる前??
あるクラスとtraitをmix-inしただけの、特別な実装がないクラスも作れる
class MyQueue extends BasicIntQueue with Doubling
クラス名を付けずに直接newでインスタンスを作成する事も出来る。
val x = new BasicIntQueue with Doubling
複数のtraitをstack modificationする事も出来る。
val queue = new BasicIntQueue with Incrementing with Filtering
右のtraitから実行される。順番の詳細は後で。
** 複数traitを連結してパイプや合成関数的な多段変換的な事が出来る。
12.6 Why not multiple inheritance?
traitと他の言語で良くある多重継承の違いは線形化(linearization)
線形化することでスーパークラス、traitへのメソッド呼び出し順番が決まる。
各クラス、traitのメソッドが一回ずつ呼ばれるように線形化される。
これがstacking of modificationsを可能にしている。
ダイヤモンド継承問題も発生しない。
多重継承だと呼び出しはサブクラスからスーパークラスの順に行われる。
スーパークラスが二つあった場合にどちらを呼ぶか?
これは実装依存だが、両方を一度に呼ぶことは出来ない。
それぞれを分けて呼び出した場合により上位のクラスが2重に呼び出されるかも。
ダイヤモンド継承になっている場合。
traitでは各クラス、traitのメソッドが一回ずつ呼ばれるように線形化される。
正確な順番は言語仕様書に記載あるが、少し複雑。
クラスがtrait、スーパークラス、より先に一番最初に呼ばれる。
最後に呼ばれるのはスーパークラスと更に上位のtrait, クラス。
その前は左のtrait順に、そのtraitと上位のtrait, クラスが順に呼ばれる。
深さ優先探索と同じような順番
共通の親がいる場合は、マージされて一番遅い順番の所で一回だけ呼ばれる。
定義例
class Animal
trait Furry extends Animal
trait HasLegs extends Animal
trait FourLegged extends HasLegs
class Cat extends Animal with Furry with FourLegged
呼び出し順
最後 Animal -> AnyRef -> Any
3番目 Furry -> (Animal)マージ
2番目 FourLegged -> HasLegs -> (Animal)マージ
1番目 Cat
Cat -> FourLegged -> HasLegs -> Furry -> AnyRef -> Any
12.7 To trait, or not to trait?
traitと抽象クラスどちらを使うべきか?
明確な基準は無いが、ガイドラインを示す。
振る舞いが再利用されない場合は具象クラスにする。
** 再利用されないなら抽象化する必要なし。
クラスと無関係に複数回再利用される場合はtraitにする。
クラス階層と違う部分いmix-in出来るのはtraitだけ。
??なんで??
javaのコードで使いたい場合は抽象クラスにする。
実装を持たないtraitはjavaのinterfaceに変換されるので使える。
scalaの継承はjavaの継承と同じ。
コンパイル後のバイナリで配布する場合は抽象クラスにする。
traitのメンバが増減した時は、継承しているクラスは変更なくても再コンパイル必要。
継承せずに単に機能を呼び出すだけ(使用)にすれば問題なく使える。
性能が重要なら具象クラスにする。
JVMはインタフェースよりクラスの方が実行速度が速い。
traitはインタフェースになるのでクラスにした方が速い。
ただし、性能の原因がそこにあると確信があるとのみにすべき。
それでもわからなかったら、まずはtraitにしてみる。
後から何時でも変えられる。
一般的にはtraitが一番汎用性が高い。
12.8 Conclusion
線形化によるsuper callによって多重継承の問題を解決できる。
stack behaviorが出来る。
traitは継承を用いたコード再利用の基礎単位。
この性質から熟練したscalaプログラマーはtraitから書き始める。
traitは単なるコンセプトの断片に過ぎない
設計が固まるにつれ、traitのmix-inを通して断片の結合が完全コンセプトを作る。
traitはscalaのコード再利用の基礎単位。
メソッドとフィールド定義をカプセル化する。
クラスとmixingすることで再利用できる。
クラスは複数のtratをmix-inできる。
scalaでは継承は一つのスーパークラスからのみ。
他の言語では多重継承をサポートしているものもある。C++等
二つの代表的な利用方法
widening thin interfaces
stackable modifications
12.1 How traits work
traitの定義
キーワードclassのtraitを使う。他はクラス定義と同じ。
trait TRAITNAME extends SUPERCLASSNAME { ... }
class parameterは使えない。Section 20.5
fields, concrete methodsも定義できる。
javaのインタフェースと異なる。
スーパークラスを指定しない場合はAnyRefを継承する。クラスと同じ。
クラスにmix-inして使用
キーワードextendsを使う
class CLASSNAME(...) extends TRAITNAME { ... }
キーワードwithを使う
class CLASSNAME(...) extends SUPERCLASSNAME with TRAIT1NAME with TRAIT2NAME ... { ... }
withを複数使って複数のtraitをmix-in出来る。
多重継承とは異なるので、継承ではなくてmix-inと言う。
super callがdynamically bound => Section 12.6
クラスではstatically bound
stackable modificationsが出来るようになる。Section 12.5
12.2 Thin versus rich interfaces
traitの主な使用方法の一つはクラスの持っているメソッドに自動的にメソッドを加える事。
richにするかthinにするかはオブジェクト指向設計によくあるtrade-off
richにすると使うときは便利
javaのインタフェースはthinになる傾向がある。
そのインタフェースを実装する人は皆、沢山のメソッドの実装を要求される。
scalaのtraitなら具象メソッドを実装出来るのでそれを再利用できる。
scalaのtairtはrichになる傾向がある。
traitをクラスにmix-inして少ない部分を実装するだけでrichなクラスを作れる。
12.3 Example: Rectangular objects
四角を表す色々なクラスを作る時に、基本部分をtraitにして再利用する。
traitに具象メソッドを実装することで、クラスの実装を減らしてrichAPIに出来る。
12.4 The Ordered trait
trait Ordered[T] {
def compare(that: T): Int
def <(that: T): Boolean = (this compare that) < 0
def >(that: T): Boolean = (this compare that) > 0
def <=(that: T): Boolean = (this compare that) <= 0
def >=(that: T): Boolean = (this compare that) >= 0
}
class Rational(n: Int, d: Int) extends Ordered[Rational] {
// ...
def compare(that: Rational) =
(this.numer * that.denom) - (that.numer * this.denom)
}
RationalにOrdered traitをmix-inする。
少ない抽象メソッドをクラス内で実装して、それをtraitの具象メソッドが利用する。
trait内で実装されている具象メソッドの動作を抽象メソッドでカスタマイズする。
[]は型パラメータ
def compare()はtraitのabstract method
これを実装すると<,>,<=,>=の比較演算子が使えるようになる。
演算子は具象クラスで実装されているのでRationalでは実装不要。
equalsはtrait内では定義出来ない。Chapter 28
渡された引数の型チェックが必要なため。
downcastが必要になってしまう。
12.5 Traits as stackable modifications
trait Doubling extends IntQueue {
abstract override def put(x: Int) { super.put(2 * x) }
}
あるスーパークラスを継承したtraitは同じスーパークラスのサブクラスにしかmix-inできない。
trait内からスーパークラスの実装されていないabstract methodを呼べる。
traitのメッソッド定義にabstractキーワードをつける。
普通のクラスでは出来ない。
traitは動的束縛で実行時に実装があれば良いから可能。
他のtraitやクラスが実装を定義した後でmix-inすればOK。
?? この後と言うのはどういうタイミング? メソッドが実際に呼ばれる前??
あるクラスとtraitをmix-inしただけの、特別な実装がないクラスも作れる
class MyQueue extends BasicIntQueue with Doubling
クラス名を付けずに直接newでインスタンスを作成する事も出来る。
val x = new BasicIntQueue with Doubling
複数のtraitをstack modificationする事も出来る。
val queue = new BasicIntQueue with Incrementing with Filtering
右のtraitから実行される。順番の詳細は後で。
** 複数traitを連結してパイプや合成関数的な多段変換的な事が出来る。
12.6 Why not multiple inheritance?
traitと他の言語で良くある多重継承の違いは線形化(linearization)
線形化することでスーパークラス、traitへのメソッド呼び出し順番が決まる。
各クラス、traitのメソッドが一回ずつ呼ばれるように線形化される。
これがstacking of modificationsを可能にしている。
ダイヤモンド継承問題も発生しない。
多重継承だと呼び出しはサブクラスからスーパークラスの順に行われる。
スーパークラスが二つあった場合にどちらを呼ぶか?
これは実装依存だが、両方を一度に呼ぶことは出来ない。
それぞれを分けて呼び出した場合により上位のクラスが2重に呼び出されるかも。
ダイヤモンド継承になっている場合。
traitでは各クラス、traitのメソッドが一回ずつ呼ばれるように線形化される。
正確な順番は言語仕様書に記載あるが、少し複雑。
クラスがtrait、スーパークラス、より先に一番最初に呼ばれる。
最後に呼ばれるのはスーパークラスと更に上位のtrait, クラス。
その前は左のtrait順に、そのtraitと上位のtrait, クラスが順に呼ばれる。
深さ優先探索と同じような順番
共通の親がいる場合は、マージされて一番遅い順番の所で一回だけ呼ばれる。
定義例
class Animal
trait Furry extends Animal
trait HasLegs extends Animal
trait FourLegged extends HasLegs
class Cat extends Animal with Furry with FourLegged
呼び出し順
最後 Animal -> AnyRef -> Any
3番目 Furry -> (Animal)マージ
2番目 FourLegged -> HasLegs -> (Animal)マージ
1番目 Cat
Cat -> FourLegged -> HasLegs -> Furry -> AnyRef -> Any
12.7 To trait, or not to trait?
traitと抽象クラスどちらを使うべきか?
明確な基準は無いが、ガイドラインを示す。
振る舞いが再利用されない場合は具象クラスにする。
** 再利用されないなら抽象化する必要なし。
クラスと無関係に複数回再利用される場合はtraitにする。
クラス階層と違う部分いmix-in出来るのはtraitだけ。
??なんで??
javaのコードで使いたい場合は抽象クラスにする。
実装を持たないtraitはjavaのinterfaceに変換されるので使える。
scalaの継承はjavaの継承と同じ。
コンパイル後のバイナリで配布する場合は抽象クラスにする。
traitのメンバが増減した時は、継承しているクラスは変更なくても再コンパイル必要。
継承せずに単に機能を呼び出すだけ(使用)にすれば問題なく使える。
性能が重要なら具象クラスにする。
JVMはインタフェースよりクラスの方が実行速度が速い。
traitはインタフェースになるのでクラスにした方が速い。
ただし、性能の原因がそこにあると確信があるとのみにすべき。
それでもわからなかったら、まずはtraitにしてみる。
後から何時でも変えられる。
一般的にはtraitが一番汎用性が高い。
12.8 Conclusion
線形化によるsuper callによって多重継承の問題を解決できる。
stack behaviorが出来る。
traitは継承を用いたコード再利用の基礎単位。
この性質から熟練したscalaプログラマーはtraitから書き始める。
traitは単なるコンセプトの断片に過ぎない
設計が固まるにつれ、traitのmix-inを通して断片の結合が完全コンセプトを作る。
2014年4月18日金曜日
2014/4/17(木)
いちごと二人で仲良く寝られた。
あんずが朝自主的に着替えを出してくれた。
英語で読む村上春樹の1回終了で2回目開始できた。
一週間経つのが早い。
[Programming in Scala, First Edition]
2014/4/16 60 chapter12 1-2/8
traitについて勉強中。
あんずが朝自主的に着替えを出してくれた。
英語で読む村上春樹の1回終了で2回目開始できた。
一週間経つのが早い。
[Programming in Scala, First Edition]
2014/4/16 60 chapter12 1-2/8
traitについて勉強中。
2014年4月17日木曜日
Programming in Scala, First Edition Chapter 11まで読破!!
今日はchapter10 12-16/16 chapter11 1-4/4までを220分で進めた。
継承は拡張のイメージが強かったが、実装の隠蔽する使い方のイメージがつかめた。
復習として、ElementとSprialの実装をやってみたくなった。
-> Courseraでやると良いんだけど。
scala-ideを使ってみるがイマイチ使い難い。。。
これだとemacsのshell-modeでの実行の方が楽そう。
継承は拡張のイメージが強かったが、実装の隠蔽する使い方のイメージがつかめた。
復習として、ElementとSprialの実装をやってみたくなった。
-> Courseraでやると良いんだけど。
scala-ideを使ってみるがイマイチ使い難い。。。
これだとemacsのshell-modeでの実行の方が楽そう。
自動翻訳と英語学習について少し考えてみた。
Lang-8等で作文の練習をしていると、何の助けも借りずに自力で書くのと、辞書やネットや自動翻訳などの助けを借りて書くのとどちらが良いか悩ましい。
独力で書く練習をした方が、自分の知識から文を作り出す運用力が鍛えられると思う。
ツールの助けを借りれば、ツールでわかる間違いはそこでつぶして、Lang-8のネイティブの添削はもっと高度内容に集中してもらう事が出来る。
両方の良い取ろうとすると下記の手順かな?
1. まずは自力でツールを使わずに一通り文章をひねり出す。途中で調べたりしないのがポイント。
2. その後で表現に自身のないところをツールや辞書やネットで調べて修正。
3. それをLang-8に投稿して、ネイティブからコメントをもらう。
文法チェックのサイトについては下記があった
http://www.gingersoftware.com/grammarcheck
http://www.grammarly.com/
そんなに強力な感じはしなかった。
googleで使用例調べたり、weblioで例文を調べた方が良いかも知れない。
入力された英文からネットを検索して、関連する用例を集めてくるソフトが作れたら面白いかも。
独力で書く練習をした方が、自分の知識から文を作り出す運用力が鍛えられると思う。
ツールの助けを借りれば、ツールでわかる間違いはそこでつぶして、Lang-8のネイティブの添削はもっと高度内容に集中してもらう事が出来る。
両方の良い取ろうとすると下記の手順かな?
1. まずは自力でツールを使わずに一通り文章をひねり出す。途中で調べたりしないのがポイント。
2. その後で表現に自身のないところをツールや辞書やネットで調べて修正。
3. それをLang-8に投稿して、ネイティブからコメントをもらう。
文法チェックのサイトについては下記があった
http://www.gingersoftware.com/grammarcheck
http://www.grammarly.com/
そんなに強力な感じはしなかった。
googleで使用例調べたり、weblioで例文を調べた方が良いかも知れない。
入力された英文からネットを検索して、関連する用例を集めてくるソフトが作れたら面白いかも。
自分の中にある要素と他者の理解について少し考察出来た。
察しが良いと、相手が察しが悪い場合に、それを予測出来なくて困る。
更に上のレベルでは相手の察しの悪さを予測、理解して進められる。
悪い先入観で見ないようにするのは大事だが。
自分の中にもある察しの悪い要素から類推して考えれば他人事にならない。
自分に中にある色々な要素から類推する能力が高ければ、他人を受け入れやすい?
この自分の中にある要素から他者を理解しようという発想は、シャドー、自分のいやな部分を思い出させる存在の克服とも関係していそう。
その存在に嫌悪感を抱くのは、結局は自分の中の許せない部分が刺激されるから。
逆に受け入れることで、自分自身を受け入れることにもつながる
更に上のレベルでは相手の察しの悪さを予測、理解して進められる。
悪い先入観で見ないようにするのは大事だが。
自分の中にもある察しの悪い要素から類推して考えれば他人事にならない。
自分に中にある色々な要素から類推する能力が高ければ、他人を受け入れやすい?
この自分の中にある要素から他者を理解しようという発想は、シャドー、自分のいやな部分を思い出させる存在の克服とも関係していそう。
その存在に嫌悪感を抱くのは、結局は自分の中の許せない部分が刺激されるから。
逆に受け入れることで、自分自身を受け入れることにもつながる
電王戦から機械と人間の関係を少し考察出来た。
Lang-8に電王戦のネタを投稿した。上手く英語で書けずに苦戦した。
今朝の新聞にも電王戦の話が出ていたし、たまたま今日読んだ日経コンピュータで将棋が強くなったの機械学習の成果とあった。
ビックデータの時代になって機械によるデータ解析がより進むのは間違いない。
上手く活用出来れば時間を効率的に使うためのツールになる気もする。
今朝の新聞にも電王戦の話が出ていたし、たまたま今日読んだ日経コンピュータで将棋が強くなったの機械学習の成果とあった。
ビックデータの時代になって機械によるデータ解析がより進むのは間違いない。
上手く活用出来れば時間を効率的に使うためのツールになる気もする。
NHKラジオ 英語で読む村上春樹 4月から9月の「踊る小人」を開始できた。
NHKラジオ 英語で読む村上春樹 4月から9月の「踊る小人」を開始できた。
司会者、解説者が変わって番組の構成も若干変わったが、英語の朗読と日本語の朗読と解説があるという構成は同じ。
解説者が試訳として直訳を披露すると言うのは面白い試みだと思った。
「踊る小人」の話自身も面白そうで期待できる。
司会者、解説者が変わって番組の構成も若干変わったが、英語の朗読と日本語の朗読と解説があるという構成は同じ。
解説者が試訳として直訳を披露すると言うのは面白い試みだと思った。
「踊る小人」の話自身も面白そうで期待できる。
Programming in Scala, First Edition: Chapter 11
11. Scala's Hierarchy
scalaのクラス階層の全体像を見る。
Any: top
全てのクラスのスーパークラス。どんなクラスも受け入る。universal
Null, Nothing: bottom
全てのクラスのサブクラス。どんなクラスにも受け入れられる。
11.1 Scala's class hierarchy
Any
全てのクラスのスーパークラス。
==, !=, equals, haCode, toString のmethodがある。
==, != はfinalでoverride不可能。
== のoverrideされている様に見えても、実際はequalsをoverrideしている。
AnyVal extends Any
built-in value classのスーパークラス
Byte, Short, Char, Int, Long, Float, Double, Boolean, and Unit
Unit以外はjavaのprimitive, value class
このクラスのインスタンスはリテラルのみ。new出来ない。
abstractとfinalを組み合わせることで実現。
Unitはjavaのvoidとほぼ同じ。インスタンスは()のみ。Section 7.2参照。
value classはAnyValの直系。互いに上下関係は無い。
代わりにimplicit conversionsが定義されている。
Longの型にIntの値を入れることが出来るようにしている。
更に多くのメソッドが使えるが、これは例えばscala.runtime.RichInt定義されている。
"booster classes"
implicit conversionsでIntをRichIntのメソッドにも使えるようにする。
AnyRef extends Any
全ての参照クラスのスーパークラス
java.lang.Objectとして実装されている。どちらでも同じ。どちらも使える。
scalaではAnyRefと書くことを当然推奨。
scalaのクラスはScalaObjectと言うtraitも継承している。
ここはjavaのクラスと異なる。
例外を効率よく扱うために定義されている。
唯一のメソッドは$tag。内部向け。パターンマッチングの高速化用。
11.2 How primitives are implemented
整数はjavaと同じように32bitで格納している。
JVM上で実行する上で効率的
javaのライブラリとの互換性
+や*もjavaの組み込みを使っている。
"backup" class として java.lang.Integer を使っている。
javaのオブジェクトの様に扱わなければいけない時はいつでも。
toStringを使うときやAnyの型に入れる時等。
透過的に"boxed integers"である java.lang.Integer に変換される。
これはjava 5のauto-boxingとほぼ同様。
scalaの方がより隠蔽されていはいる。
scalaではAnyRefの==はequalsを呼んでいて、サブクラスでoverrideされる。
通常は参照の比較ではなくて内容の比較が実装されている。
したがって違う参照でも値が同じならtrueになる。
参照の比較にeq, neを使う。
同一性についてはChapter 28で更に詳しく扱う。
11.3 Bottom types
scala.Null, scala.Nothingはクラス階層の一番下に位置する。
全てのクラスのサブクラスとして振舞う。
オブジェクト指向的な型階層の"corner cases"に統一的な方法で対処するため。
Nullはすべてのリファレンスクラスのサブクラス
Valueクラスのサブクラスとしてはふるまえない。
Nothingはすべてのクラスのサブクラス
通常は値を返さないと言う場合に使う。
(x: Int) => if (x == 0) 0 else throw new RuntimeException("hoge")
Int => Int
戻り値の型がIntなのはthrowの戻り値の型がNothingのため。
if (...) Int else Nothing extends Int
が成り立つので、NothingがIntのup castされて全体がIntになる。
11.4 Conclusion
topとbottomのクラスと全体の階層を見た。
次は trait による mixin composition
scalaのクラス階層の全体像を見る。
Any: top
全てのクラスのスーパークラス。どんなクラスも受け入る。universal
Null, Nothing: bottom
全てのクラスのサブクラス。どんなクラスにも受け入れられる。
11.1 Scala's class hierarchy
Any
全てのクラスのスーパークラス。
==, !=, equals, haCode, toString のmethodがある。
==, != はfinalでoverride不可能。
== のoverrideされている様に見えても、実際はequalsをoverrideしている。
AnyVal extends Any
built-in value classのスーパークラス
Byte, Short, Char, Int, Long, Float, Double, Boolean, and Unit
Unit以外はjavaのprimitive, value class
このクラスのインスタンスはリテラルのみ。new出来ない。
abstractとfinalを組み合わせることで実現。
Unitはjavaのvoidとほぼ同じ。インスタンスは()のみ。Section 7.2参照。
value classはAnyValの直系。互いに上下関係は無い。
代わりにimplicit conversionsが定義されている。
Longの型にIntの値を入れることが出来るようにしている。
更に多くのメソッドが使えるが、これは例えばscala.runtime.RichInt定義されている。
"booster classes"
implicit conversionsでIntをRichIntのメソッドにも使えるようにする。
AnyRef extends Any
全ての参照クラスのスーパークラス
java.lang.Objectとして実装されている。どちらでも同じ。どちらも使える。
scalaではAnyRefと書くことを当然推奨。
scalaのクラスはScalaObjectと言うtraitも継承している。
ここはjavaのクラスと異なる。
例外を効率よく扱うために定義されている。
唯一のメソッドは$tag。内部向け。パターンマッチングの高速化用。
11.2 How primitives are implemented
整数はjavaと同じように32bitで格納している。
JVM上で実行する上で効率的
javaのライブラリとの互換性
+や*もjavaの組み込みを使っている。
"backup" class として java.lang.Integer を使っている。
javaのオブジェクトの様に扱わなければいけない時はいつでも。
toStringを使うときやAnyの型に入れる時等。
透過的に"boxed integers"である java.lang.Integer に変換される。
これはjava 5のauto-boxingとほぼ同様。
scalaの方がより隠蔽されていはいる。
scalaではAnyRefの==はequalsを呼んでいて、サブクラスでoverrideされる。
通常は参照の比較ではなくて内容の比較が実装されている。
したがって違う参照でも値が同じならtrueになる。
参照の比較にeq, neを使う。
同一性についてはChapter 28で更に詳しく扱う。
11.3 Bottom types
scala.Null, scala.Nothingはクラス階層の一番下に位置する。
全てのクラスのサブクラスとして振舞う。
オブジェクト指向的な型階層の"corner cases"に統一的な方法で対処するため。
Nullはすべてのリファレンスクラスのサブクラス
Valueクラスのサブクラスとしてはふるまえない。
Nothingはすべてのクラスのサブクラス
通常は値を返さないと言う場合に使う。
(x: Int) => if (x == 0) 0 else throw new RuntimeException("hoge")
Int => Int
戻り値の型がIntなのはthrowの戻り値の型がNothingのため。
if (...) Int else Nothing extends Int
が成り立つので、NothingがIntのup castされて全体がIntになる。
11.4 Conclusion
topとbottomのクラスと全体の階層を見た。
次は trait による mixin composition
Programming in Scala, First Edition: Chapter 10
10. Composition and Inheritance
Composition(集約) 他のクラスへの参照を保持して、その機能を利用する。
Inheritance(継承) super class, sub classの関係
abstract classes, parameterless methods, extending classes,
overriding methods and fields, parametric fields,
invoking superclass constructors, polymorphism and dynamic binding,
final members and classes, factory objects and methods
10.1 A two-dimensional layout library
この章で例題として作成するlibraryの説明。
長方形の文字列を格納するlayout elementを作成する。
elem(s: String): Element // factory methods
val column1 = elem("hello") above elem("***") // combinator
val column2 = elem("***") above elem("world")
column1 beside column2 // operatoer
->
hello *** // result
*** world
layout elementは単純パーツから構成演算子でオブジェクトを生成する好例。
above, besideはcomposing operator, combinato
combinatorの視点から設計を検討するのは良い方法。
application domainのobjectの性質を反映しているから。
基本的なオブジェクトとは?
基本的なオブジェクトからより複雑なものを構成する方法は?
意味のある規則に従っている?
10.2 Abstract classes
最初に行うのはtype Elementの定義から。
保持している内容はArray[String]とする。
各Stringが行で、列を配列で表現する。
内容を返すcontentsメソッドを定義する。
abstract class Element {
def contents: Array[String]
}
実装のないメソッドを含むクラスは抽象クラス。abstract修飾子を付ける。
インスタンス化出来ない。
サブクラスでメソッドを実装したらインスタンス化出来る。
実装のないメソッドには特に修飾子は付けない。
javaとは違う。
実装があるのは具象(concrete)クラス。
宣言(declarations)と定義(definitions)
抽象クラスElementは抽象メソッドcontentsを宣言しているが具象メソッドは定義していない。
10.3 Defining parameterless methods
Elementに幅と高さを返すメソッドを加える。
幅: 最初の行の文字列長 or 一行も無いときは0
高さ: 配列の要素数
abstract class Element {
def contents: Array[String]
def height: Int = contents.length
def width: Int = if (height == 0) 0 else contents(0).length
}
メソッドに引数を示す()が無い。parameterless methods
scalaでは一般的
空の()があるものはempty-paren methods.
def height(): Int
どちらを使うかはscalaの慣習に従うと良い。
parameterless methods
副作用なしで、そのオブジェクト内のmutable stateのみを参照する場合。
empty-paren methods
上記以外の場合。
uniform access principle
クライアントコードが、属性へのアクセスがメソッドで実装されているか、フィールドで実装されているかの影響を受けないようにすべき。
メソッドだと実行時の処理時間が多少長くなる。
フィールドだとメモリ消費が多少多くなる。
どちらにせよクライアントコードから違いが見えないようにする。
副作用が無く他のオブジェクトの可変状態を使わないようにする。
javaはこの原則に従っていない。メソッドは()が必要。
parameterless methodとempty-paren methodを互いにoverrideさせている。
引数無しのメソッドは全て()無しでも呼び出せる。
()を付けた方がいい場合もある。
レシーバに含まれていない属性にアクセスする場合。
I/O, 可変値の書き込み, レシーバのフィールド以外の可変値の読み出し。
直接、間接的にmutable objectsを使う。
何か値を返す以上の事が行われていることを示す手がかりになる。
10.4 Extending classes
サブクラスの作成。Javaと同様にextends clauseを使う。
class ArrayElement(conts: Array[String]) extends Element {
def contents: Array[String] = conts
}
クラス図
scala.AnyRef<<java.lang.Object>>
^
Element<<abstract>>
^
ArrayElement <>-- Array[String]
extends clauseを使わない場合は暗黙的にscala.AnyRefを継承する。
scala.AnyRefの実体はjava.lang.Object
二つの例外を除いて全てのメンバを継承する。
privateメンバは継承しない。
上書き(override)しているメンバは継承しない。
スーパークラスと同じ名前、パラメータのメンバ。
抽象メソッドを上書きする際は、実装する(implements)とも言う。
この場合はoverride修飾子がいらない。
具象メソッドの場合はoverride修飾子必要。
サブクラスはサブタイプにもなる。
スーパークラスが使える所ならサブクラスも使える。互換性があるため。
ArrayElementはArray[String]を集約(compositon)している。
集約と継承の設計上の考察は Section 10.11 で行う。
10.5 Overriding methods and fields
scalaではフィールドとメソッドで上書きが出来る。
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}
uniform access principleに従っている。
名前空間(namespace)が同じだから。
javaでは出来ない。
scalaの名前空間
values (fields, methods, packages, and singleton objects)
types (class and trait names)
javaの名前空間
fields, methods, types, and packages
scalaではスーパークラスのメソッドをサブクラスではフィールドで定義したりする。
10.6 Defining parametric fields
クラスのパラメータをそのままフィールドとして使える。parametric fields
class ArrayElement(
val contents: Array[String]
) extends Element
valやvarをパラメータに付けるだけ。
privateやoverrideやprotected等の修飾子も使える。
名前の衝突を減らせる。
単に衝突避けるためだけに付けられた名前があった時、それは冗長の"code smell"
** "code smell"を嗅ぎ取って簡素化出来たら良い。
10.7 Invoking superclass constructors
class LineElement(s: String) extends ArrayElement(Array(s)) {
override def width = s.length
override def height = 1
}
具象クラスのサブクラスを作成する。
一行のみのElementを作成する場合を考える。
?? 補助コンストラクタ(Auxiliary constructor)でも同様の事が出来る??
スーパークラスの主コンストラクタ(primary constructor)にパラメータを渡す。
... extends ArrayElement(Array(s)) ...
10.8 Using override modifiers
override修飾子は上書きするときは必要。
実装(implement)の時はあってもなくても良い。
上書きも実装もしない時は付けられない。
typoで名前を間違った時にコンパイラがエラーを出せる。
スーパークラスに新しいメンバを追加した時に役に立つ。
既存のサブクラスと名前が衝突する可能性がある。fragile base class problem
既存のサブクラスに同名のメンバが定義済みの場合エラーに出来る。
overrideを過去には付ける事が出来なかったから。
完全な解決では無いが、不可解な動作をするならエラーになった方がマシ。
10.9 Polymorphism and dynamic binding
Polymorphism
スーパークラスの型の変数にサブクラスの型の値を入れられる。
下位のクラスは上位のクラスと互換性があるから。
スーパークラスの型の変数の振る舞い代入するサブクラスで色々変えられる。
dynamic binding
型ではなくて、実行時の値によって実際に実行されるメソッドが決定される。
10.10 Declaring final members
final修飾子を使うと、メンバのやクラスの上書き、サブクラス化を禁止出来る。
メンバ定義にfinalを付けるとサブクラスでのoverrideを禁止できる。
final def method ..., final val field ...
クラス定義にfinalを付けるとサブクラスの作成を禁止できる。
final class ClassName(...) {...
10.11 Using composition and inheritance
集約と継承では、再利用を考えると一般的には集約の方が好ましい。
継承だとfragile base class problemでうっかりサブクラスの実装を壊してしまう
継承の方が良い場合もある。
is-aの関係がある時。
サブクラスの型をスーパークラスの型として使いたい場合。
LineElementのについて考えると、Elementのサブクラスにするのが相応しい。
class LineElement(s: String) extends Element {
val contents = Array(s)
override def width = s.length
override def height = 1
}
Section 10.7では具象クラスのサブクラスの例が必要だったので無理やり定義。
10.12 Implementing above, beside, and toString
** 元々仕様としてElement内の各行は同じ幅(文字列数)。
10.2 の説明時点では良く分からなかった。
現時点では幅が同じかどうかのチェックする処理は入れていない。
?? 後で追加される??
aboveメソッドの実装
def above(that: Element): Element =
new ArrayElement(this.contents ++ that.contents)
現状では簡単のために同じ幅前提。後で拡張する。
scalaの配列はjavaに基づいているが多くの拡張もしている。
scala.Seqを継承していてシーケンスを扱うメソッドが多数ある。
besideメソッドの実装
def bside(that: Element): Element =
new ArrayElement(
for (
(line1, line2) <- this.contents zip that.contents
) yield line1 + line2
)
ループを使ってもかけるが命令型的なので別の実装にする。
indexを使った配列のループが出てきたら命令型の"telltale sign"
zipメソッドは二つの配列を各要素をTuple2sに結合した一つの配列を返す。
要素数が異なる場合は、少ない方の要素数になる。
(line1, line2) <- はパターマッチング。Chapter 15参照。
?? besideの実装はサブクラスに依存している。循環??
toStringメソッドの上書き
override def toString = contents mkString "\n"
toStringは()を使わないparameterless method
uniform access principleに従うとpure methodには()付けない方が良い。
10.13 Defining a factory object
factory object
他のクラスを生成するメソッドを持ったオブジェクト。
clietからクラス階層等の実装を隠蔽するため。
後で修正したときにclient codeへの影響が出難いようにするため。
詳細をなるべく見せない方がそれに依存したコードを書かれなくて良い。
factory objectの実装方法
object Element {
def elem(contents: Array[String]): Element = new ArrayElement(contents)
def elem(chr: Char, width: Int, height: Int): Element =
new UniformElement(chr, width, height)
def elem(line: String): Element = new LineElement(line)
}
抽象クラスElementのcompanion objectとして実装する。
overloadして統一的に呼べるようにしている。
** 戻り値の型をElementにしているのがポイント
?? applyメソッド使わないのは何故??
Elementだけを公開すれば良くなる。
ArrayElement, LineElement, UniformElementの実装は隠蔽。
Element内にprivate classとして定義する。
import Element.elem とすればクラス内からクラスのメソッド同様に呼べる。
10.14 Heighten and widen
大きさの違うElementを結合出来るように拡張する。
小さいほうの両端に空白、空行を入れて大きい方に合わせる。
空白を入れるためのhelper methodsを定義する。
def widen(w: Int): Element =
if (w <= width) this
else {
val left = elem(' ', (w - width) / 2, height)
var right = elem(' ', w - width - left.width, height)
left beside this beside right
}
** left beside this beside right が見たままで分かりやすくて凄い!
?? どうしてprivateで定義しないのか??
?? beside内でwidenを使うので呼び出しが循環参照になっている??
改良後のbeside
def beside(that: Element): Element = {
val this1 = this heighten that.height
val that1 = that heighten this.height
elem(
for ((line1, line2) <- this1.contents zip that1.contents)
yield line1 + line2)
}
** heighten, aboveも同様に改良。
10.15 Putting it all together
与えられた数の角を持つ渦巻きを書くアプリケーションを例題として作成する。
殆ど全ての機能を使う練習として面白い。
** 素晴らしい!!
import Element.elem
object Spiral {
val space = elem(" ")
val corner = elem("+")
def spiral(nEdges: Int, direction: Int): Element = {
if (nEdges == 1)
elem("+")
else {
val sp = spiral(nEdges - 1, (direction + 3) % 4)
def verticalBar = elem('|', 1, sp.height)
def horizontalBar = elem('-', sp.width, 1)
if (direction == 0)
(corner beside horizontalBar) above (sp beside space)
else if (direction == 1)
(sp above space) beside (corner above verticalBar)
else if (direction == 2)
(space beside sp) above (horizontalBar beside corner)
else
(verticalBar above corner) beside (space above sp)
}
}
def main(args: Array[String]) {
val nSides = args(0).toInt
println(spiral(nSides, 0))
}
}
10.16 Conclusion
abstract classes, inheritance and subtyping, class hierarchies,
parametric fields, method overriding, class hierarchy
layout libraryはChapter 14で再び使う。
Composition(集約) 他のクラスへの参照を保持して、その機能を利用する。
Inheritance(継承) super class, sub classの関係
abstract classes, parameterless methods, extending classes,
overriding methods and fields, parametric fields,
invoking superclass constructors, polymorphism and dynamic binding,
final members and classes, factory objects and methods
10.1 A two-dimensional layout library
この章で例題として作成するlibraryの説明。
長方形の文字列を格納するlayout elementを作成する。
elem(s: String): Element // factory methods
val column1 = elem("hello") above elem("***") // combinator
val column2 = elem("***") above elem("world")
column1 beside column2 // operatoer
->
hello *** // result
*** world
layout elementは単純パーツから構成演算子でオブジェクトを生成する好例。
above, besideはcomposing operator, combinato
combinatorの視点から設計を検討するのは良い方法。
application domainのobjectの性質を反映しているから。
基本的なオブジェクトとは?
基本的なオブジェクトからより複雑なものを構成する方法は?
意味のある規則に従っている?
10.2 Abstract classes
最初に行うのはtype Elementの定義から。
保持している内容はArray[String]とする。
各Stringが行で、列を配列で表現する。
内容を返すcontentsメソッドを定義する。
abstract class Element {
def contents: Array[String]
}
実装のないメソッドを含むクラスは抽象クラス。abstract修飾子を付ける。
インスタンス化出来ない。
サブクラスでメソッドを実装したらインスタンス化出来る。
実装のないメソッドには特に修飾子は付けない。
javaとは違う。
実装があるのは具象(concrete)クラス。
宣言(declarations)と定義(definitions)
抽象クラスElementは抽象メソッドcontentsを宣言しているが具象メソッドは定義していない。
10.3 Defining parameterless methods
Elementに幅と高さを返すメソッドを加える。
幅: 最初の行の文字列長 or 一行も無いときは0
高さ: 配列の要素数
abstract class Element {
def contents: Array[String]
def height: Int = contents.length
def width: Int = if (height == 0) 0 else contents(0).length
}
メソッドに引数を示す()が無い。parameterless methods
scalaでは一般的
空の()があるものはempty-paren methods.
def height(): Int
どちらを使うかはscalaの慣習に従うと良い。
parameterless methods
副作用なしで、そのオブジェクト内のmutable stateのみを参照する場合。
empty-paren methods
上記以外の場合。
uniform access principle
クライアントコードが、属性へのアクセスがメソッドで実装されているか、フィールドで実装されているかの影響を受けないようにすべき。
メソッドだと実行時の処理時間が多少長くなる。
フィールドだとメモリ消費が多少多くなる。
どちらにせよクライアントコードから違いが見えないようにする。
副作用が無く他のオブジェクトの可変状態を使わないようにする。
javaはこの原則に従っていない。メソッドは()が必要。
parameterless methodとempty-paren methodを互いにoverrideさせている。
引数無しのメソッドは全て()無しでも呼び出せる。
()を付けた方がいい場合もある。
レシーバに含まれていない属性にアクセスする場合。
I/O, 可変値の書き込み, レシーバのフィールド以外の可変値の読み出し。
直接、間接的にmutable objectsを使う。
何か値を返す以上の事が行われていることを示す手がかりになる。
10.4 Extending classes
サブクラスの作成。Javaと同様にextends clauseを使う。
class ArrayElement(conts: Array[String]) extends Element {
def contents: Array[String] = conts
}
クラス図
scala.AnyRef<<java.lang.Object>>
^
Element<<abstract>>
^
ArrayElement <>-- Array[String]
extends clauseを使わない場合は暗黙的にscala.AnyRefを継承する。
scala.AnyRefの実体はjava.lang.Object
二つの例外を除いて全てのメンバを継承する。
privateメンバは継承しない。
上書き(override)しているメンバは継承しない。
スーパークラスと同じ名前、パラメータのメンバ。
抽象メソッドを上書きする際は、実装する(implements)とも言う。
この場合はoverride修飾子がいらない。
具象メソッドの場合はoverride修飾子必要。
サブクラスはサブタイプにもなる。
スーパークラスが使える所ならサブクラスも使える。互換性があるため。
ArrayElementはArray[String]を集約(compositon)している。
集約と継承の設計上の考察は Section 10.11 で行う。
10.5 Overriding methods and fields
scalaではフィールドとメソッドで上書きが出来る。
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}
uniform access principleに従っている。
名前空間(namespace)が同じだから。
javaでは出来ない。
scalaの名前空間
values (fields, methods, packages, and singleton objects)
types (class and trait names)
javaの名前空間
fields, methods, types, and packages
scalaではスーパークラスのメソッドをサブクラスではフィールドで定義したりする。
10.6 Defining parametric fields
クラスのパラメータをそのままフィールドとして使える。parametric fields
class ArrayElement(
val contents: Array[String]
) extends Element
valやvarをパラメータに付けるだけ。
privateやoverrideやprotected等の修飾子も使える。
名前の衝突を減らせる。
単に衝突避けるためだけに付けられた名前があった時、それは冗長の"code smell"
** "code smell"を嗅ぎ取って簡素化出来たら良い。
10.7 Invoking superclass constructors
class LineElement(s: String) extends ArrayElement(Array(s)) {
override def width = s.length
override def height = 1
}
具象クラスのサブクラスを作成する。
一行のみのElementを作成する場合を考える。
?? 補助コンストラクタ(Auxiliary constructor)でも同様の事が出来る??
スーパークラスの主コンストラクタ(primary constructor)にパラメータを渡す。
... extends ArrayElement(Array(s)) ...
10.8 Using override modifiers
override修飾子は上書きするときは必要。
実装(implement)の時はあってもなくても良い。
上書きも実装もしない時は付けられない。
typoで名前を間違った時にコンパイラがエラーを出せる。
スーパークラスに新しいメンバを追加した時に役に立つ。
既存のサブクラスと名前が衝突する可能性がある。fragile base class problem
既存のサブクラスに同名のメンバが定義済みの場合エラーに出来る。
overrideを過去には付ける事が出来なかったから。
完全な解決では無いが、不可解な動作をするならエラーになった方がマシ。
10.9 Polymorphism and dynamic binding
Polymorphism
スーパークラスの型の変数にサブクラスの型の値を入れられる。
下位のクラスは上位のクラスと互換性があるから。
スーパークラスの型の変数の振る舞い代入するサブクラスで色々変えられる。
dynamic binding
型ではなくて、実行時の値によって実際に実行されるメソッドが決定される。
10.10 Declaring final members
final修飾子を使うと、メンバのやクラスの上書き、サブクラス化を禁止出来る。
メンバ定義にfinalを付けるとサブクラスでのoverrideを禁止できる。
final def method ..., final val field ...
クラス定義にfinalを付けるとサブクラスの作成を禁止できる。
final class ClassName(...) {...
10.11 Using composition and inheritance
集約と継承では、再利用を考えると一般的には集約の方が好ましい。
継承だとfragile base class problemでうっかりサブクラスの実装を壊してしまう
継承の方が良い場合もある。
is-aの関係がある時。
サブクラスの型をスーパークラスの型として使いたい場合。
LineElementのについて考えると、Elementのサブクラスにするのが相応しい。
class LineElement(s: String) extends Element {
val contents = Array(s)
override def width = s.length
override def height = 1
}
Section 10.7では具象クラスのサブクラスの例が必要だったので無理やり定義。
10.12 Implementing above, beside, and toString
** 元々仕様としてElement内の各行は同じ幅(文字列数)。
10.2 の説明時点では良く分からなかった。
現時点では幅が同じかどうかのチェックする処理は入れていない。
?? 後で追加される??
aboveメソッドの実装
def above(that: Element): Element =
new ArrayElement(this.contents ++ that.contents)
現状では簡単のために同じ幅前提。後で拡張する。
scalaの配列はjavaに基づいているが多くの拡張もしている。
scala.Seqを継承していてシーケンスを扱うメソッドが多数ある。
besideメソッドの実装
def bside(that: Element): Element =
new ArrayElement(
for (
(line1, line2) <- this.contents zip that.contents
) yield line1 + line2
)
ループを使ってもかけるが命令型的なので別の実装にする。
indexを使った配列のループが出てきたら命令型の"telltale sign"
zipメソッドは二つの配列を各要素をTuple2sに結合した一つの配列を返す。
要素数が異なる場合は、少ない方の要素数になる。
(line1, line2) <- はパターマッチング。Chapter 15参照。
?? besideの実装はサブクラスに依存している。循環??
toStringメソッドの上書き
override def toString = contents mkString "\n"
toStringは()を使わないparameterless method
uniform access principleに従うとpure methodには()付けない方が良い。
10.13 Defining a factory object
factory object
他のクラスを生成するメソッドを持ったオブジェクト。
clietからクラス階層等の実装を隠蔽するため。
後で修正したときにclient codeへの影響が出難いようにするため。
詳細をなるべく見せない方がそれに依存したコードを書かれなくて良い。
factory objectの実装方法
object Element {
def elem(contents: Array[String]): Element = new ArrayElement(contents)
def elem(chr: Char, width: Int, height: Int): Element =
new UniformElement(chr, width, height)
def elem(line: String): Element = new LineElement(line)
}
抽象クラスElementのcompanion objectとして実装する。
overloadして統一的に呼べるようにしている。
** 戻り値の型をElementにしているのがポイント
?? applyメソッド使わないのは何故??
Elementだけを公開すれば良くなる。
ArrayElement, LineElement, UniformElementの実装は隠蔽。
Element内にprivate classとして定義する。
import Element.elem とすればクラス内からクラスのメソッド同様に呼べる。
10.14 Heighten and widen
大きさの違うElementを結合出来るように拡張する。
小さいほうの両端に空白、空行を入れて大きい方に合わせる。
空白を入れるためのhelper methodsを定義する。
def widen(w: Int): Element =
if (w <= width) this
else {
val left = elem(' ', (w - width) / 2, height)
var right = elem(' ', w - width - left.width, height)
left beside this beside right
}
** left beside this beside right が見たままで分かりやすくて凄い!
?? どうしてprivateで定義しないのか??
?? beside内でwidenを使うので呼び出しが循環参照になっている??
改良後のbeside
def beside(that: Element): Element = {
val this1 = this heighten that.height
val that1 = that heighten this.height
elem(
for ((line1, line2) <- this1.contents zip that1.contents)
yield line1 + line2)
}
** heighten, aboveも同様に改良。
10.15 Putting it all together
与えられた数の角を持つ渦巻きを書くアプリケーションを例題として作成する。
殆ど全ての機能を使う練習として面白い。
** 素晴らしい!!
import Element.elem
object Spiral {
val space = elem(" ")
val corner = elem("+")
def spiral(nEdges: Int, direction: Int): Element = {
if (nEdges == 1)
elem("+")
else {
val sp = spiral(nEdges - 1, (direction + 3) % 4)
def verticalBar = elem('|', 1, sp.height)
def horizontalBar = elem('-', sp.width, 1)
if (direction == 0)
(corner beside horizontalBar) above (sp beside space)
else if (direction == 1)
(sp above space) beside (corner above verticalBar)
else if (direction == 2)
(space beside sp) above (horizontalBar beside corner)
else
(verticalBar above corner) beside (space above sp)
}
}
def main(args: Array[String]) {
val nSides = args(0).toInt
println(spiral(nSides, 0))
}
}
10.16 Conclusion
abstract classes, inheritance and subtyping, class hierarchies,
parametric fields, method overriding, class hierarchy
layout libraryはChapter 14で再び使う。
2014年4月16日水曜日
2014/4/15(火)
朝、あんずからもらったあんこパンが美味しかった。
出社途中で、白い野良猫?に会えた。時々見る猫。近づくと逃げて行ったがこちらの様子を伺っていた。
まぐまぐの「時間のないあなたに!即効TOEIC250点UP」に登録!
中村澄子のメルマガで、著書同様良問を配信してくれる。
studyplusのプロフィールを更新。少しずつ使い方になれた来た。
これまで読んだ洋書を振り返り、完了炭として登録した。
全部で24冊。それなりに読んではいる。
最近読んでないので、また読みたい。
重い腰を上げて、とりあえずブログの備忘録を更新。こちらは今後も整理必要。
[Programming in Scala, First Edition]
2014/4/15 300 chapter9 1-6/6 chapter10 1-11/16
自作の制御構造myWhileをbuilt-inのwhileと同様に定義出来た。
Programming in Scalaは頭から全部読んでいるので、理解できている実感がある。
今まで何となくの理解だった概念もScala視点からは理解できてきた。
Scalaの良さもも分かってきた。
Programming in Scala, First Editionの目次と各節の一言コメントを記載したものを作成すると復習しやすいかも。
目次を使って復習すると言うのは、記憶術的にもよさそうな感じがする。
出社途中で、白い野良猫?に会えた。時々見る猫。近づくと逃げて行ったがこちらの様子を伺っていた。
まぐまぐの「時間のないあなたに!即効TOEIC250点UP」に登録!
中村澄子のメルマガで、著書同様良問を配信してくれる。
studyplusのプロフィールを更新。少しずつ使い方になれた来た。
これまで読んだ洋書を振り返り、完了炭として登録した。
全部で24冊。それなりに読んではいる。
最近読んでないので、また読みたい。
重い腰を上げて、とりあえずブログの備忘録を更新。こちらは今後も整理必要。
[Programming in Scala, First Edition]
2014/4/15 300 chapter9 1-6/6 chapter10 1-11/16
自作の制御構造myWhileをbuilt-inのwhileと同様に定義出来た。
Programming in Scalaは頭から全部読んでいるので、理解できている実感がある。
今まで何となくの理解だった概念もScala視点からは理解できてきた。
Scalaの良さもも分かってきた。
Programming in Scala, First Editionの目次と各節の一言コメントを記載したものを作成すると復習しやすいかも。
目次を使って復習すると言うのは、記憶術的にもよさそうな感じがする。
2014年4月15日火曜日
Programming in Scala, First Edition: Chapter 9
9.1 Reducing code duplication
関数には呼び出し毎に変わる部分と、変わらない部分がある。
変わる部分 -> argument
変わらない部分 -> body
変わる部分(argument)にfunction valueを渡すことでalgorithmを変えられる。
higher-order function(高階関数)
引数に関数をとる関数。
higher-order functionを使ってcontrol abstractionしてduplicationを減らす。
function literalを使うことで簡単にfunction valueを渡せる。
target typingを使うことで型指定を減らせる。
placeholder, _, を使うことで、parameter記述を更に減らせる。
clousre, partially applyを使うことで更にparameterを減らせる。
** 全てを駆使すると非常に短く書ける。凄い。
?? 省略し過ぎて分かり難くならないか少し心配。
?? それでも使われ方から関数の引数の型推論をしてくれないのが不便 。
?? HaskellやMLの様にもっと厳密な型システムなら推論でき る??
?? 全てのtypeの共通の祖先のroot typeがあると、 全てその型に集約されて型チェックが働か無くなる?
動的言語なら実行時にコードをpasteして実行することも出来 るかも。(eval)
shell scriptやtclでは可能。
ex. fileName.endsWith(query) のfunction valueを考える。
fileName: String, query: String, endsWith(String, String): Boolean
全て書くと
(fileNmae: Strng, query: String) => fileName.endsWith(query)
target typingで引数の型を省略
(fileNmae, query) => fileName.endsWith(query)
clousreを使う
fileNmae => fileName.endsWith(query)
placeholderを使う
_.endsWith(query)
9.2 Simplifying client code
higher-order functionのAPIを使えばclient codeも簡素化出来る。
9.3 Currying
** カリー化とは
複数の引数をとる関数を、
引数が「もとの関数の最初の引数」で
戻り値が「もとの関数の残りの引数を取り結果を返す関数」で
あるような関数にすること。(Wikipedia)
scalaでは複数パラーメタリストを持つ関数を書ける。
def sum(w: Int)(x: Int, y: Int)(z: Int) = w + x + y + z
sum: (w: Int)(x: Int, y: Int)(z: Int)Int
?? この記法をcurryingと呼んでいる??
?? 一つのパラメータリストを使う方法とどちらが推奨とかあるのか? ?
?? Haskell等ではこちらの定義の方が一般的
各パラメータリストに複数パラメータがあっても良い。
下記と概念的には透過。
def sum(w: Int) = (x: Int, y: Int) => ((z: Int) => w + x + y + z)
sum: (w: Int)(Int, Int) => Int => Int
?? 型シグネチャは違うが、実装上も違う??
部分適用も出来る。
val sum2 = sum(2)_
sum2: (Int, Int) => Int => Int = <function2>
9.4 Writing new control structures
first-class functionを持つ言語では、function valueで新しいcontrol structureを作れる。
control patternの繰り返しがある場合は、新しいcontrol structureを検討する。
loan pattern
リソースオープン、実行、クローズのパターン等で使う。
実行をfunction valueにして、他を固定化することでリソースリークを防ぐ。
パラメータリスト内の要素が一つの時は()の代わりに{} が使える。
curryingとBy-name parametersと組み合わせると、built- inの様になる。
** 関数を細かく分割すればplaceholderを使えるケースが 増えて簡素に出来る。
9.5 By-name parameters
パラメータ無しの場合、関数リテラルの () => 表記を省略する記法がある。
By-name parametersと呼ばれる。
関数のパラメータの場合にのみ。 fieldやvariableには使えない。
関数リテラルを格納したfunction valueを評価するときの()も付けない。
()なしで評価されても関数実行される。
パラメータのannotationで () => の変わりに => を使う。
ex.
// (cond: => Boolean)(body: => Unit)
// By-name parameters, => で表記。()は使わない。
def myWhile(cond: => Boolean)(body: => Unit): Unit =
if (cond) { // ()無し評価で関数実行
body // ()無し評価で関数実行
myWhile(cond)(body) // パラメータとして渡す時点では評価されない。
}
var i = 10
// curringした関数の第一引数にpartially applyして1引数関数値を生成
// 1引数なので{}を使って更にfunction valueを渡す
myWhile (i > 0) { // () => i > 0 のby-name paramaters記法。
println(i)
i -= 1
} // () => {println(i); i -=1} のby-name paramaters記法。
** whileと同様の制御構造を自分で定義出来た!!
** 引数がある場合はplaceholderを使って簡素化する。
9.6 Conclusion
control abstractions, higher-order functions, currying, by-name parameters
上記を使えばbuilt-inの様に見えるcontrol structureを作れる。
関数には呼び出し毎に変わる部分と、変わらない部分がある。
変わる部分 -> argument
変わらない部分 -> body
変わる部分(argument)にfunction valueを渡すことでalgorithmを変えられる。
higher-order function(高階関数)
引数に関数をとる関数。
higher-order functionを使ってcontrol abstractionしてduplicationを減らす。
function literalを使うことで簡単にfunction valueを渡せる。
target typingを使うことで型指定を減らせる。
placeholder, _, を使うことで、parameter記述を更に減らせる。
clousre, partially applyを使うことで更にparameterを減らせる。
** 全てを駆使すると非常に短く書ける。凄い。
?? 省略し過ぎて分かり難くならないか少し心配。
?? それでも使われ方から関数の引数の型推論をしてくれないのが不便
?? HaskellやMLの様にもっと厳密な型システムなら推論でき
?? 全てのtypeの共通の祖先のroot typeがあると、
動的言語なら実行時にコードをpasteして実行することも出来
shell scriptやtclでは可能。
ex. fileName.endsWith(query)
fileName: String, query: String, endsWith(String, String): Boolean
全て書くと
(fileNmae: Strng, query: String) => fileName.endsWith(query)
target typingで引数の型を省略
(fileNmae, query) => fileName.endsWith(query)
clousreを使う
fileNmae => fileName.endsWith(query)
placeholderを使う
_.endsWith(query)
9.2 Simplifying client code
higher-order functionのAPIを使えばclient codeも簡素化出来る。
9.3 Currying
** カリー化とは
複数の引数をとる関数を、
引数が「もとの関数の最初の引数」で
戻り値が「もとの関数の残りの引数を取り結果を返す関数」で
あるような関数にすること。(Wikipedia)
scalaでは複数パラーメタリストを持つ関数を書ける。
def sum(w: Int)(x: Int, y: Int)(z: Int) = w + x + y + z
sum: (w: Int)(x: Int, y: Int)(z: Int)Int
?? この記法をcurryingと呼んでいる??
?? 一つのパラメータリストを使う方法とどちらが推奨とかあるのか?
?? Haskell等ではこちらの定義の方が一般的
各パラメータリストに複数パラメータがあっても良い。
下記と概念的には透過。
def sum(w: Int) = (x: Int, y: Int) => ((z: Int) => w + x + y + z)
sum: (w: Int)(Int, Int) => Int => Int
?? 型シグネチャは違うが、実装上も違う??
部分適用も出来る。
val sum2 = sum(2)_
sum2: (Int, Int) => Int => Int = <function2>
9.4 Writing new control structures
first-class functionを持つ言語では、function valueで新しいcontrol structureを作れる。
control patternの繰り返しがある場合は、新しいcontrol structureを検討する。
loan pattern
リソースオープン、実行、クローズのパターン等で使う。
実行をfunction valueにして、他を固定化することでリソースリークを防ぐ。
パラメータリスト内の要素が一つの時は()の代わりに{}
curryingとBy-name parametersと組み合わせると、built-
** 関数を細かく分割すればplaceholderを使えるケースが
9.5 By-name parameters
パラメータ無しの場合、関数リテラルの () => 表記を省略する記法がある。
By-name parametersと呼ばれる。
関数のパラメータの場合にのみ。
関数リテラルを格納したfunction valueを評価するときの()も付けない。
()なしで評価されても関数実行される。
パラメータのannotationで () => の変わりに => を使う。
ex.
// (cond: => Boolean)(body: => Unit)
// By-name parameters, => で表記。()は使わない。
def myWhile(cond: => Boolean)(body: => Unit): Unit =
if (cond) { // ()無し評価で関数実行
body // ()無し評価で関数実行
myWhile(cond)(body) // パラメータとして渡す時点では評価されない。
}
var i = 10
// curringした関数の第一引数にpartially applyして1引数関数値を生成
// 1引数なので{}を使って更にfunction valueを渡す
myWhile (i > 0) { // () => i > 0 のby-name paramaters記法。
println(i)
i -= 1
} // () => {println(i); i -=1} のby-name paramaters記法。
** whileと同様の制御構造を自分で定義出来た!!
** 引数がある場合はplaceholderを使って簡素化する。
9.6 Conclusion
control abstractions, higher-order functions, currying, by-name parameters
上記を使えばbuilt-inの様に見えるcontrol structureを作れる。
2014/4/14(月)
昨日は寝る前に足が冷えてしまっていたが、温めながら寝たら眠る前に暖かくなって、朝まで冷たくなかった。
やっぱり安眠には末端を温めるのが重要と思う。
朝ニュースで村岡花子の朗読のレコードが残っていて、フランダースの犬の最後も死なずに幸せに暮らしましたとさ、になっていた。
フランダースの犬の最後は可哀想だという話をよくするが、本当に可哀想なのだろうか?
その時代では苦労したまま報われずに死んでいく人も多かったのかもしれない。
そういう環境では、せめて死んだ後に救済されて幸せになれると言うのは、日々の辛さは先立たれた人を思い起こす時の慰めになったかも知れない。
現代の常識からの視点以外の解釈も面白いと思う。
朝の新聞で電脳戦が人間の1勝4敗とあった。コンピュータは強くなる一方なので、そろそろ人間に勝ち目はないかも知れない。ちょっと寂しい。
ただ「コンピュータで東大合格を目指すプロジェクト」で言われている様に、コンピュータが苦手な分野もまだまだある。
苦手な分野の克服よりも、人間との協業を目指した方が人間のためになる気がする。
動物将棋についてもLang-8ネタには使えそう。
アプリもあるようなので子供にやらせても良いかも知れない。
今後発展が見込まれるとしてミャンマーが注目を集めているが、マネーが集まってくると色々な問題が発生しているともあった。
新聞を読んでいても思うのだが、時間は有限なので情報発信/収集内容の断捨離は重要。
速読や記憶術で効率よく情報を処理できるようになるのも重要。
やっぱり思いついた事をすぐ忘れるので小さいメモ帳を常に携帯した方が良い気がする。
アインシュタインだかエジソンだかも何かあれば常にメモしていたと聞いた気がする。
また、起きた瞬間に良いことを思いつくことが多いので、枕元にも置いておいた方が良いとか。
私は朝シャワーを浴びているときに色々考えるので、風呂場にも必要? 防水は?(笑)
今夜は子供たちが寝る前に帰宅できた。
サンもあんずも素直に寝てくれた。
あんずは最近お喋りが上手になってきて、無邪気でとても愛らしい。
オイコノミアの断捨離の後半と食べ物の前半を見たが、あまり面白いネタが無かった。
減反の話で、需要が価格の影響を受けにくと、価格が下がると供給側に不利になるのは分かった。
あと、さりげなく同調バイアスと言っていたのが面白かった。
物事には因果関係があるが、子供は全ての事が自分の行為の影響だと考る傾向が強い。
主観的で、世界を自分中心で捉えているから。
成長するに従い客観的になり、自分の手の届かない範囲でも物事が進んでいる事も認識出来てくる。
Programming in Scala, First Editionのchapter8まで進んだ。
関数型プログラミングで重要なのは並列化可能であること。
そのためには処理の順番非依存である必要がある。
そうすると参照透過性が必要になる。
そうすると結局人間に分かりやすくなる。
reduceも分割してreduceした結果を後で合成しても結果は同じになる。
この処理順番非依存と言うのが、処理の手順を考える手続き型、命令型のプログラミングスタイルとは大きな発想の違いに感じる。
うーん、徒然なるままに書いてみたけど、これで良いのかなあ。。。
何も書かないよりはマシだから、あれこれ悩まずにしばらく続けてみるか。。。
やっぱり安眠には末端を温めるのが重要と思う。
朝ニュースで村岡花子の朗読のレコードが残っていて、フランダースの犬の最後も死なずに幸せに暮らしましたとさ、になっていた。
フランダースの犬の最後は可哀想だという話をよくするが、本当に可哀想なのだろうか?
その時代では苦労したまま報われずに死んでいく人も多かったのかもしれない。
そういう環境では、せめて死んだ後に救済されて幸せになれると言うのは、日々の辛さは先立たれた人を思い起こす時の慰めになったかも知れない。
現代の常識からの視点以外の解釈も面白いと思う。
朝の新聞で電脳戦が人間の1勝4敗とあった。コンピュータは強くなる一方なので、そろそろ人間に勝ち目はないかも知れない。ちょっと寂しい。
ただ「コンピュータで東大合格を目指すプロジェクト」で言われている様に、コンピュータが苦手な分野もまだまだある。
苦手な分野の克服よりも、人間との協業を目指した方が人間のためになる気がする。
動物将棋についてもLang-8ネタには使えそう。
アプリもあるようなので子供にやらせても良いかも知れない。
今後発展が見込まれるとしてミャンマーが注目を集めているが、マネーが集まってくると色々な問題が発生しているともあった。
新聞を読んでいても思うのだが、時間は有限なので情報発信/収集内容の断捨離は重要。
速読や記憶術で効率よく情報を処理できるようになるのも重要。
やっぱり思いついた事をすぐ忘れるので小さいメモ帳を常に携帯した方が良い気がする。
アインシュタインだかエジソンだかも何かあれば常にメモしていたと聞いた気がする。
また、起きた瞬間に良いことを思いつくことが多いので、枕元にも置いておいた方が良いとか。
私は朝シャワーを浴びているときに色々考えるので、風呂場にも必要? 防水は?(笑)
今夜は子供たちが寝る前に帰宅できた。
サンもあんずも素直に寝てくれた。
あんずは最近お喋りが上手になってきて、無邪気でとても愛らしい。
オイコノミアの断捨離の後半と食べ物の前半を見たが、あまり面白いネタが無かった。
減反の話で、需要が価格の影響を受けにくと、価格が下がると供給側に不利になるのは分かった。
あと、さりげなく同調バイアスと言っていたのが面白かった。
物事には因果関係があるが、子供は全ての事が自分の行為の影響だと考る傾向が強い。
主観的で、世界を自分中心で捉えているから。
成長するに従い客観的になり、自分の手の届かない範囲でも物事が進んでいる事も認識出来てくる。
Programming in Scala, First Editionのchapter8まで進んだ。
関数型プログラミングで重要なのは並列化可能であること。
そのためには処理の順番非依存である必要がある。
そうすると参照透過性が必要になる。
そうすると結局人間に分かりやすくなる。
reduceも分割してreduceした結果を後で合成しても結果は同じになる。
この処理順番非依存と言うのが、処理の手順を考える手続き型、命令型のプログラミングスタイルとは大きな発想の違いに感じる。
うーん、徒然なるままに書いてみたけど、これで良いのかなあ。。。
何も書かないよりはマシだから、あれこれ悩まずにしばらく続けてみるか。。。
2014年4月14日月曜日
Programming in Scala, First Edition: Chapter 8
8. Functions and Closures
色々な関数のバリエーションがある。
methods, nested within functions, function literals, function values
javaでは出来ない定義方法もある。
関数に部品化する事は分かりやすくするために重要。
8.1 Methods
何らかのobjectのmemberとして定義されているfun ction。
javaでもある普通のmethod。
helper method等はprivateにすることも出来る。
8.2 Local functions
関数の中で関数を定義できる。ローカル変数と同じように。
関数名のスコープは関数内に閉じる。
名前の衝突を気にしなくて良くなる。
private modifierは不要。
上位の関数のスコープ内の変数にもアクセス出来る。
普遍のパラメータを引数で渡す必要がない。
?? 宣言時or実行時どちらの値が使われる??
?? 密結合になって分かり難くならないか??
沢山の小さい関数を作ってそれを積み上げていくやり方を推奨。
その方が分かりやすい。
名前の衝突の問題が出てくるが、local functionsは一つの解決手段。
scalaの全てnest可能と言う概念は単純かつ強力。
8.3 First-class functions
関数リテラルで関数を表したり、 オブジェクトとして代入したり実行したり出来る。
function literalは実行時はfunction valueにインスタンス化される。
classが実行時にはobjectにインスタンス化されるのと 同様。
(parameter: annotation, ...) => body
ex. (x: Int) => x + 1
collectionのforeachやfilterメソッドの 引数として渡せる。
bodyは{}で括れば何でも書ける。
8.4 Short forms of function literals
scalaでは冗長情報を省略する記法を色々と用意している。
なるべく簡素に書くようにした方が良い。
関数リテラルの引数の型が推論できる場合は省略出来る。( target typing)
関数リテラルの引数の()も省略出来る場合が多い。
x => x + 1
?? とりあえず省略して書いてコンパイラが混乱したら付け加えると良 い??
?? コンパイラが誤解してそのまま通ってしまうと。 バグになるのでは??
8.5 Placeholder syntax
パラメータの記述を省略して、_ をパラメータのplaceholderとして使える。
_ + 1
annotationもbody側に付ける。
(_: Int) + (_: Int)
一つのパラメータがbodyに一回しか出てこない場合にしか使え ない。
一つのパラメータが繰り返し出てくるときには使えない。
最初に出てきた_が最初のパラメータ、2番目の_ は2番目のパラメータと解釈される。
複数のパラメータでも使える。
8.6 Partially applied functions
引数に_を使うことで関数の部分適用が出来る。
def sum(a: Int, b: Int, c: Int) = a + b + c
sum(_: Int, _: Int, 1) // 一つのパラメータを適用したfunction value
sum(_, _, _) // 全部のパラメータを適用していないfunction value
sum _ // 全部のパラメータを適用していないfunction value
sum_はNGでスペースが必要なので注意。
function typeが期待されているときは_を省略することが出来る。
Array(1, 2, 3).foreach(println)
?? sum(_: Int, _: Int, 1)のannotationはどうして省略できない??
?? sum _ と sum(_)は違う??
?? 同じ名前の関数をoverload出来るのはmethodだけ? local functionではNG??
?? 別スコープの関数をshadowするとsignatureが違っ ても元関数は呼べない??
8.7 Closures
closureはfree variableが生成時に閉じられた(capturing) function value
x => x + more // more: free variable, x: bound variable
free variableがないfunction literalはclosed term(コードの断片)
x => x + 1
free variableありはopen term
free variableの元変数が変更されたらclosure内の値も 変化する。
逆にclosure内で値を変更したら元の値も変化する。
?? 値ではなくてリファレンスをcapturingしてる??
var, val, parameterでもlocal variableでも何でもcapturing出来る。
stackに置かれているものでも自動的にheapに置きなおさ れる。
?? パフォーマンス上の問題はでない??
scalaでは関数の部分適用を明示的示すために_ を一部を除き必須にしている。
これは古典的な関数型減言語(Haskell,ML) とは異なる。
javaとの親和性を考えて言語仕様のtrade- offでそうなっている。
root typeなら何でもOKと言う世界だと厳密な型チェックが出来な い。
間違って関数部分適用の形になってもエラーチェックできないので 明示する。
8.8 Repeated parameters
最後の引数のannotationに* を付けることで任意回数の繰り返しを表現できる。
def f(args: Int*) = for (arg <- args) println(arg)
関数内ではArray typeとして扱われる。
Arrayを引数として渡したときはannotationで_* とする。
f(Array(1, 2, 3): _*)
?? : Int* ではなんでだめ??
8.9 Tail recursion
再帰呼び出しでも、末尾再帰の最適化でループと同等の性能になる。
ループの方がメモリ消費や処理速度で一般に優れているが、最適化出来れば変わらない。
Tracing tail-recursive functions
末尾再帰と言うのは、再帰呼び出しの後に処理がないこと。
処理の一番最後が再帰呼び出しだと言うこと。
単純に関数の先頭にジャンプする処理に置き換えられる。
exceptionを発生されてtraceを見るとスタックが積まれていないのが見える。
Limits of tail recursion
javaのJVMの実装に依存して最適化出来るパターンが限られている。
複数の関数の相互呼び出しでは最適化されない。
function valueに同一関数を格納して呼び出しても最適化されない。
自分自身を直接最後に呼び出した場合のみ最適化される。
?? 最新コンパイラでも同じ??
8.10 Conclusion
methods, local functions, function literals, function values.
partially applied functions, functions with repeated parameters.
optimized tail calls.
色々な関数のバリエーションがある。
methods, nested within functions, function literals, function values
javaでは出来ない定義方法もある。
関数に部品化する事は分かりやすくするために重要。
8.1 Methods
何らかのobjectのmemberとして定義されているfun
javaでもある普通のmethod。
helper method等はprivateにすることも出来る。
8.2 Local functions
関数の中で関数を定義できる。ローカル変数と同じように。
関数名のスコープは関数内に閉じる。
名前の衝突を気にしなくて良くなる。
private modifierは不要。
上位の関数のスコープ内の変数にもアクセス出来る。
普遍のパラメータを引数で渡す必要がない。
?? 宣言時or実行時どちらの値が使われる??
?? 密結合になって分かり難くならないか??
沢山の小さい関数を作ってそれを積み上げていくやり方を推奨。
その方が分かりやすい。
名前の衝突の問題が出てくるが、local functionsは一つの解決手段。
scalaの全てnest可能と言う概念は単純かつ強力。
8.3 First-class functions
関数リテラルで関数を表したり、
function literalは実行時はfunction valueにインスタンス化される。
classが実行時にはobjectにインスタンス化されるのと
(parameter: annotation, ...) => body
ex. (x: Int) => x + 1
collectionのforeachやfilterメソッドの
bodyは{}で括れば何でも書ける。
8.4 Short forms of function literals
scalaでは冗長情報を省略する記法を色々と用意している。
なるべく簡素に書くようにした方が良い。
関数リテラルの引数の型が推論できる場合は省略出来る。(
関数リテラルの引数の()も省略出来る場合が多い。
x => x + 1
?? とりあえず省略して書いてコンパイラが混乱したら付け加えると良
?? コンパイラが誤解してそのまま通ってしまうと。
8.5 Placeholder syntax
パラメータの記述を省略して、_
_ + 1
annotationもbody側に付ける。
(_: Int) + (_: Int)
一つのパラメータがbodyに一回しか出てこない場合にしか使え
一つのパラメータが繰り返し出てくるときには使えない。
最初に出てきた_が最初のパラメータ、2番目の_
複数のパラメータでも使える。
8.6 Partially applied functions
引数に_を使うことで関数の部分適用が出来る。
def sum(a: Int, b: Int, c: Int) = a + b + c
sum(_: Int, _: Int, 1) // 一つのパラメータを適用したfunction value
sum(_, _, _) // 全部のパラメータを適用していないfunction value
sum _ // 全部のパラメータを適用していないfunction value
sum_はNGでスペースが必要なので注意。
function typeが期待されているときは_を省略することが出来る。
Array(1, 2, 3).foreach(println)
?? sum(_: Int, _: Int, 1)のannotationはどうして省略できない??
?? sum _ と sum(_)は違う??
?? 同じ名前の関数をoverload出来るのはmethodだけ? local functionではNG??
?? 別スコープの関数をshadowするとsignatureが違っ
8.7 Closures
closureはfree variableが生成時に閉じられた(capturing)
x => x + more // more: free variable, x: bound variable
free variableがないfunction literalはclosed term(コードの断片)
x => x + 1
free variableありはopen term
free variableの元変数が変更されたらclosure内の値も
逆にclosure内で値を変更したら元の値も変化する。
?? 値ではなくてリファレンスをcapturingしてる??
var, val, parameterでもlocal variableでも何でもcapturing出来る。
stackに置かれているものでも自動的にheapに置きなおさ
?? パフォーマンス上の問題はでない??
scalaでは関数の部分適用を明示的示すために_
これは古典的な関数型減言語(Haskell,ML)
javaとの親和性を考えて言語仕様のtrade-
root typeなら何でもOKと言う世界だと厳密な型チェックが出来な
間違って関数部分適用の形になってもエラーチェックできないので
8.8 Repeated parameters
最後の引数のannotationに*
def f(args: Int*) = for (arg <- args) println(arg)
関数内ではArray typeとして扱われる。
Arrayを引数として渡したときはannotationで_*
f(Array(1, 2, 3): _*)
?? : Int* ではなんでだめ??
8.9 Tail recursion
再帰呼び出しでも、末尾再帰の最適化でループと同等の性能になる。
ループの方がメモリ消費や処理速度で一般に優れているが、最適化出来れば変わらない。
Tracing tail-recursive functions
末尾再帰と言うのは、再帰呼び出しの後に処理がないこと。
処理の一番最後が再帰呼び出しだと言うこと。
単純に関数の先頭にジャンプする処理に置き換えられる。
exceptionを発生されてtraceを見るとスタックが積まれていないのが見える。
Limits of tail recursion
javaのJVMの実装に依存して最適化出来るパターンが限られている。
複数の関数の相互呼び出しでは最適化されない。
function valueに同一関数を格納して呼び出しても最適化されない。
自分自身を直接最後に呼び出した場合のみ最適化される。
?? 最新コンパイラでも同じ??
8.10 Conclusion
methods, local functions, function literals, function values.
partially applied functions, functions with repeated parameters.
optimized tail calls.
登録:
投稿 (Atom)