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.

再帰で脱Javaライク

2,189 views

Published on

「実戦での Scala 〜 6つの事例から知る Scala の勘所〜」でLTしました。

Published in: Engineering
  • Be the first to comment

再帰で脱Javaライク

  1. 1. Copyright © 2014 TIS Inc. All rights reserved. 再帰で   脱Javaライク 2015.2.21 TIS株式会社   前出祐吾
  2. 2. Copyright © 2014 TIS Inc. All rights reserved. 2 自己紹介 TIS株式会社 戦略技術センター! 社内向けエンジニア基盤の整備
       ▶ Scalaの活用検証! 甲賀忍者! 東京デビュー(先月)! @yugolf
  3. 3. Copyright © 2014 TIS Inc. All rights reserved. 3 Scalaってどうなの? コード量が半分くらいに!   ! とはいえ、Scalaって難しいんじゃないの?   ! 大丈夫、Javaのライブラリ呼べるし、Javaライクにも書ける し。   (その場合、コード量は半分にならないけど。。)   ! ん!?
  4. 4. Copyright © 2014 TIS Inc. All rights reserved. 4 脱Javaライクしてファ ンクショナルなプロ グラマーになるんです。  
  5. 5. Copyright © 2014 TIS Inc. All rights reserved. 5 Javaライクってなんですの? 例:総和 1からnまでを全部足す。   ! (めいっ   ぱい、   Javaっぽ   く足す)   def  sowa(value:  Int):  Int  ={      var  result  =  0;      for(n  <-­‐  1  to  value)  {          result  =  result  +  n;      }      return  result;   }         ! sowa(10)   //>  res4:  Int  =  55
  6. 6. Copyright © 2014 TIS Inc. All rights reserved. 6 脱Javaライク varはやめよう。   forループもやめよう。   再帰にしよう。 ①終了条件 ②自分呼出 ③計算
  7. 7. Copyright © 2014 TIS Inc. All rights reserved. 7 再帰にしよう:ステップ① def  sowa(value:  Int)  :Int  =      if(value  ==  0)  value      else  value  +  sowa(value  -­‐  1) ①終了条件 ②自分呼出 ③計算 計算対象の数値が0に なったとき
  8. 8. Copyright © 2014 TIS Inc. All rights reserved. 8 再帰にしよう:ステップ② def  sowa(value:  Int)  :Int  =      if(value  ==  0)  value      else  value  +  sowa(value  -­‐  1) ①終了条件 ②自分呼出 ③計算 自分()
  9. 9. Copyright © 2014 TIS Inc. All rights reserved. 9 再帰にしよう:ステップ③ def  sowa(value:  Int)  :Int  =      if(value  ==  0)  value      else  value  +  sowa(value  -­‐  1) ①終了条件 ②自分呼出 ③計算 対象値 + 自分()
  10. 10. Copyright © 2014 TIS Inc. All rights reserved. 10 再帰にしよう:実行1 def  sowa(value:  Int)  :Int  =      if(value  ==  0)  value      else  value  +  sowa(value  -­‐  1) ①終了条件 ②自分呼出 ③計算 sowa(10)                           //>  res6:  Int  =  55  
  11. 11. Copyright © 2014 TIS Inc. All rights reserved. 11 再帰にしよう:実行2 def  sowa(value:  Int)  :Int  =      if(value  ==  0)  value      else  value  +  sowa(value  -­‐  1) sowa(100000)   //>  java.lang.StackOverflowError   //|     at  Demo$$anonfun$main$1.sowa$1(Demo.scala:64)   //|     at  Demo$$anonfun$main$1.sowa$1(Demo.scala:64)   //|     at  Demo$$anonfun$main$1.sowa$1(Demo.scala:64) ①終了条件 ②自分呼出 ③計算
  12. 12. Copyright © 2014 TIS Inc. All rights reserved. 12 スタックオーバーフロー!? スタックのサイズを大きくする? (-Xss) !  スタックの消費量を減らそう!
  13. 13. Copyright © 2014 TIS Inc. All rights reserved. 13 スタックオーバーフロー対策 末尾再帰で最適化 ! ! ! ! 自分を呼び出したあとに計算(足し算)するのではなく、 計算したものを引数として、その結果を蓄積する。 ! 戻ってきたあとに計算がなく、呼出し元に戻るだけ。 def  sowa(value:  Int)  :Int  =      if(value  ==  0)  value      else  value  +  sowa(value  -­‐  1)
  14. 14. Copyright © 2014 TIS Inc. All rights reserved. 14 末尾再帰 アキュムレータ (accumulator) 自分を呼び出したあとに計算(足し算)するのではなく、 計算したものを引数として、その結果を蓄積する。 +  3  = +  2  = +  1  =
  15. 15. Copyright © 2014 TIS Inc. All rights reserved. 15 末尾再帰 アキュムレータ (accumulator) 自分を呼び出したあとに計算(足し算)するのではなく、 計算したものを引数として、その結果を蓄積する。 ①終了条件 ②自分呼出 ③アキュムレータ      による計算
  16. 16. Copyright © 2014 TIS Inc. All rights reserved. 16 末尾再帰にしよう:ステップ① def sowa(value: Int, acc: Int = 0):Int = if(value == 0) acc else sowa(value - 1, value + acc) 計算対象の数値が0に なったとき ①終了条件 ②自分呼出 ③アキュムレータ      による計算
  17. 17. Copyright © 2014 TIS Inc. All rights reserved. 17 末尾再帰にしよう:ステップ② def sowa(value: Int, acc: Int = 0):Int = if(value == 0) acc else sowa(value - 1, value + acc) 自分() ①終了条件 ②自分呼出 ③アキュムレータ      による計算
  18. 18. Copyright © 2014 TIS Inc. All rights reserved. 18 末尾再帰にしよう:ステップ③ def sowa(value: Int, acc: Int = 0):Int = if(value == 0) acc else sowa(value - 1, value + acc) 対象値 + アキュム         レータ ①終了条件 ②自分呼出 ③アキュムレータ      による計算
  19. 19. Copyright © 2014 TIS Inc. All rights reserved. 19 末尾再帰にしよう:実行 def sowa(value: Int, acc: Int = 0):Int = if(value == 0) acc else sowa(value - 1, value + acc) sowa(100000)   //>  res4:  Long  =  705082704 ①終了条件 ②自分呼出 ③アキュムレータ      による計算
  20. 20. Copyright © 2014 TIS Inc. All rights reserved. 20 逆アセンブル public  final  int  sowa(int);    Code:          0:  iload_1          1:  iconst_0          2:  if_icmpne          9          5:  iload_1          6:  goto                    18          9:  iload_1        10:  aload_0        11:  iload_1        12:  iconst_1        13:  isub        14:  invokevirtual  #101                //  Method  sowa2:(I)I        17:  iadd        18:  ireturn public  final  int  sowa(int,  int);    Code:          0:  iload_1          1:  iconst_0          2:  if_icmpne          7          5:  iload_2          6:  ireturn          7:  iload_1          8:  iconst_1          9:  isub        10:  iload_1        11:  iload_2        12:  iadd        13:  istore_2        14:  istore_1        15:  goto                    0 ただの再帰 末尾再帰
  21. 21. Copyright © 2014 TIS Inc. All rights reserved. 21 逆コンパイル public final int sowa(int value) { return value != 0 ? value + sowa(value - 1) : value; } def  sowa(value:  Int)  :Int  =      if(value  ==  0)  value      else  value  +  sowa(value  -­‐  1) ただの再帰
  22. 22. Copyright © 2014 TIS Inc. All rights reserved. 22 逆コンパイル public final int sowa(int value, int acc) { do { if (value == 0) return acc; acc = value + acc; value = value - 1; } while (true); } def  sowa(value:  Int,  acc:  Int  =  0):Int  =      if(value  ==  0)  acc      else  sowa(value  -­‐  1,  value  +  acc) 最適 化!! 末尾再帰
  23. 23. Copyright © 2014 TIS Inc. All rights reserved. 23 tailrecアノテーション 末尾再帰になっていないメソッドの検出 import  scala.annotation.tailrec   @tailrec   final  def  sowa(value:  Int):  Int  =      if  (value  ==  0)  value      else  value  +  sowa(value  -­‐  1) could  not  optimize  @tailrec  annotated  method  sowa:   it  contains  a  recursive  call  not  in  tail  position
  24. 24. Copyright © 2014 TIS Inc. All rights reserved. 24 上書き禁止 @tailrec   def  sowa(value:  Int,  acc:  Int  =  0):  Int  =      if  (value  ==  0)  acc      else  sowa(value  -­‐  1,  value  +  acc)  @tailrec      final  def  sowa(value:  Int,… could  not  optimize  @tailrec  annotated  method   sowa:  it  is  neither  private  nor  final  so  can  be   overridden overrideしてメソッドを変更出来ないようにfinalにする privateにするか   ローカルメソッドにしてもOK
  25. 25. Copyright © 2014 TIS Inc. All rights reserved. 25 総和 def  sowa(value:  Int)  :Int  =      if(value  ==  0)  value      else  value  +  sowa(value  -­‐  1) ノーアキュム @tailrec final  def  sowa(value:  Int,  acc:  Int  =  0):  Int  =      if  (value  ==  0)  acc      else  sowa(value  -­‐  1,  value  +  acc) アキュム
  26. 26. Copyright © 2014 TIS Inc. All rights reserved. 26 !(階乗) def  factorial(value:  Int):  Int  =      if  (value  ==  0)  1      else  value  *  factorial(value  -­‐  1) @tailrec final def factorial(value: Int, acc: Long): Long = if (value == 0) acc else factorial(value - 1, value * acc) ノーアキュム アキュム
  27. 27. Copyright © 2014 TIS Inc. All rights reserved. 27 (おまけ)関数(計算式)を引数に def calculate[A](value: Int, acc: A, func: (Int, A) => A): A = { def calculate(value: Int, acc: A): A = if (value == 0) acc else calculate(value - 1, func(value, acc)) calculate(value, acc) } ! def factorial(value: Int) = calculate(value, 1L, (v: Int, acc: Long) => v * acc) ! def sowa(value: Int) = calculate(value, 0, (v: Int, acc: Int) => v + acc) 計算(たすか  かけるか) 型(Int  か  Longか) 階乗 総和
  28. 28. Copyright © 2014 TIS Inc. All rights reserved. 28 まとめ:脱Javaライクの第一歩 再帰で関数型プログラミング   アキュムレータで末尾再帰   @tailrecで末尾かチェック   ! 注)   間接的な再帰は最適化されません。
  29. 29. Copyright © 2014 TIS Inc. All rights reserved. 29 よくわからなかった、という方は   スライドの最初に再帰してくださ い。(コップ本より) まとめ:脱Javaライクの第一歩
  30. 30. THANK YOU

×