モノイド、モナドの章は、抽象的な内容で難しいといえば難しいのだが、試行錯誤の文章が少ないので、パートⅡの章に比べれば読みやすい。途中の問題への取り組みはあちこち省き、読み進んだ。
結合律の説明がすぐには理解できなかったので、丁寧にたどってみた。
x.flatMap(f).flatMap(g)==x.flatMap(a=>f(a).flatMap(g))の結合律で、xをSome(v)に置き換えると
Some(v).flatMap(f).flatMap(g) == Some(v).flatMap(a=>f(a).flatMap(g))であることを示す。
def flatMap[B](f:A=>Option[B]):Option[B]だから、
左辺のSome(v).flatMap(f)の部分はSome(v)のvに関数fを適用してf(v):Option[B]となる。
右辺のほうは、Some(v)のvに関数a=>f(a).flatMap(g)を適用するので、
v=>f(v).flatMap(g)で、結局f(v).flatMap(g)
Exercise11.7 クライスリ関数composeを実装せよ
def compose[A,B,C](f: A => F[B], g: B => F[C]): A => F[C] =
a => flatMap(f(a))(g)
f: A => F[B]の部分は、a=>f(a)で、次にg: B => F[C]のところは、flatMapを使うしかない。
( def flatMap(g:B=>F[C]):F[C]とみなして )
f(a).flatMap(g)とも書けるが、flatMap(f(a))(g)ということ。
Exercise 11.9 flatMapの観点からの結合律と、composeの観点からの結合律の式が等価であることを証明せよ。
compose(compose(f, g), h) == compose(f, compose(g, h))
a => flatMap(compose(f, g)(a))(h) == a => flatMap(f(a))(compose(g, h))
a => flatMap((b => flatMap(f(b))(g))(a))(h) == a => flatMap(f(a))(b => flatMap(g(b))(h))
左辺を少し簡単にすると
( b => flatMap(f(b))(g) )(a)は b => flatMap(f(b))(g)にaを適用するということ
だから a=> flatMap(f(a))(g) つまり下記のようになる
a => flatMap(flatMap(f(a))(g))(h) == a => flatMap(f(a))(b => flatMap(g(b))(h))
aを除いて簡単にする。f(a)の代わりにxとすると
flatMap(flatMap(x)(g))(h) == flatMap(x)(b => flatMap(g(b))(h))
文字を置き換えると
flatMap(flatMap(x)(f))(g) == flatMap(x)(a => flatMap(f(a))(g))
ん~ けっこう、ややこしい。
2015年6月30日火曜日
2015年6月27日土曜日
scala関数型デザイン&プログラミング P195
Exercise 9.7 flatMapをベースとしてproductとmap2を実装せよ
def product[A,B](p: Parser[A], p2: => Parser[B]): Parser[(A,B)] =
flatMap(p)(a => map(p2)(b => (a,b)))
def map2[A,B,C](p: Parser[A], p2: => Parser[B])(f: (A,B) => C): Parser[C] =
for { a <- p; b <- p2 } yield f(a,b)
for内包表記は、もう忘れていた
P74を参考にすれば
p flatMap ( a =>
p2 map ( b =>
f(a,b))) という意味になる
Exercise 9.8 mapをflatMapや他のコンビネータをベースにして表現せよ
def map[A,B](p: Parser[A])(f: A => B): Parser[B] =
p.flatMap(a => succeed(f(a)))
def product[A,B](p: Parser[A], p2: => Parser[B]): Parser[(A,B)] =
flatMap(p)(a => map(p2)(b => (a,b)))
def map2[A,B,C](p: Parser[A], p2: => Parser[B])(f: (A,B) => C): Parser[C] =
for { a <- p; b <- p2 } yield f(a,b)
for内包表記は、もう忘れていた
P74を参考にすれば
p flatMap ( a =>
p2 map ( b =>
f(a,b))) という意味になる
Exercise 9.8 mapをflatMapや他のコンビネータをベースにして表現せよ
def map[A,B](p: Parser[A])(f: A => B): Parser[B] =
p.flatMap(a => succeed(f(a)))
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で表せるのだろうが、いまいち勉強不足
温度変化グラフをVb.net2010で表示
以前は、PICを使って温度変化を表示していたが、最近はarduinoという便利な基板があるので、そちらを使ってみた。センサーはLM35。
http://projectsbiotope.blogspot.jp/2010/01/arduino_25.htmlにつなぎ方が出ている。
http://kana-soft.com/tech/sample_0008.htmでシリアル通信ソフトを用意して、グラフ表示
できるようにしてみました。
ソース付き実行ファイルはこちら
センサー部分は、空気中でないとうまく動作しないようです。液体等の温度変化見るには、エポキシ樹脂等で固める必要あるようです。
http://projectsbiotope.blogspot.jp/2010/01/arduino_25.htmlにつなぎ方が出ている。
http://kana-soft.com/tech/sample_0008.htmでシリアル通信ソフトを用意して、グラフ表示
できるようにしてみました。
ソース付き実行ファイルはこちら
センサー部分は、空気中でないとうまく動作しないようです。液体等の温度変化見るには、エポキシ樹脂等で固める必要あるようです。
2015年6月20日土曜日
scala関数型デザイン&プログラミング P170
Exercise8.13 空でないリストを生成するlistOf1を定義し、このジェネレータを利用するようにmaxの仕様を更新せよ
def listOf1[A](g: Gen[A]): SGen[List[A]] =
SGen(n => g.listOfN(n max 1))
最低値が0にならないようにしている?
val maxProp1 = forAll(listOf1(smallInt)) { l =>
val max = l.max
!l.exists(_ > max) // lの中にmaxより大きい値はないはず
}
Exercise8.14 List[Int]のソートなどに利用できるList.sortedの振る舞いを検証するためのプロパティを記述せよ。
val sortedProp = forAll(listOf(smallInt)) { ns =>
val nss = ns.sorted
// どのソート済みのリストも、空か、一つの要素かで、 aがbより大きいような連続(a,b)要素ではない。
(ns.isEmpty || nss.tail.isEmpty || !ns.zip(ns.tail).exists {
case (a,b) => a > b
})
// また、ソート済みリストは入力リストの要素をすべて持っている
&& !ns.exists(!nss.contains(_))
// また、入力リストにない要素はない。
&& !nss.exists(!ns.contains(_))
}
!ns.zip(ns.tail).exists { case (a,b) => a > b } これの意味は? List(1,2,3).zip(List(2,3)はList((1,2),(2,3))つまり、リストの中から連続した2つの要素のペアを取り出すことになる。そこに、逆順の要素があればTrue、!により、false
でも、nsは、ソート済みでないから、falseがあり得るのでないか? nsがnssなら分かるが
contains(x)はxが要素に含まれているかどうかを調べる.
!ns.exists(!nss.contains(_))は、!ns.exists(_=>!nss.contains(_))ということで、入力リストの各要素に対して、ソート済みリストに存在しないものがあるかチェックしている。もちろんないので、falseで!により、Trueになる。ということ?
この章のこの後ののExerciseは、難易度が高く、パス
def listOf1[A](g: Gen[A]): SGen[List[A]] =
SGen(n => g.listOfN(n max 1))
最低値が0にならないようにしている?
val maxProp1 = forAll(listOf1(smallInt)) { l =>
val max = l.max
!l.exists(_ > max) // lの中にmaxより大きい値はないはず
}
Exercise8.14 List[Int]のソートなどに利用できるList.sortedの振る舞いを検証するためのプロパティを記述せよ。
val sortedProp = forAll(listOf(smallInt)) { ns =>
val nss = ns.sorted
// どのソート済みのリストも、空か、一つの要素かで、 aがbより大きいような連続(a,b)要素ではない。
(ns.isEmpty || nss.tail.isEmpty || !ns.zip(ns.tail).exists {
case (a,b) => a > b
})
// また、ソート済みリストは入力リストの要素をすべて持っている
&& !ns.exists(!nss.contains(_))
// また、入力リストにない要素はない。
&& !nss.exists(!ns.contains(_))
}
!ns.zip(ns.tail).exists { case (a,b) => a > b } これの意味は? List(1,2,3).zip(List(2,3)はList((1,2),(2,3))つまり、リストの中から連続した2つの要素のペアを取り出すことになる。そこに、逆順の要素があればTrue、!により、false
でも、nsは、ソート済みでないから、falseがあり得るのでないか? nsがnssなら分かるが
contains(x)はxが要素に含まれているかどうかを調べる.
!ns.exists(!nss.contains(_))は、!ns.exists(_=>!nss.contains(_))ということで、入力リストの各要素に対して、ソート済みリストに存在しないものがあるかチェックしている。もちろんないので、falseで!により、Trueになる。ということ?
この章のこの後ののExerciseは、難易度が高く、パス
scala関数型デザイン&プログラミング P167
Exercise8.10 GenをSGenに変換するヘルパー関数を実装せよ。これはGenのメソッドとして追加できる。 unsized(すべてのサイズに合うという意味)
def unsized: SGen[A] = SGen(_ => this)
なぜ、Intの部分が_でいいのか?
Exercise8.11 当然のことながら、SGenは少なくともGenと同じ演算の多くをサポートし、その実装はかなり機械的である。Genの対応する関数にデリゲートするするだけの便利な関数をSGenで定義せよ。
case class Gen[+A](sample: State[RNG,A]) {
def map[B](f: A => B): Gen[B] =
Gen(sample.map(f))
def flatMap[B](f: A => Gen[B]): Gen[B] =
Gen(sample.flatMap(a => f(a).sample))
def **[B](g: Gen[B]): Gen[(A,B)] =
(this map2 g)((_,_))
}
が、すでにGenの中にあるとして?、のようだ。
case class SGen[+A](g: Int => Gen[A]) {
def apply(n: Int): Gen[A] = g(n)
def map[B](f: A => B): SGen[B] =
SGen(g andThen (_ map f))
def flatMap[B](f: A => Gen[B]): SGen[B] =
SGen(g andThen (_ flatMap f))
def **[B](s2: SGen[B]): SGen[(A,B)] =
SGen(n => apply(n) ** s2(n))
}
andThenは関数の合成 (_ map f)等は _ =>_ map f ということ?
Genの中では、**は、map2をつかって、AとBを結合して(A,B)にするということのようだ。
これを使って、SGenの中では、 apply(n)がGen[A]で、s2(n)がGen[B]となり、apply(n)**s2(n)が、Gen[(A,B)]となるということ?
Exercise8.12 明示的なサイズを受け取らないListOfコンビネータを実装せよ。この実装は、GenでなくSGenを返し、要求されたサイズのリストを生成する。
def listOf[A](g:Gen[A]):SGen[List[A]] =
SGen(n => g.listOfN(n))
SGenのイメージがいまいちつかめない
def unsized: SGen[A] = SGen(_ => this)
なぜ、Intの部分が_でいいのか?
Exercise8.11 当然のことながら、SGenは少なくともGenと同じ演算の多くをサポートし、その実装はかなり機械的である。Genの対応する関数にデリゲートするするだけの便利な関数をSGenで定義せよ。
case class Gen[+A](sample: State[RNG,A]) {
def map[B](f: A => B): Gen[B] =
Gen(sample.map(f))
def flatMap[B](f: A => Gen[B]): Gen[B] =
Gen(sample.flatMap(a => f(a).sample))
def **[B](g: Gen[B]): Gen[(A,B)] =
(this map2 g)((_,_))
}
が、すでにGenの中にあるとして?、のようだ。
case class SGen[+A](g: Int => Gen[A]) {
def apply(n: Int): Gen[A] = g(n)
def map[B](f: A => B): SGen[B] =
SGen(g andThen (_ map f))
def flatMap[B](f: A => Gen[B]): SGen[B] =
SGen(g andThen (_ flatMap f))
def **[B](s2: SGen[B]): SGen[(A,B)] =
SGen(n => apply(n) ** s2(n))
}
andThenは関数の合成 (_ map f)等は _ =>_ map f ということ?
Genの中では、**は、map2をつかって、AとBを結合して(A,B)にするということのようだ。
これを使って、SGenの中では、 apply(n)がGen[A]で、s2(n)がGen[B]となり、apply(n)**s2(n)が、Gen[(A,B)]となるということ?
Exercise8.12 明示的なサイズを受け取らないListOfコンビネータを実装せよ。この実装は、GenでなくSGenを返し、要求されたサイズのリストを生成する。
def listOf[A](g:Gen[A]):SGen[List[A]] =
SGen(n => g.listOfN(n))
SGenのイメージがいまいちつかめない
2015年6月18日木曜日
scala関数型デザイン&プログラミング P168
type MaxSize = Int
case class Prop(run: (MaxSize,TestCases,RNG) => Result)
def forAll[A](g: SGen[A])(f: A => Boolean): Prop =
forAll(g(_))(f)
def forAll[A](g: Int => Gen[A])(f: A => Boolean): Prop = Prop {
(max,n,rng) =>
val casesPerSize = (n - 1) / max + 1
val props: Stream[Prop] =
Stream.from(0).take((n min max) + 1).map(i => forAll(g(i))(f))
val prop: Prop =
props.map(p => Prop { (max, n, rng) =>
p.run(max, casesPerSize, rng)
}).toList.reduce(_ && _)
prop.run(max,n,rng)
}
n min maxはnとmaxの小さいほうを返す
Stream.from(0).take((n min max) + 1).map(i => forAll(g(i))(f))はnかmaxの小さいほうの数分のStream(中身はiをもとにしてつくったforAll(g(i))(f)):Propを生成する。これをpopsとしている。
props:Stream[Prop]からmapで props.map(p => Prop { (max, n, rng) =>p.run(max, casesPerSize, rng) })をつくる。これは、(max,n,rng)=>p.run(max, casesPerSize, rng) より最終的にp.run(max, casesPerSize, rng)に変換されるということか。
toList.reduce(_&&_)で、StreamをListにして、要素を&&でつなぐということのようだ。
こういったテストの経験もないこともあり、いまいちすっきり理解できない。
case class Prop(run: (MaxSize,TestCases,RNG) => Result)
def forAll[A](g: SGen[A])(f: A => Boolean): Prop =
forAll(g(_))(f)
def forAll[A](g: Int => Gen[A])(f: A => Boolean): Prop = Prop {
(max,n,rng) =>
val casesPerSize = (n - 1) / max + 1
val props: Stream[Prop] =
Stream.from(0).take((n min max) + 1).map(i => forAll(g(i))(f))
val prop: Prop =
props.map(p => Prop { (max, n, rng) =>
p.run(max, casesPerSize, rng)
}).toList.reduce(_ && _)
prop.run(max,n,rng)
}
n min maxはnとmaxの小さいほうを返す
Stream.from(0).take((n min max) + 1).map(i => forAll(g(i))(f))はnかmaxの小さいほうの数分のStream(中身はiをもとにしてつくったforAll(g(i))(f)):Propを生成する。これをpopsとしている。
props:Stream[Prop]からmapで props.map(p => Prop { (max, n, rng) =>p.run(max, casesPerSize, rng) })をつくる。これは、(max,n,rng)=>p.run(max, casesPerSize, rng) より最終的にp.run(max, casesPerSize, rng)に変換されるということか。
toList.reduce(_&&_)で、StreamをListにして、要素を&&でつなぐということのようだ。
こういったテストの経験もないこともあり、いまいちすっきり理解できない。
登録:
コメント (Atom)