Scala関数型デザイン&プログラミング 〜2章 (2)
"Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド"を買いました。 ちょっとずつ読んで、各章の内容をまとめてゆきます。
Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド (impress top gear)
- 作者: Paul Chiusano,Rúnar Bjarnason,株式会社クイープ
- 出版社/メーカー: インプレス
- 発売日: 2015/03/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (7件) を見る
今回は2章まで。ループの処理や型について。
2章
2章では、まずScalaの説明が少しあります。 その後、再帰を用いたループの書き方、末尾再帰について触れられています。 ここで実習が入ります。
フィボナッチ数列を取得する再帰関数を作れ。ただし、定義は末尾再帰関数で作ること
直前に階乗の例がありますが、フィボナッチでは直前2つを使うので、引数を更に増やす工夫が必要ですね。 下のようなプログラムを書きました。 末尾再帰なので1000でも大丈夫(Intなので値のオーバーフローはしますが)。
import scala.annotation.tailrec object Main extends App { def fib(n: Int): Int = { @tailrec def go(n: Int, pre: Int, prepre: Int): Int = if(n <= 0) prepre else go(n-1, pre + prepre, pre) go(n, 1, 0) } (0 to 1000).foreach( i => println( fib(i) )) }
次に、高階関数、多相化の説明に入ります。 仮にC++の入門書であればテンプレートは結構後に出てくるものですが、序盤で出てくるのが面白いです。 ここまでで、また練習問題が出てきます。
比較関数を渡してソートされているかを判定する関数を作れ
直前に、配列内の要素を検索する関数があるので、それと同じ要領で作ればよいです。
import scala.annotation.tailrec object Main extends App { def isSorted[A](as: Array[A], ordered: (A,A) => Boolean): Boolean = { @tailrec def loop(n: Int): Boolean = { if(n >= as.length - 1) true else if(! ordered(as(n), as(n+1))) false else loop(n+1) } loop(0) } val test = Array(1, 2, 2, 3) println(isSorted(test, (a:Int, b:Int) => a <= b)) println(isSorted(test, (a:Int, b:Int) => a < b)) }
引き続き、関数リテラル、partialを使った型による制限の例を示しています。 型で実装を決めてゆくのはパズル感があります。
ここから怒涛の練習問題。 curry, uncurry, composeの3つを作成せよ、というお題です。 型定義に従えば、自ずと実装が出てきます。
object Main extends App { // ex 1 def curry[A, B, C](f: (A, B) => C): A => (B => C) = (a: A) => (b: B) => f(a, b) def test(a: Int, b: Float): (Float, Double) = (a.toFloat, b.toDouble) val t = curry(test) val res = t(10) println(res(2.5f)) // ex 2 def uncurry[A, B, C](f: A => B => C): (A, B) => C = (a: A, b: B) => f(a)(b) val t2 = uncurry(t) println(t2(10, 2.5f)) // ex 3 def compose[A, B, C](f: B=> C, g: A=>B): A => C = (a: A) => f(g(a)) val t3 = compose( (b: Int) => b.toDouble, (a: Float) => a.toInt ) println(t3(10.5f)) }
思ったこと
汎用的な処理を作ってそれを組み合わせるという、ライブラリから作るようなイメージです。 最近は大規模なプロダクトも多く仕様変更のオーバーヘッドが大きいため、抽象的な処理を設計・記述する需要は昔より高まっているのではないかと感じます。 そのような能力を鍛える意味でも、関数型を学習する価値はあるのではないかと思いました。