Scala exercises for beginners
id:akihiro4chawon 氏の scala を左傾化させる話 - akihiro4chawonの日記 が元ネタ
Pattern match*1 で解いてみた。
Pattern match は好きなのだが RWH によると明示的な再帰より fold を使う方がわかりやすいと書いてあった。この辺りは経験を積んでから判断したい。
object Exercises { def succ(n: Int) = n + 1 def pred(n: Int) = n - 1 // Exercise 1 def add(x: Int, y: Int): Int = y match { case 0 => x case y => if (y > 0) add(succ(x), pred(y)) else add(pred(x), succ(y)) } // Exercise 2 def sum(x: List[Int]): Int = x match { case Nil => 0 case x::xs => add(x, sum(xs)) } // Exercise 3 def length[A](x: List[A]): Int = x match { case Nil => 0 case x::xs => succ(length(xs)) } // Exercise 4 def map[A, B](x: List[A], f: A => B): List[B] = x match { case Nil => Nil case x::xs => f(x)::map(xs, f) } // Exercise 5 def filter[A](x: List[A], f: A => Boolean): List[A] = x match { case Nil => Nil case x::xs => if (f(x)) x::filter(xs, f) else filter(xs, f) } // Exercise 6 def append[A](x: List[A], y: List[A]): List[A] = x match { case Nil => y case x::xs => x::append(xs, y) } // Exercise 7 def concat[A](x: List[List[A]]): List[A] = x match { case Nil => Nil case x::xs => append(x, concat(xs)) } // Exercise 8 def concatMap[A, B](x: List[A], f: A => List[B]): List[B] = concat(map(x, f)) // Exercise 9 def maximum(x: List[Int]): Int = x match { case Nil => Int.MinValue case x::Nil => x case x::xs => x max maximum(xs) } // Exercise 10 def reverse[A](x: List[A]): List[A] = { def rec(xs: List[A], ys: List[A]): List[A] = xs match { case Nil => ys case x::xs => rec(xs, x::ys) } rec(x, Nil) } } object ExercisesTest { import org.scalacheck._ import Arbitrary.arbitrary import Prop._ import Exercises._ // val prop_add = forAll((x: Int, y: Int) => add(x, y) == x + y) // val prop_sum = forAll((xs: List[Int]) => sum(xs) == xs.sum) val prop_length = forAll((xs: List[Int]) => length(xs) == xs.length) val prop_map = forAll((xs: List[Int], f: Int => String) => map(xs, f) == xs.map(f)) val prop_filter = forAll((xs: List[Int], f: Int => Boolean) => filter(xs, f) == xs.filter(f)) val prop_append = forAll((xs: List[Int], ys: List[Int]) => append(xs, ys) == xs ++ ys) val prop_concat = forAll((xs: List[List[Int]]) => concat(xs) == xs.flatten) val prop_concatMap = forAll((xs: List[Int], f: Int => List[String]) => concatMap(xs, f) == xs.flatMap(f)) val prop_maximum = forAll((xs: List[Int]) => xs.nonEmpty ==> (maximum(xs) == xs.max)) val prop_reverse = forAll((xs: List[Int]) => reverse(xs) == xs.reverse) val props = Seq( // prop_add, // prop_sum, prop_length, prop_map, prop_filter, prop_append, prop_concat, prop_concatMap, prop_maximum, prop_reverse) def main(args: Array[String]) { props foreach (_.check) } }
ScalaCheck の部分は id:akihiro4chawon 氏のものをコピーしただけ。
prop_sum を実行すると返ってこないのは再帰が深すぎるから?
QuickCheck を勉強してから調べる。
あまり意味はないかもしれないが Pattern match で Nil を最初に置くのか後に置くのかどちらのスタイルが一般的なんだろう?
RWH では where 句がない場合は後ろに置かれている気がする。
関数型言語は Ninety-Nine XXX Problems とか良質な問題が多いので楽しい。
2011-05-29 追記
『Scheme 手習い』の add の方法で
def add(x: Int, y: Int): Int = y match { case 0 => x case y => if (y > 0) succ(add(x, pred(y))) else pred(add(x, succ(y))) }
*1:http://groovy.codehaus.org/Roadmap をみると Groovy では 2.0 で導入予定