Scala関数型デザイン&プログラミング 〜2章

概要

を読んだメモをまとめてゆきます。
今回は第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))
}

思ったこと

汎用的な処理を作ってそれを組み合わせるという、ライブラリから作るようなイメージです。 最近は大規模なプロダクトも多く仕様変更のオーバーヘッドが大きいため、抽象的な処理を設計・記述する需要は昔より高まっているのではないかと感じます。
そのような能力を鍛える意味でも、関数型を学習する価値はあるのではないかと思いました。