fp in scala第三章練習問題1-10
6475 ワード
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
1
val x = List(1, 2, 3, 4, 5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + List.sum(t)
case _ => 101
}
xの値を聞くと、パターンマッチングの問題で、結果は3で、私たちは第1のcaseが第3の数が3ではなく4であるため、次の1つを歩いて、Nilではないため、更に次の1つを歩いて、結果は一致することができて、xバインド1、yバインド2、結果は3です
2
一つのListを実現するtailメソッド実装構想は,一つのNilであれば異常を投げ出し,そうでなければlistをConsで分割するとtがtailとなる.
def tail[A](list: List[A]): List[A] =
list match {
case Nil => throw new Exception("error")
case Cons(_, t) => t
}
3
setHeadメソッドを実装し,ヘッダ要素実装の考え方を置き換え,listがNilであるか否かを判断し,Nilであれば異常を投げ出し,そうでなければヘッダをxにバインドすればよい.
def setHead[A](x: A, list: List[A]): List[A] = {
list match {
case Nil => throw new Exception("error")
case Cons(_, xs) => Cons(x, xs)
}
4
dropを実装し、指定された要素を除去する実装構想:除去要素の個数が0以下の場合、list自体を返し、そうでない場合、listが空の場合、空を返し、Consの場合、再帰的に除去し、呼び出し自体を呼び出し、末尾をlistとし、除去個数は最初の要素が除去されたため、n-1
@tailrec
def drop[A](list: List[A], n: Int): List[A] = {
if (n <= 0) list
else list match {
case Nil => Nil
case Cons(_, xs) => drop(xs, n - 1)
}
}
5
dropWhileを実装し、条件に合致する要素を除去し、最初の条件に合致しない実装構想がlistがConsであれば、最初の要素が除去できるかどうかを判断し、できればtailは除去方法を再帰的に呼び出し、そうでなければlistに直接戻る
@tailrec
def dropWhile[A](l: List[A], f: A => Boolean): List[A] = {
l match {
//case Cons(x, xs) => if (f(x)) dropWhile(xs, f) else l
case Cons(x, xs) if f(x) => dropWhile(xs, f)
case _ => l
}
}
6
実装initは,最後の要素を除いた他の要素実装構想を返し,listがNilであれば異常を投げ出し,Cons(x,Nil)であればNilを返し,Cons(x,xs)であれば新しいConsのheadがx,末尾がinit(xs)である.
def init[A](l: List[A]): List[A] = l match {
case Nil => throw new Exception("empty")
case Cons(x, Nil) => Nil
case Cons(x, xs) => Cons(x, init(xs))
}
8
ひとつのfoldRightを実現するのは実は1つの初期値で、1つの操作関数で、それからListの各要素に対して順次関数を呼び出して構想を実現します:もしListがNilであれば、初期値zは最終結果として、さもなくば、f関数を呼び出して、fのタイプの署名を見てみましょう、2つのパラメータを受け取って、それぞれAとBのタイプで、最終結果はBのタイプで、ここで我々Aタイプは自然にxであり,Bタイプは再帰呼び出しxsの戻り値タイプがBであることを知ることができるので,最終的な書き方はf(x,foldRight(xs,z)(f))である.
def foldRight[A, B](as: List[A], z: B)(f: (A, B) => B): B = as match {
case Nil => z
case Cons(x, xs) => f(x, foldRight(xs, z)(f))
}
9
これを実現するにはfoldRightの考え方を用い,初期値は1,foldRightに伝達されるf関数は(xs,acc)=>acc+1,すなわち再帰時に元の結果に+1の要素があり,foldRightのタイプ署名がAタイプのasとIntタイプのaccであるのに,なぜaccがIntなのかを見てみよう.彼のタイプは(as,1)の時に確定され,1と同じタイプである.
def length[A](as: List[A]): Int = foldRight(as, 1)((xs, acc) => acc + 1)
10
実現foldLeftの実現構想はfoldRightと類似しており、関数fが変化している.ここではBとAをBに変換し、すなわち左から右へ、右から左への区別である.したがって、Cons(x,xs)については、テール再帰が可能であり、fはzをBタイプとして伝達し、Consの最初の要素に伝達し、Bタイプを得、テールとfoldLeftを行うことができる.
@tailrec
def foldLeft[A, B](as: List[A], z: B)(f: (B, A) => B): B = as match {
case Nil => z
case Cons(x, xs) => foldLeft(xs, f(z, x))(f)
}