ゆるふわScalaコップ本読書会 第7章

40
ゆゆゆゆゆゆゆゆゆゆゆゆ 7 ゆ Scala ののののののののののののののの ののののののののののののののののののののののの ののののの のののの のののののののののののののののの ののののののののののののの 。一、。 P130

Transcript of ゆるふわScalaコップ本読書会 第7章

Page 1: ゆるふわScalaコップ本読書会 第7章

ゆるふわコップ本読書会第 7章

Scalaの基本制御構造はごく小規模だが、命令形言語の基本機能を全て提供できるだけの力を持っている。そして一貫して結果値を返すことによって、コードの短縮化を実現す

 る。 P130

Page 2: ゆるふわScalaコップ本読書会 第7章

はじめに

・ Scalaの制御構造 (if,while,for,,,)は、関数型へのアプローチとしてほとんどが何らかの値を返します。

・関数型言語ではプログラムは値を計算する存在とみなします。

Page 3: ゆるふわScalaコップ本読書会 第7章

if 式

if (条件式) A [else B]条件式: Boolean 型である必要があるelse B :省略可能 (省略した場合、下記と同価 )if (条件式) A else ()

条件式が falseの時Unit 型が返ってくる

Scalaの if は、他の多くの言語と同じように機能します。

次は具体例

Page 4: ゆるふわScalaコップ本読書会 第7章

if 式の具体例val filename = if (!augs.isEmpty) args(0) else “default.txt”

ポイント・ valを使用する事より、読解時、変数の値が不変の為、変数のスコープ内を全チェックして値の変化を調べる必要が無い。・ valを使用する事より、等式推論 ( 変数と計算式を等価とみなす ) がサポートされ、変数名の代わりに計算式を書ける。println (if (!args.isEmpty) args(0) else “default.txt”)

条件式 true の時 falseの時

値を返す

Page 5: ゆるふわScalaコップ本読書会 第7章

while ループwhile (条件式) A条件式: Boolean 型である必要がある条件式が trueの間、 Aを評価し続けます。while も式なので値を返しますが、返却値は Unit 型の値 () を返します。※while は意味のある値を返さず、 Unit型を返すため、式では無く「ループ」と呼ばれます。

Page 6: ゆるふわScalaコップ本読書会 第7章

while ループの具体例while (条件式) Avar count = 0while (count < 10) { println(count) count += 1}

注意点・ while自体は値を返さない為、計算処理をする時は、 varを更新するか、入出力処理を実行する必要がある。つまり!!、whileを使う部分では、変数の値の変化がある為、 while自体非推奨

なので、 while を使わない選択を

Page 7: ゆるふわScalaコップ本読書会 第7章

while ループを使わない

scala> def gcd(x:Long,y:Long):Long = if (y == 0) x else gcd(y,x % y)gcd: (x: Long, y: Long)Long

scala> gcd(24,8)res3: Long = 8

ループ処理を whileを使わずに処理し、以下のように再起 ( 自分を呼び出す ) を使用するのが一般的です。

条件式が falseになるまで繰り返すvarが出てこな

い!!

例:最大公約数を求める

Page 8: ゆるふわScalaコップ本読書会 第7章

while ループでよくあるエラー

var line = “”while((line = readLine()) != “”)println(“Read: ” + line)

コンパイルすると、「 Unit 型の値と String型の値を != で比較すれば、必ず true になる」という警告が表示されます。※scalaでは、代入の結果値は常に Unit 値 () になります。

代入結果は Unit 型

必ず true

String型

無限ループ

Page 9: ゆるふわScalaコップ本読書会 第7章

for 式for( ジェネレータ 1; ジェネレータ 2; ... ジェネレータ n) A

for(a1 <- exp1; a2 <- exp2; ... an <- expn) A

一般例

具体例

変数 a1〜 an:ループ変数exp1~ expn:式。例えば、ある数の範囲を表す式※ 「 1 to 10」や「 1 until 10 」

Page 10: ゆるふわScalaコップ本読書会 第7章

for 式の具体例for(x <- 1 to 3; y <- 1 until 4 if x != y){  println("x = " + x + " y = " + y)}

x = 1 y = 2x = 1 y = 3x = 2 y = 1x = 2 y = 3x = 3 y = 1x = 3 y = 2

各変数に対する計算式出力結果

Page 11: ゆるふわScalaコップ本読書会 第7章

