Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

267 views

Published on

https://weeyble-scala.connpass.com/event/51207/
https://connpass.com/user/yutayokoi/open/
発表資料です。

Published in: Technology
  • Be the first to comment

  • Be the first to like this

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

  1. 1. ゆるふわコップ本読書会第7章 Scalaの基本制御構造はごく小規模だが、命令形言語の基本 機能を全て提供できるだけの力を持っている。そして一貫し て結果値を返すことによって、コードの短縮化を実現する。 P130
  2. 2. はじめに ・Scalaの制御構造(if,while,for,,,)は、関数型へのアプローチ としてほとんどが何らかの値を返します。 ・関数型言語ではプログラムは値を計算する存在とみなしま す。
  3. 3. if式 if (条件式) A [else B] 条件式:Boolean型である必要がある else B:省略可能(省略した場合、下記と同価) if(条件式) A else () 条件式がfalseの時 Unit型が返ってくる Scalaのifは、他の多くの言語と同じように機能します。 次は具体例
  4. 4. if式の具体例 val filename = if (!augs.isEmpty) args(0) else “default.txt” ポイント ・valを使用する事より、読解時、変数の値が不変の為、変数 のスコープ内を全チェックして値の変化を調べる必要が無い。 ・valを使用する事より、等式推論(変数と計算式を等価とみな す)がサポートされ、変数名の代わりに計算式を書ける。 println (if (!args.isEmpty) args(0) else “default.txt”) 条件式 trueの時 falseの時 値を返す
  5. 5. whileループ while (条件式) A 条件式:Boolean型である必要がある 条件式がtrueの間、Aを評価し続けます。 whileも式なので値を返しますが、 返却値はUnit型の値()を返します。 ※whileは意味のある値を返さず、Unit型を返すた め、式では無く「ループ」と呼ばれます。
  6. 6. whileループの具体例 while (条件式) A var count = 0 while (count < 10) { println(count) count += 1 } 注意点 ・while自体は値を返さない為、 計算処理をする時は、varを更新す るか、入出力処理を実行する必要 がある。 つまり!!、 whileを使う部分では、変数の値の 変化がある為、while自体非推奨 なので、whileを使わない選択を
  7. 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が出てこない!! 例:最大公約数を求める
  8. 8. whileループでよくあるエラー var line = “” while((line = readLine()) != “”) println(“Read: ” + line) コンパイルすると、「Unit型の値とString型の値を!=で比較す れば、必ずtrueになる」という警告が表示されます。 ※scalaでは、代入の結果値は常にUnit値()になります。 代入結果はUnit型 必ずtrue String型 無限 ループ
  9. 9. for式 for(ジェネレータ1; ジェネレータ2; ... ジェネレータn) A for(a1 <- exp1; a2 <- exp2; ... an <- expn) A 一般例 具体例 変数a1〜an:ループ変数 exp1~expn:式。例えば、ある数の範囲を表す式 ※「1 to 10」や「1 until 10」
  10. 10. for式の具体例 for(x <- 1 to 3; y <- 1 until 4 if x != y){ println("x = " + x + " y = " + y) } x = 1 y = 2 x = 1 y = 3 x = 2 y = 1 x = 2 y = 3 x = 3 y = 1 x = 3 y = 2 各変数に対する計算式 出力結果
  11. 11. for式の具体例2 val fileHere = (new java.io.file(“.”)).listfiles for( file <- filesHere ) println(file) 良い例 val fileHere = (new java.io.file(“.”)).listfiles for( i <- 0 to fileHere.length - 1 ) println(filesHere(i)) 駄目な例 Scalaではコレクションを直接反復処理できる。 その方がコードは短くなるし、 数値の境界値におけるエラー等を避けられる。 例:「0 to 9」 を 「1 to 10」にしてしまう等のエ ラーを直接コレクションを弄る事により無くせる。
  12. 12. for式のフィルタリング val fileHere = (new java.io.file(“.”)).listfiles for( file <- filesHere if file.isFile // ファイルであるか? if file.getName.endswith(“.scala”) // 末尾が”.scala”であるか? ) println(file) フィルタリング if文を複数追加可能
  13. 13. 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 = 1 i = 0; x = 3 i = 2; x = 1 i = 2; x = 3 i = 4; x = 1 i = 4; x = 3 複数のコレクションi = 0に対して x = 0 ~ 4 i = 2に対して x = 0 ~ 4 ...
  14. 14. 変数への中間結果の束縛 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が複数回呼ばれるので、 等号(=)を使って変数に結果を束 縛する。束縛というのは、等号の 左辺と右辺の式の結果値を紐付け る。要は、この式の中だけで有効 な代入である。
  15. 15. 新しいコレクションの作成 scala> var a = for{i <- 0 to 6 if i % 2 == 0} yield i a: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6) 今までの例では、反復的に生成された値をその場で使用した だけでしたが、yieldを使い、値を保存する事ができます。 for {節} yield <本体> 一般例 具体例 生成された値 を格納していく
  16. 16. 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の組み合わせを出力してください
  17. 17. 練習問題のヒント 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となる 条件をつけて下さい
  18. 18. 練習問題の回答 for( a <- 1 to 100; b <- 1 to 100; c <- 1 to 100 if a * a == b * b + c * c ) { println((a, b, c)) } 条件を付ける
  19. 19. try式による例外処理 ・メソッドは、通常の値で結果値を返すのでは無く、例外を 投げて終了する事ができる。 ・メソッドの呼び出し元は、例外をキャッチして処理するか、 そのまま終了する事ができる。 ・例外は、キャッチして処理するメソッドが現れるまで、呼 び出しスタックを巻き戻しながら呼び出し元から呼び出し元 へと例外が伝播していく。
  20. 20. try式による例外処理 try { 何らかの処理が失敗時に例外を投げる。 又は、明示的に例外を投げる } catch { //例外を受ける所 case ex: Exception => // 例外の処理 } finally { //最後に必ず実行する処理を書く所 何らかの処理 }
  21. 21. try式による例外のスロー scala> val n = 3 n: 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 条件にマッチしたので例外投げない 条件にマッチしない ので例外を投げる 値を返す 例外を返す
  22. 22. 例外のキャッチ import 色々 try{ val f = new FileReader(“input.txt”) } catch { case ex: FileNotFoundException => {ファイル無しerror処理} case ex: IOException => {I/O error処理} } finally { file.close() } 最後に必ず呼ばれる処理を書く。 例外が呼ばれた場合でも、 必ずファイルは閉じる。 例外のキャッチと処理
  23. 23. 例外発生時の値の生成について ・例外が投げられない場合は、try節の値となる。 ・例外が投げられて、catch節で処理した場合は、catch節の値 となる。 ・例外が投げられて、catch節で処理されなかった場合は、結果 値を持たない。 ・例外が投げられて、finally節が計算した値は、finallyの値で上 書きされる。その為、finally節では、一般的にtryやcatchの計 算された値を変更してはならない。 次にfinallyの副作用
  24. 24. 直感に反する値の生成 scala> def f(): Int = try return 1 finally return 2 f: ()Int scala> f() res3: Int = 2 scala> def g(): Int = try 1 finally 2 g: ()Int scala> g() res4: Int = 1 finallyの値が返る tryの値が返る 不思議?
  25. 25. 例外の注意点1 Javaの検査例外も含めすべての例外が非検査例外の扱いになる。 JavaのAPIを呼び出す時にthrowされる例外を全て捕捉しなくて もコンパイルエラーにはなりません。 Javaとの相互運用のために@throwsアノテーションを用いると、 throwsを付与したJavaバイトコードを生成することができます。 @throws(classOf[FooException]) @throws(classOf[BarException]) Javaとの相互運用時、Javaの例外をなげる場合
  26. 26. 例外の注意点2 例外は非同期プログラミングでは使えない 例外の動作は送出されたらcatchされるまでコールスタックを遡 っていくというものです。ということは別スレッドや、別のイ ベントループなどで実行される非同期プログラミングとは相容 れないものです。特にScalaでは非同期プログラミングが多用さ れるので、例外をそのまま使えないことが多いです。
  27. 27. match式 マッチ対象の式 match { case パターン1 [if ガード1] => 式1 case パターン2 [if ガード2] => 式2 case ... case パターンN => 式N } scala> val one = 1 one: Int = 1 scala> one match { | case 1 => "one" | case _ => "other" | } res0: String = one 具体例 一般例 1にmatchしたらoneを返す。 それ以外ならotherを返す。
  28. 28. match式 ・Scalaのcaseでは、Javaのcase文とは異なり、整数型や文字 列型だけでは無く、あらゆる型を扱えます。 ・Scalaでは、breakが暗黙のうちに指定され、上の選択肢から 下の選択肢に制御が落ちる事はありません。 ・Javaのswitchとの大きな違いは、match式が結果値を生成す る事である。
  29. 29. breakとcontinueは無い var i = 0 var foundIt = false while (i < args.length && !foundIt) { // 引数の方が長く、見つかって無 い if (!args(i).startsWith(“-”)) { // 文頭が”-”ではない if (args(i).endWith(“.scala”)) // 文末が”.scala”である foundIt = true // 見つかった!! } i = i + 1 } 命令型の書き方だと
  30. 30. 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のみを使う
  31. 31. 変数のスコープ val a = 1; { println(a) } 1と表示される val a = 1; { val a = 2 println(a) } println(a) 2 1 と表示される val a = 1; { println(a) val a = 2 println(a) } error 変数のスコープは{}等で切り替わる。 ※中括弧以外で切り替わるのある?? forward reference extends over definition of value a
  32. 32. 命令形から関数型へ1 scala> 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")) zero one two 標準出力という副作用 varは控えよう!
  33. 33. 命令形から関数型へ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")) zero one two varを無くし、 whileからforへの変更 Unit型
  34. 34. 命令形から関数型へ2 返り値のデータ型が Unit型 のメソッドは、実行すると副作用 を引き起こすメソッドである。 副作用 を伴う println()メソッド の呼び出し処理を、「引数の リストから要素を取り出して、標準出力に表示させたい文字 列を返す処理」として切り離し、副作用が生じる処理領域を 見つけやすいようする。 副作用の無い関数には、単体テストが簡単になるというメリ ットもある。
  35. 35. 命令形から関数型へ2 scala> def formatArgs3(args: Array[String]) = args.mkString("¥n") formatArgs3: (args: Array[String])String scala> println(formatArgs3(Array("zero", "one", "two"))) zero one two String型 String型を受取り、 受け取り手が標準出力を実施する。
  36. 36. if,try,matchの練習問題 入力された西暦が、閏年ならば「”LeapYear”」 閏年でないなら「”NotLeapYear”」2000年なら 「”Millennium”」 を返すメソッドを作ってください。 受け取った値に応じて、「閏年です」や「閏年では無い」とい う標準出力、又は「Problem」という例外を返してください。 ★閏年の定義 ・西暦年が4で割り切れる年は閏年 ・ただし、西暦年が100で割り切れる年は平年
  37. 37. 練習問題のヒント 閏年判定部分 (year % 400 == 0) || (year % 100 != 0 && year % 4 == 0) match部分 val YearJudge = 閏年判定メソッド(2000) try{ YearJudge match{ case “LeapYear” => ・・・ } } catch { } finally {}
  38. 38. 練習問題の回答 def isLeapYear(year: Int) = { if (year == 2000) { "Millennium" } else if ((year % 400 == 0) || (year % 100 != 0 && year % 4 == 0)) { "LeapYear" } else { "NotLeapYear" } } 2枚目に続く
  39. 39. 練習問題の回答 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") }
  40. 40. まとめ ・Scalaに組み込みの制御構造は最小限のものだが、 必要な仕事を見事にこなす。 ・制御構造は対応する命令形言語の構文と良く似ているが、 ほとんどが結果値を返すので、 関数型スタイルもサポートする。 ・次章では、関数リテラルについて説明されます!!

×