2015年6月27日土曜日

scala関数型デザイン&プログラミング P192-


●Exercise9.1 productを使ってコンビネータmap2を実装し、これを使って、manyをベースとしてmany1を実装せよ。
def map2[A,B,C](p: Parser[A], p2: Parser[B])( f: (A,B) => C): Parser[C] =
  map(product(p, p2))(f.tupled)
 tupledは、2引数関数fをタプルを引数とする関数に変換する。productの返値がParser[(A,B)]で中身がタプルだから、mapが適用できるということらしい。
def many1[A](p: Parser[A]): Parser[List[A]] =
  map2(p, many(p))(_ :: _)

●Exercise9.2 productの振る舞いを定義する法則を考え出せ
解答を訳してみたが、内容は難しい。
`product` は結合則をみたす。次の2つの式は等しい:
  (a ** b) ** c
  a ** (b ** c)
違うのは、ペアの入れ子の状態である。 `(a ** b) ** c` パーサーは `((A,B), C)`をかえす。( `a ** (b ** c)` がn `(A, (B,C))`をかえすのと違い)
.入れ子のタプルをフラットな3タプルに変換する関数 `unbiasL` と `unbiasR` を定義できる。
  def unbiasL[A,B,C](p: ((A,B), C)): (A,B,C) = (p._1._1, p._1._2, p._2)
  def unbiasR[A,B,C](p: (A, (B,C))): (A,B,C) = (p._1, p._2._1, p._2._2)
これらの関数により、結合則がいえる。
  (a ** b) ** c map (unbiasL) == a ** (b ** c) map (unbiasR)
2つの関係の間に、自明な全単射がある場合に、~=を使うことがある。
  (a ** b) ** c ~= a ** (b ** c)
`map` や `product`も興味深いリレーションシップをもつ。 2つのパーサーのプロダクトを実行の前でも後でもMapが可能である。(そのふるまいに影響与えることなく)
  a.map(f) ** b.map(g) == (a ** b) map { case (a,b) => (f(a), g(b)) }
たとえば,もし`a` と `b` が両方とも `Parser[String]`で  `f` と `g` のどちらも文字の長さを計算されたものだとするなら、 aの結果をその長さにmapしても、あるいは、プロダクトの後にやっても、どちらでも問題ない。12章でこの法則をもう少し検討する

●Exercise9.3 先へ進む前にor、map2、succeedをベースとしてmanyを定義できるか検討せよ
def many[A](p: Parser[A]): Parser[List[A]] =
  map2(p, many(p))(_ :: _) or succeed(List())
manyを再帰的に使っている。失敗したら空のリストを返す

●Exercise9.4 map2とsucceedを使って先のlistOfNコンビネータを実装せよ
def listOfN[A](n: Int, p: Parser[A]): Parser[List[A]] =
  if (n <= 0) succeed(List())
  else map2(p, listOfN(n-1, p))(_ :: _)

●Exercise9.5 第7章で示したように、非正格性に別のコンビネータで対処することもできる。ここでもそれを試して、既存のコンビネータに必要な変更を追加せよ。このアプローチをここで試すことについてどう思うか。

`wrap`というコンビネータを紹介する
  def wrap[A](p: => Parser[A]): Parser[A]

manyの定義は
  def many[A](p: Parser[A]): Parser[List[A]] =
    map2(p, wrap(many(p)))(_ :: _) or succeed(List())
英文の説明がいまいちよくわからない
In the parallelism chapter, we were particularly interested in avoiding having `Par` objects that took as much time and space to build as the corresponding serial computation, and the `delay` combinator let us control this more carefully. Here, this isn't as much of a concern, and having to think carefully each time we `map2` to decide whether we need to call `wrap` seems like unnecessary friction for users of the API.


def flatMap[A,B](p:Parser[A])(f:A=.Parser[B]):Parser[B]
●Exercise9.6 flatMapと他のコンビネータをつかって、文脈依存パーサーを記述せよ
implicit def regex(r:Regex):Parser[String]

for {
  digit <- "[0-9]+".r
  val n = digit.toInt
  _ <- listOfN(n, char('a'))
} yield n
これも、flatMapで表せるのだろうが、いまいち勉強不足

0 件のコメント:

コメントを投稿