for 式の具体例 2val fileHere = (new java.io.file(“.”)).listfilesfor( file <- filesHere )  println(file)

良い例

val fileHere = (new java.io.file(“.”)).listfilesfor( i <- 0 to fileHere.length - 1 )  println(filesHere(i))

駄目な例

Scalaではコレクションを直接反復処理できる。その方がコードは短くなるし、数値の境界値におけるエラー等を避けられる。例:「 0 to 9    」 を 「 1 to 10」にしてしまう等のエラーを直接コレクションを弄る事により無くせる。

Page 12: ゆるふわScalaコップ本読書会 第7章

for 式のフィルタリングval fileHere = (new java.io.file(“.”)).listfilesfor(   file <- filesHere   if file.isFile                                       //  ファイルであるか?  if file.getName.endswith(“.scala”)   //  ”末尾が .scala” であるか?)  println(file) フィルタリング

if 文を複数追加可能

Page 13: ゆるふわScalaコップ本読書会 第7章

for 式の入れ子の反復処理for(i <- 0 to 4 if i % 2 == 0;x <- 0 to 4 if x % 2 != 0){  println("i = " + i + ";  x = " + x)}i = 0;  x = 1i = 0;  x = 3i = 2;  x = 1i = 2;  x = 3i = 4;  x = 1i = 4;  x = 3

複数のコレクションi = 0 に対してx = 0 ~ 4

i = 2 に対してx = 0 ~ 4

...

Page 14: ゆるふわScalaコップ本読書会 第7章

変数への中間結果の束縛for{  line <- fileLines(file)  if line.trim.matches(pattern)} println(file + “: ” + line.trim)

for{  line <- fileLines(file)  trimed = line.trim  if trimed.matches(pattern)} println(file + “: ” + trimmed)

line.trim が複数回呼ばれるので、等号 (=)を使って変数に結果を束縛する。束縛というのは、等号の左辺と右辺の式の結果値を紐付ける。要は、この式の中だけで有効な代入である。

Page 15: ゆるふわScalaコップ本読書会 第7章

新しいコレクションの作成

scala> var a = for{i <- 0 to 6 if i % 2 == 0} yield ia: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6)

今までの例では、反復的に生成された値をその場で使用しただけでしたが、 yield を使い、値を保存する事ができます。

for {節 } yield < 本体>

一般例

具体例

生成された値を格納していく

Page 16: ゆるふわScalaコップ本読書会 第7章

if と for を使った練習問題1から 100までの 3つの整数 a, b, cについて、三辺からな

 る三角形が直角三角形になるような a, b, cの組み合わせを全て出力してください。直角三角形の条件にはピタゴラス

 の定理を利用してください。ピタゴラスの定理とは三平方の定理とも呼ばれ、 a ^ 2 == b ^ 2 + c ^ 2を満たす、 a, b, c の長さの三辺を持つ三角形は、直角三角形になるというものです。

要は、 a^2 == b^2 + c^2 となる a,b,c の組み合わせを出力してください

Page 17: ゆるふわScalaコップ本読書会 第7章

練習問題のヒント

for(a <- 1 to 100; b <- 1 to 100; c <- 1 to 100) { println((a, b, c))}

単純に、 a,b,cの組み合わせを出力するだけなら下記

for節の中に、 a^2 == b^2 + b^2となる条件をつけて下さい

Page 18: ゆるふわScalaコップ本読書会 第7章

練習問題の回答for( a <- 1 to 100; b <- 1 to 100; c <- 1 to 100 if a * a == b * b + c * c) { println((a, b, c))}

条件を付ける

Page 19: ゆるふわScalaコップ本読書会 第7章

try 式による例外処理・メソッドは、通常の値で結果値を返すのでは無く、例外を投げて終了する事ができる。

・メソッドの呼び出し元は、例外をキャッチして処理するか、そのまま終了する事ができる。

・例外は、キャッチして処理するメソッドが現れるまで、呼び出しスタックを巻き戻しながら呼び出し元から呼び出し元へと例外が伝播していく。

Page 20: ゆるふわScalaコップ本読書会 第7章

try 式による例外処理try {   何らかの処理が失敗時に例外を投げる。  又は、明示的に例外を投げる} catch {  // 例外を受ける所   case ex: Exception => //  例外の処理} finally {  // 最後に必ず実行する処理を書く所   何らかの処理}

Page 21: ゆるふわScalaコップ本読書会 第7章

try 式による例外のスローscala> val n = 3n: Int = 3

scala> val half = if (n % 2 == 1) {n / 2} else {throw new RuntimeException("n must be even")}half: Int = 1

scala> val half = if (n % 2 == 0) {n / 2} else {throw new RuntimeException("n must be even")}java.lang.RuntimeException: n must be even  ... 27 elided

条件にマッチしたので例外投げない

条件にマッチしないので例外を投げる値を返す

例外を返す

Page 22: ゆるふわScalaコップ本読書会 第7章

例外のキャッチimport 色々

try{  val f = new FileReader(“input.txt”)} catch {  case ex: FileNotFoundException => { ファイル無し error 処理 }  case ex: IOException => {I/O error 処理 }} finally {  file.close()}

最後に必ず呼ばれる処理を書く。例外が呼ばれた場合でも、必ずファイルは閉じる。

例外のキャッチと処理

Page 23: ゆるふわScalaコップ本読書会 第7章

例外発生時の値の生成について・例外が投げられない場合は、 try節の値となる。・例外が投げられて、 catch節で処理した場合は、 catch節の値となる。

・例外が投げられて、 catch節で処理されなかった場合は、結果値を持たない。

・例外が投げられて、 finally節が計算した値は、 finally の値で上書きされる。その為、 finally節では、一般的に try や catchの計算された値を変更してはならない。 次に finally の副作

Page 24: ゆるふわScalaコップ本読書会 第7章

直感に反する値の生成scala> def f(): Int = try return 1 finally return 2f: ()Int

scala> f()res3: Int = 2

scala> def g(): Int = try 1 finally 2g: ()Int

scala> g()res4: Int = 1

finallyの値が返る

tryの値が返る

不思議?

Page 25: ゆるふわScalaコップ本読書会 第7章

例外の注意点 1Javaの検査例外も含めすべての例外が非検査例外の扱いになる。 Javaの API を呼び出す時に throwされる例外を全て捕捉しなくてもコンパイルエラーにはなりません。Javaとの相互運用のために @throws アノテーションを用いると、 throws を付与した Javaバイトコードを生成することができます。

@throws(classOf[FooException])@throws(classOf[BarException])

Javaとの相互運用時、 Javaの例外をなげる場合

Page 26: ゆるふわScalaコップ本読書会 第7章

例外の注意点 2例外は非同期プログラミングでは使えない

例外の動作は送出されたら catch されるまでコールスタックを遡っていくというものです。ということは別スレッドや、別のイベントループなどで実行される非同期プログラミングとは相容れないものです。特に Scalaでは非同期プログラミングが多用されるので、例外をそのまま使えないことが多いです。

Page 27: ゆるふわScalaコップ本読書会 第7章

match 式

 マッチ対象の式 match {  case  パターン 1 [if ガード 1] => 式 1  case  パターン 2 [if ガード 2] => 式 2  case ...  case  パターン N =>  式 N}

scala> val one = 1one: Int = 1

scala> one match {     | case 1 => "one"     | case _ => "other"     | }res0: String = one

具体例一般例

1に matchしたら oneを返す。それ以外なら other を返す。

Page 28: ゆるふわScalaコップ本読書会 第7章

match 式・ Scalaの caseでは、 Javaの case文とは異なり、整数型や文字列型だけでは無く、あらゆる型を扱えます。

・ Scalaでは、 breakが暗黙のうちに指定され、上の選択肢から下の選択肢に制御が落ちる事はありません。

・ Javaの switchとの大きな違いは、 match式が結果値を生成する事である。

Page 29: ゆるふわScalaコップ本読書会 第7章

break と continue は無いvar i = 0var foundIt = falsewhile (i < args.length && !foundIt) { // 引数の方が長く、見つかって無い  if (!args(i).startsWith(“-”)) {             //  ”文頭が -”ではない    if (args(i).endWith(“.scala”))         //  ”文末が .scala” である      foundIt = true                   // 見つかった!!  }  i = i + 1} 命令型の書き方だと

Page 30: ゆるふわScalaコップ本読書会 第7章

break と continue は無い

def searchfrom(i: Int): Int =   if (i >= arts.length) -1  else if (args(i).startsWith(“-”))    searchFrom(i + 1)  else if (args(i).endWith(“.scala”)  i  else                                              searchFrom(i + 1)val i = searchFrom(0)

”文頭が -” なので、iに 1を足して最チャレンジ ( 再帰 )

”文末が .scala” では無いので、iに 1を足して最チャレンジ ( 再帰 )

ループ処理は、再帰処理と valのみを使う

Page 31: ゆるふわScalaコップ本読書会 第7章

変数のスコープ

val a = 1;{  println(a)}

1 と表示される

val a = 1;{  val a = 2   println(a)}println(a)21と表示される

val a = 1;{  println(a)  val a = 2   println(a)}

error

変数のスコープは {} 等で切り替わる。※ 中括弧以外で切り替わるのある??

forward reference extends over definition of value a

Page 32: ゆるふわScalaコップ本読書会 第7章

命令形から関数型へ 1scala> def printArgs(args: Array[String]): Unit = {     |     var i = 0     |     while (i < args.length) {     |         println(args(i))     |         i += 1     |     }     | }printArgs: (args: Array[String])Unit

scala> printArgs(Array("zero", "one", "two"))zeroonetwo

標準出力という副作用

var は控えよう!

Page 33: ゆるふわScalaコップ本読書会 第7章

命令形から関数型へ 1・ whileを使う部分では、 変数の値の変化がある為、 while自体非推奨・ var では無く、 valを使おう!!

scala> def printArgs2(args: Array[String]): Unit = {     |     for (arg <- args)     |     println(arg)     | }printArgs2: (args: Array[String])Unit

scala> printArgs2(Array("zero", "one", "two"))zeroonetwo

var を無くし、while から for への変更

Unit 型

Page 34: ゆるふわScalaコップ本読書会 第7章

命令形から関数型へ 2 返り値のデータ型が Unit  型 のメソッドは、実行すると副作

用を引き起こすメソッドである。

   副作用 を伴う println()  メソッド の呼び出し処理を、「引数のリストから要素を取り出して、標準出力に表示させたい文字列を返す処理」として切り離し、副作用が生じる処理領域を見つけやすいようする。

副作用の無い関数には、単体テストが簡単になるというメリットもある。

Page 35: ゆるふわScalaコップ本読書会 第7章

命令形から関数型へ 2scala> def formatArgs3(args: Array[String]) = args.mkString("\n")formatArgs3: (args: Array[String])String

scala> println(formatArgs3(Array("zero", "one", "two")))zeroonetwo

String型

String型を受取り、受け取り手が標準出力を実施する。

Page 36: ゆるふわScalaコップ本読書会 第7章

if,try,match の練習問題”入力された西暦が、閏年ならば「 LeapYear” 」

”閏年でないなら「 NotLeapYear” 」 2000年なら”「 Millennium”」

を返すメソッドを作ってください。受け取った値に応じて、「閏年です」や「閏年では無い」という標準出力、又は「 Problem 」という例外を返してください。

★閏年の定義・西暦年が 4で割り切れる年は閏年・ただし、西暦年が 100 で割り切れる年は平年・ただし、西暦年が 400 で割り切れる年は閏年

Page 37: ゆるふわScalaコップ本読書会 第7章

練習問題のヒント閏年判定部分(year % 400 == 0) || (year % 100 != 0 && year % 4 == 0)match 部分val YearJudge = 閏年判定メソッド (2000)try{  YearJudge match{    case “LeapYear” =>     ・・・  }} catch {} finally {}

Page 38: ゆるふわScalaコップ本読書会 第7章

練習問題の回答def isLeapYear(year: Int) = {  if (year == 2000) {    "Millennium"  } else if ((year % 400 == 0) || (year % 100 != 0 && year % 4 == 0)) {    "LeapYear"  } else {    "NotLeapYear"  }}

2枚目に続く

Page 39: ゆるふわScalaコップ本読書会 第7章

練習問題の回答val YearJudge = isLeapYear(2000)try{  YearJudge match {    case "LeapYear"    => println("閏年です ")    case "Other"          => println("閏年では無い ")    case "Millennium"  => throw new Exception("Problem")  }} catch {  case ex: Exception  => println("Millennium catch")} finally {  println("finish")}

Page 40: ゆるふわScalaコップ本読書会 第7章

まとめ・ Scalaに組み込みの制御構造は最小限のものだが、  必要な仕事を見事にこなす。

・制御構造は対応する命令形言語の構文と良く似ているが、  ほとんどが結果値を返すので、  関数型スタイルもサポートする。

・次章では、関数リテラルについて説明されます!!