モノイド、モナドの章は、抽象的な内容で難しいといえば難しいのだが、試行錯誤の文章が少ないので、パートⅡの章に比べれば読みやすい。途中の問題への取り組みはあちこち省き、読み進んだ。
結合律の説明がすぐには理解できなかったので、丁寧にたどってみた。
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にして、要素を&&でつなぐということのようだ。
こういったテストの経験もないこともあり、いまいちすっきり理解できない。
scala関数型デザイン&プログラミング p165
リスト8-3を少し確認してみた。
def randomStream[A](g: Gen[A])(rng: RNG): Stream[A] =
Stream.unfold(rng)(rng => Some(g.sample.run(rng)))
def forAll[A](as: Gen[A])(f: A => Boolean): Prop = Prop {
(n,rng) => randomStream(as)(rng).zip(Stream.from(0)).take(n).map {
case (a, i) => try {
if (f(a)) Passed else Falsified(a.toString, i)
} catch { case e: Exception => Falsified(buildMsg(a, e), i) }
}.find(_.isFalsified).getOrElse(Passed)
}
randomStream(as)(rng).zip(Stream.from(0)).take(n)の部分で、(a,i)のストリームをn個 生成
するようだ。
その後、PassedかFalsifiedにmapするということか。
.find(_.isFalsified).getOrElse(Passed)では、最初に失敗にぶつかるとそこでストップ?
def buildMsg[A](s: A, e: Exception): String =
s"test case: $s\n" +
s"generated an exception: ${e.getMessage}\n" +
s"stack trace:\n ${e.getStackTrace.mkString("\n")}"
def randomStream[A](g: Gen[A])(rng: RNG): Stream[A] =
Stream.unfold(rng)(rng => Some(g.sample.run(rng)))
def forAll[A](as: Gen[A])(f: A => Boolean): Prop = Prop {
(n,rng) => randomStream(as)(rng).zip(Stream.from(0)).take(n).map {
case (a, i) => try {
if (f(a)) Passed else Falsified(a.toString, i)
} catch { case e: Exception => Falsified(buildMsg(a, e), i) }
}.find(_.isFalsified).getOrElse(Passed)
}
randomStream(as)(rng).zip(Stream.from(0)).take(n)の部分で、(a,i)のストリームをn個 生成
するようだ。
その後、PassedかFalsifiedにmapするということか。
.find(_.isFalsified).getOrElse(Passed)では、最初に失敗にぶつかるとそこでストップ?
def buildMsg[A](s: A, e: Exception): String =
s"test case: $s\n" +
s"generated an exception: ${e.getMessage}\n" +
s"stack trace:\n ${e.getStackTrace.mkString("\n")}"
2015年6月14日日曜日
scala関数型デザイン&プログラミング P162
値を生成した後、その値にもとづいて次に使用するジェネレータを決定する、すなわち
ジェネレータを別のジェネレータに依存させるためにはflatMapが必要ということのようだ。
Exercise6.6 flatMapを実装し、それを使ってもう少し動的なlistOfNを実装せよ。Genクラスに配置。
def flatMap[B](f: A => Gen[B]): Gen[B] =
Gen(sample.flatMap(a => f(a).sample))
ここでsample.flatMapのsampleはState[RNG,A]で、f(a):Gen[B]だからf(a).sampleはState[RNG,B]
ということか?とすればGenのカッコの中はState[RNG,B]だからGen[B]が返されている。
def listOfN(size: Int): Gen[List[A]] =
Gen.listOfN(size, this)
これを使うと、下のようになる?
def listOfN(size: Gen[Int]): Gen[List[A]] =
size flatMap (n => this.listOfN(n))
わかったような、わからないような。。難しい
ジェネレータを別のジェネレータに依存させるためにはflatMapが必要ということのようだ。
Exercise6.6 flatMapを実装し、それを使ってもう少し動的なlistOfNを実装せよ。Genクラスに配置。
def flatMap[B](f: A => Gen[B]): Gen[B] =
Gen(sample.flatMap(a => f(a).sample))
ここでsample.flatMapのsampleはState[RNG,A]で、f(a):Gen[B]だからf(a).sampleはState[RNG,B]
ということか?とすればGenのカッコの中はState[RNG,B]だからGen[B]が返されている。
def listOfN(size: Int): Gen[List[A]] =
Gen.listOfN(size, this)
これを使うと、下のようになる?
def listOfN(size: Gen[Int]): Gen[List[A]] =
size flatMap (n => this.listOfN(n))
わかったような、わからないような。。難しい
scala関数型デザイン&プログラミング P161
case class Gen[A](sample:State[RNG,A])
Exercise8.4 Genの上記の表現を使って、startからstopExclusiveの範囲内の整数を生成するchooseを実装せよ
def choose(start: Int, stopExclusive: Int): Gen[Int] =
Gen(State(RNG.nonNegativeInt).map(n => start + n % (stopExclusive-start)))
Stateの中身を忘れていて、もう一度、前のところを読み直さないとよくわからなくなっている。
Exercise6.10のmapを確認すると def map[B](f: A => B): State[S, B] ということだから
StateのnonNegativeInt:Aが範囲内の整数:Bに変換されState[S,B]になっている。
Exercise8.5
def unit[A](a: => A): Gen[A] =
Gen(State.unit(a))
def boolean: Gen[Boolean] =
Gen(State(RNG.boolean))
booleanという関数が見当たらないような気がするが。。
def listOfN[A](n: Int, g: Gen[A]): Gen[List[A]] =
Gen(State.sequence(List.fill(n)(g.sample)))
Exercise8.4 Genの上記の表現を使って、startからstopExclusiveの範囲内の整数を生成するchooseを実装せよ
def choose(start: Int, stopExclusive: Int): Gen[Int] =
Gen(State(RNG.nonNegativeInt).map(n => start + n % (stopExclusive-start)))
Stateの中身を忘れていて、もう一度、前のところを読み直さないとよくわからなくなっている。
Exercise6.10のmapを確認すると def map[B](f: A => B): State[S, B] ということだから
StateのnonNegativeInt:Aが範囲内の整数:Bに変換されState[S,B]になっている。
Exercise8.5
def unit[A](a: => A): Gen[A] =
Gen(State.unit(a))
def boolean: Gen[Boolean] =
Gen(State(RNG.boolean))
booleanという関数が見当たらないような気がするが。。
def listOfN[A](n: Int, g: Gen[A]): Gen[List[A]] =
Gen(State.sequence(List.fill(n)(g.sample)))
scala関数型デザイン&プログラミング P149~151
Exercise7.11 choiceNを実装し、choiceNをベースにchoiceを実装せよ.
def choiceN[A](n:Par[Int])(choices:List[Par[A]]):Par[A]=
es => {
val ind = run(es)(n).get
run(es)(choices(ind))
}
run(es)(n).getで ind:Intを返している。choices(ind)はind番目のListの要素であるPar[A]を表す。
(chices.apply(ind)の省略形らしい)
def choice[A](cond:Par[Boolean])(t:Par[A],f:Par[A]):Par[A]=
choiceN(map(cond)(b => if (b) 0 else 1))(List(t, f))
Exercise7.12 リストの代わりにそれらのMapを使った場合はどうなるか。
def choiceMap[K,V](key: Par[K])(choices: Map[K,Par[V]]): Par[V] =
es => {
val k = run(es)(key).get
run(es)(choices(k))
}
Exercise7.13 新しいプリミティブchooserを実装し、これを使ってchoiceとchoiceNを実装せよ。
def chooser[A,B](p: Par[A])(choices: A => Par[B]): Par[B] =
es => {
val k = run(es)(p).get
run(es)(choices(k))
}
これが、じつはflatMapであるという説明のようです。
def flatMap[A,B](p: Par[A])(choices: A => Par[B]): Par[B] =
es => {
val k = run(es)(p).get
run(es)(choices(k))
}
def choiceViaFlatMap[A](p: Par[Boolean])(f: Par[A], t: Par[A]): Par[A] =
flatMap(p)(b => if (b) t else f)
def choiceNViaFlatMap[A](p: Par[Int])(choices: List[Par[A]]): Par[A] =
flatMap(p)(i => choices(i))
Exercise7.14 joinを実装せよ。joinを使ってflatMapを実装する方法はわかるか。またflatMapを使ってjoinを実装することは可能か。
def join[A](a: Par[Par[A]]): Par[A] =
es => run(es)(run(es)(a).get())
def joinViaFlatMap[A](a: Par[Par[A]]): Par[A] =
flatMap(a)(x => x)
def flatMapViaJoin[A,B](p: Par[A])(f: A => Par[B]): Par[B] =
join(map(p)(f))
最後のjoin(map(p)(f))は、型をたどれば矛盾ない感じはするが、いまいちすっきり理解できてない
def choiceN[A](n:Par[Int])(choices:List[Par[A]]):Par[A]=
es => {
val ind = run(es)(n).get
run(es)(choices(ind))
}
run(es)(n).getで ind:Intを返している。choices(ind)はind番目のListの要素であるPar[A]を表す。
(chices.apply(ind)の省略形らしい)
def choice[A](cond:Par[Boolean])(t:Par[A],f:Par[A]):Par[A]=
choiceN(map(cond)(b => if (b) 0 else 1))(List(t, f))
Exercise7.12 リストの代わりにそれらのMapを使った場合はどうなるか。
def choiceMap[K,V](key: Par[K])(choices: Map[K,Par[V]]): Par[V] =
es => {
val k = run(es)(key).get
run(es)(choices(k))
}
Exercise7.13 新しいプリミティブchooserを実装し、これを使ってchoiceとchoiceNを実装せよ。
def chooser[A,B](p: Par[A])(choices: A => Par[B]): Par[B] =
es => {
val k = run(es)(p).get
run(es)(choices(k))
}
これが、じつはflatMapであるという説明のようです。
def flatMap[A,B](p: Par[A])(choices: A => Par[B]): Par[B] =
es => {
val k = run(es)(p).get
run(es)(choices(k))
}
def choiceViaFlatMap[A](p: Par[Boolean])(f: Par[A], t: Par[A]): Par[A] =
flatMap(p)(b => if (b) t else f)
def choiceNViaFlatMap[A](p: Par[Int])(choices: List[Par[A]]): Par[A] =
flatMap(p)(i => choices(i))
Exercise7.14 joinを実装せよ。joinを使ってflatMapを実装する方法はわかるか。またflatMapを使ってjoinを実装することは可能か。
def join[A](a: Par[Par[A]]): Par[A] =
es => run(es)(run(es)(a).get())
def joinViaFlatMap[A](a: Par[Par[A]]): Par[A] =
flatMap(a)(x => x)
def flatMapViaJoin[A,B](p: Par[A])(f: A => Par[B]): Par[B] =
join(map(p)(f))
最後のjoin(map(p)(f))は、型をたどれば矛盾ない感じはするが、いまいちすっきり理解できてない
scala関数型デザイン&プログラミング P143
リスト7-11
def run[A](es: ExecutorService)(p: Par[A]): A = {
val ref = new java.util.concurrent.atomic.AtomicReference[A]
val latch = new CountDownLatch(1)
p(es) { a => ref.set(a); latch.countDown }
latch.await
ref.get
}
p(es) { a => ref.set(a); latch.countDown } の意味がすぐにはわからなかった。
trait Future[+A] {
private[parallelism] def apply(k: A => Unit): Unit
}
type Par[+A] = ExecutorService => Future[A]
を参照すると
pがPar[A]で es=>Futre[A]なので p(es)はFuture[A]を表す。
Future[A]のapplyはA=>Unitを引数としているので、 { a => ref.set(a); latch.countDown } を適用しているようだ。
def run[A](es: ExecutorService)(p: Par[A]): A = {
val ref = new java.util.concurrent.atomic.AtomicReference[A]
val latch = new CountDownLatch(1)
p(es) { a => ref.set(a); latch.countDown }
latch.await
ref.get
}
p(es) { a => ref.set(a); latch.countDown } の意味がすぐにはわからなかった。
trait Future[+A] {
private[parallelism] def apply(k: A => Unit): Unit
}
type Par[+A] = ExecutorService => Future[A]
を参照すると
pがPar[A]で es=>Futre[A]なので p(es)はFuture[A]を表す。
Future[A]のapplyはA=>Unitを引数としているので、 { a => ref.set(a); latch.countDown } を適用しているようだ。
2015年6月13日土曜日
scala関数型デザイン&プログラミング P134
Exercise 7.6 リストの要素を並行してフィルタリングするparFilterを実装せよ。
解答は
def parFilter[A](l: List[A])(f: A => Boolean): Par[List[A]] = {
val pars: List[Par[List[A]]] =
l map (asyncF((a: A) => if (f(a)) List(a) else List()))
map(sequence(pars))(_.flatten)
}
asyncF( (a: A) => if (f(a)) List(a) else List() ) の部分を見ると
def asyncF(f:A=>B):A=>Par[B] なので 返り値は A=>Par[List[A]]となっている。
したがって、
l map (asyncF((a: A) => if (f(a)) List(a) else List())) により List[Par[List[A]]]になることがわかる。
これをsequenceの引数とすれば,Par[List[List[A]]]に変換される。
flattenはリストの結合なので、Par[List[A]]となるようだ。
解答は
def parFilter[A](l: List[A])(f: A => Boolean): Par[List[A]] = {
val pars: List[Par[List[A]]] =
l map (asyncF((a: A) => if (f(a)) List(a) else List()))
map(sequence(pars))(_.flatten)
}
asyncF( (a: A) => if (f(a)) List(a) else List() ) の部分を見ると
def asyncF(f:A=>B):A=>Par[B] なので 返り値は A=>Par[List[A]]となっている。
したがって、
l map (asyncF((a: A) => if (f(a)) List(a) else List())) により List[Par[List[A]]]になることがわかる。
これをsequenceの引数とすれば,Par[List[List[A]]]に変換される。
flattenはリストの結合なので、Par[List[A]]となるようだ。
scala関数型デザイン&プログラミング P134
Exercise 7.5 このsequenceという関数を記述せよ。追加のプリミティブは必要ない。runを呼び出さないこと。
def sequence[A](ps:List[Par[A]]):Par[List[A]]
いろいろ解答例がでていた。
def sequence_simple[A](l: List[Par[A]]): Par[List[A]] =
l.foldRight[Par[List[A]]](unit(List()))((h,t) => map2(h,t)(_ :: _))
このfoldRightを使う方法は、すでにこれまでにもでてきたパターン。
def sequenceRight[A](as: List[Par[A]]): Par[List[A]] =
as match {
case Nil => unit(Nil)
case h :: t => map2(h, fork(sequence(t)))(_ :: _)
}
こちらは、効率いい方法ということのようだけれど、難しい。
def sequenceBalanced[A](as: IndexedSeq[Par[A]]): Par[IndexedSeq[A]] = fork {
if (as.isEmpty) unit(Vector())
else if (as.length == 1) map(as.head)(a => Vector(a))
else {
val (l,r) = as.splitAt(as.length/2)
map2(sequenceBalanced(l), sequenceBalanced(r))(_ ++ _)
}
}
def sequence[A](as: List[Par[A]]): Par[List[A]] =
map(sequenceBalanced(as.toIndexedSeq))(_.toList)
def sequence[A](ps:List[Par[A]]):Par[List[A]]
いろいろ解答例がでていた。
def sequence_simple[A](l: List[Par[A]]): Par[List[A]] =
l.foldRight[Par[List[A]]](unit(List()))((h,t) => map2(h,t)(_ :: _))
このfoldRightを使う方法は、すでにこれまでにもでてきたパターン。
def sequenceRight[A](as: List[Par[A]]): Par[List[A]] =
as match {
case Nil => unit(Nil)
case h :: t => map2(h, fork(sequence(t)))(_ :: _)
}
こちらは、効率いい方法ということのようだけれど、難しい。
def sequenceBalanced[A](as: IndexedSeq[Par[A]]): Par[IndexedSeq[A]] = fork {
if (as.isEmpty) unit(Vector())
else if (as.length == 1) map(as.head)(a => Vector(a))
else {
val (l,r) = as.splitAt(as.length/2)
map2(sequenceBalanced(l), sequenceBalanced(r))(_ ++ _)
}
}
def sequence[A](as: List[Par[A]]): Par[List[A]] =
map(sequenceBalanced(as.toIndexedSeq))(_.toList)
scala関数型デザイン&プログラミング P131
Exercise7.3 map2を修正し、Futureのタイムアウトの規約に従うようにせよ。
javaをよく理解してなこともあり、コードを読み取るのが難しい。
Map2Futureの中にある@volatileは、「あるスレッドで更新された値が別スレッドで読み込まれる」ことが保証されるのだそうだ。
chache.isDefinedはOption[C]のcacheがSomeかNoneかを表す。
afの評価にかかった時間を記録し、bfの評価に割り当てられた時間からその時間を引くという操作が、val br = b.get(timeoutMs - at, TimeUnit.MILLISECONDS)の部分か?
def map2[A,B,C](a: Par[A], b: Par[B])(f: (A,B) => C): Par[C] =
es => {
val (af, bf) = (a(es), b(es))
Map2Future(af, bf, f)
}
case class Map2Future[A,B,C](a: Future[A], b: Future[B],
f: (A,B) => C) extends Future[C] {
@volatile var cache: Option[C] = None
def isDone = cache.isDefined
def isCancelled = a.isCancelled || b.isCancelled
def cancel(evenIfRunning: Boolean) =
a.cancel(evenIfRunning) || b.cancel(evenIfRunning)
def get = compute(Long.MaxValue)
def get(timeout: Long, units: TimeUnit): C =
compute(TimeUnit.MILLISECONDS.convert(timeout, units))
private def compute(timeoutMs: Long): C = cache match {
case Some(c) => c
case None =>
val start = System.currentTimeMillis
val ar = a.get(timeoutMs, TimeUnit.MILLISECONDS)
val stop = System.currentTimeMillis; val at = stop-start
val br = b.get(timeoutMs - at, TimeUnit.MILLISECONDS)
val ret = f(ar, br)
cache = Some(ret)
ret
}
}
Exercise7.4 lazyUnitを使って関数を記述せよ。この関数は任意の関数A=>Bから、その結果を非同期で評価する関数へと変換する。
def asyncF[A,B](f:A=>B):A=>Par[B]=
a => lazyUnit(f(a))
javaをよく理解してなこともあり、コードを読み取るのが難しい。
Map2Futureの中にある@volatileは、「あるスレッドで更新された値が別スレッドで読み込まれる」ことが保証されるのだそうだ。
chache.isDefinedはOption[C]のcacheがSomeかNoneかを表す。
afの評価にかかった時間を記録し、bfの評価に割り当てられた時間からその時間を引くという操作が、val br = b.get(timeoutMs - at, TimeUnit.MILLISECONDS)の部分か?
def map2[A,B,C](a: Par[A], b: Par[B])(f: (A,B) => C): Par[C] =
es => {
val (af, bf) = (a(es), b(es))
Map2Future(af, bf, f)
}
case class Map2Future[A,B,C](a: Future[A], b: Future[B],
f: (A,B) => C) extends Future[C] {
@volatile var cache: Option[C] = None
def isDone = cache.isDefined
def isCancelled = a.isCancelled || b.isCancelled
def cancel(evenIfRunning: Boolean) =
a.cancel(evenIfRunning) || b.cancel(evenIfRunning)
def get = compute(Long.MaxValue)
def get(timeout: Long, units: TimeUnit): C =
compute(TimeUnit.MILLISECONDS.convert(timeout, units))
private def compute(timeoutMs: Long): C = cache match {
case Some(c) => c
case None =>
val start = System.currentTimeMillis
val ar = a.get(timeoutMs, TimeUnit.MILLISECONDS)
val stop = System.currentTimeMillis; val at = stop-start
val br = b.get(timeoutMs - at, TimeUnit.MILLISECONDS)
val ret = f(ar, br)
cache = Some(ret)
ret
}
}
Exercise7.4 lazyUnitを使って関数を記述せよ。この関数は任意の関数A=>Bから、その結果を非同期で評価する関数へと変換する。
def asyncF[A,B](f:A=>B):A=>Par[B]=
a => lazyUnit(f(a))
scala関数型デザイン&プログラミング P122
前提となるjavaの並列処理の知識もないので、なかなか先に進めない。
def sum(ints:IndexdSeq[Int]): Int=
if(ints.size<=1)
ints headOption getOreElse 0
else {
val (l,r)=ints.splitAt(ints,length/2)
val sumL: Par[Int]=Par.unit(sum(l))
val sumR:Par[Int]=Par.unit(sum(r))
Par.get(sumL)+Par.get(sumR)
}
Par.get(Par.unit(sum(l)))+Par.get(Par.unit(sum(r)))とみなした場合どうなるか、考察している。
結局は、unitが引数の評価をすぐに開始した場合(その評価は並列で別々に処理されている)、その評価が完了するのを(両方の処理が終わってもどってくるまで)getが待機することになるということ。
だから、getの呼び出しを避けるか、最後の最後まで呼び出しを遅らせるほうがいい。非同期計算が終了するのをまたずに、それらを結合できるようにしたほうがいい。(副作用がない、参照透過性が失われない構造にするために?)ということか?
改良版が示されている。getでIntを呼び出すことは避けるため、sumでPar[Int]を呼び出している。
def sum(ints:IndexSeq[Int]):Par[Int]=
if(ints.size<=1)
Par.unit(ints.headOption getOrElse 0)
else {
val (l,r)=ints.splitAt(ints,length/2)
Par.map2(sum(l),sum(r))(_+_)
}
Exercise7.1 Par.map2は2つの並列計算の結果を統合する新しい高階関数である。そのシグネチャはどのようなものになるか。Intにのみ対応すると想定せず、できるだけ汎用的なシグネチャを示せ。
de Par.map2(Par[A],Par[A])((A,A)=>A):Par[A]
としてみたが、正解は
/* def map2[A,B,C](a: Par[A], b: Par[B])(f: (A,B) => C): Par[C] */
だった。必ずしもすべてAにせず、もっと汎用的にしなさいということのようだ。
def sum(ints:IndexdSeq[Int]): Int=
if(ints.size<=1)
ints headOption getOreElse 0
else {
val (l,r)=ints.splitAt(ints,length/2)
val sumL: Par[Int]=Par.unit(sum(l))
val sumR:Par[Int]=Par.unit(sum(r))
Par.get(sumL)+Par.get(sumR)
}
Par.get(Par.unit(sum(l)))+Par.get(Par.unit(sum(r)))とみなした場合どうなるか、考察している。
結局は、unitが引数の評価をすぐに開始した場合(その評価は並列で別々に処理されている)、その評価が完了するのを(両方の処理が終わってもどってくるまで)getが待機することになるということ。
だから、getの呼び出しを避けるか、最後の最後まで呼び出しを遅らせるほうがいい。非同期計算が終了するのをまたずに、それらを結合できるようにしたほうがいい。(副作用がない、参照透過性が失われない構造にするために?)ということか?
改良版が示されている。getでIntを呼び出すことは避けるため、sumでPar[Int]を呼び出している。
def sum(ints:IndexSeq[Int]):Par[Int]=
if(ints.size<=1)
Par.unit(ints.headOption getOrElse 0)
else {
val (l,r)=ints.splitAt(ints,length/2)
Par.map2(sum(l),sum(r))(_+_)
}
Exercise7.1 Par.map2は2つの並列計算の結果を統合する新しい高階関数である。そのシグネチャはどのようなものになるか。Intにのみ対応すると想定せず、できるだけ汎用的なシグネチャを示せ。
de Par.map2(Par[A],Par[A])((A,A)=>A):Par[A]
としてみたが、正解は
/* def map2[A,B,C](a: Par[A], b: Par[B])(f: (A,B) => C): Par[C] */
だった。必ずしもすべてAにせず、もっと汎用的にしなさいということのようだ。
2015年6月8日月曜日
scala関数型デザイン&プログラミング P112
exercise 6.11 Stateの使用に慣れるために、単純なスナックの自動販売機をモデリングする有限状態オートマンを実装せよ。この自動販売機では、2種類の入力を使用する。すなわち硬貨を投入することができ、ハンドルを回してスナックを取り出すことができる。自動販売機はロックされた状態とロックが解除された状態のどちらかになる。また、残りのスナックの数と自動販売機に投入された硬貨の数も追跡する。
解答は次のようになっている。書籍で断っている通り難問。sequenceの使い方がいまいちわからない。inputsからのi:Inputを逐次処理していくのだろうか。modfyで、状態を変化させるんだろうけれど。これだけの式で、List[Input]の中のInputをすべて逐次処理して、最終結果をs<-getで得るということなんだろうか?
sealed trait Input
case object Coin extends Input
case object Turn extends Input
case class Machine(locked:Boolean,candies:Int,conis:Int)
object Candy {
def simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = for {
_ <- sequence(inputs.map(i => modify((s: Machine) => (i, s) match {
case (_, Machine(_, 0, _)) => s
case (Coin, Machine(false, _, _)) => s
case (Turn, Machine(true, _, _)) => s
case (Coin, Machine(true, candy, coin)) =>
Machine(false, candy, coin + 1)
case (Turn, Machine(false, candy, coin)) =>
Machine(true, candy - 1, coin)
})))
s <- get
} yield (s.coins, s.candies)
}
解答は次のようになっている。書籍で断っている通り難問。sequenceの使い方がいまいちわからない。inputsからのi:Inputを逐次処理していくのだろうか。modfyで、状態を変化させるんだろうけれど。これだけの式で、List[Input]の中のInputをすべて逐次処理して、最終結果をs<-getで得るということなんだろうか?
sealed trait Input
case object Coin extends Input
case object Turn extends Input
case class Machine(locked:Boolean,candies:Int,conis:Int)
object Candy {
def simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = for {
_ <- sequence(inputs.map(i => modify((s: Machine) => (i, s) match {
case (_, Machine(_, 0, _)) => s
case (Coin, Machine(false, _, _)) => s
case (Turn, Machine(true, _, _)) => s
case (Coin, Machine(true, candy, coin)) =>
Machine(false, candy, coin + 1)
case (Turn, Machine(false, candy, coin)) =>
Machine(true, candy - 1, coin)
})))
s <- get
} yield (s.coins, s.candies)
}
2015年6月7日日曜日
scala関数型デザイン&プログラミング P110
Exercise 6.10 unit,map2,flatMap,sequenceの5つの関数を一般化せよ。
case class State[S, +A](run: S => (A, S)) {
def flatMap[B](f: A => State[S, B]): State[S, B] = State(s=> {
val (a,st)=run(s)
f(a)(st)
})
// runという関数は、S=>(A,S)なので、run(s)により(a,st)を返す
//f:A=>State[S,B]だから f(a)はState[S,B]すなわちS=>(A,S)という関数であり、引数は今の場合st:S
//をもってくるとよい。したがってf(a)(st)
def map[B](f: A => B): State[S, B] =
flatMap(a=> unit(f(a)) )
def map2[B,C](sb: State[S, B])(f: (A, B) => C): State[S, C] =
flatMap(a => sb.map(b =>f(a,b)))
//State[S,C] S=>(C,S)という関数が返値 b=>f(a,b)つまりb=>cをmapに適用する
// と State[S,B]がState[S,C]に変換される flatMap(f:A=>State[S,C]):State[S,C]という型になっている
}
object State {
def unit[S, A](a: A): State[S, A] =
State(s => (a, s))
def sequenceViaFoldRight[S,A](sas: List[State[S, A]]): State[S, List[A]] =
sas.foldRight(unit[S, List[A]](List()))((f, acc) => f.map2(acc)(_ :: _))
//ここで、folrRightを復習してみた。
//def foldRight[A,B](as:List[A],z:B)(f:(A,B)=>B):Bだった。
// (f, acc) => map2(f, acc)(_ :: _) では
// f.map2(acc)(_::_)が f:State[S,A]とacc:State[S,List[A]]を結合して、新たに State[S,List[A]]を返す
//fにはsas:List[State[S,A]]から State[S,A]をひとつずつ切り取ってくる感じ?けっこう難しい。
}
case classとobjectに分けて実装しているが、その理由など、基本的なことがまだよくわかってない。
case class State[S, +A](run: S => (A, S)) {
def flatMap[B](f: A => State[S, B]): State[S, B] = State(s=> {
val (a,st)=run(s)
f(a)(st)
})
// runという関数は、S=>(A,S)なので、run(s)により(a,st)を返す
//f:A=>State[S,B]だから f(a)はState[S,B]すなわちS=>(A,S)という関数であり、引数は今の場合st:S
//をもってくるとよい。したがってf(a)(st)
def map[B](f: A => B): State[S, B] =
flatMap(a=> unit(f(a)) )
def map2[B,C](sb: State[S, B])(f: (A, B) => C): State[S, C] =
flatMap(a => sb.map(b =>f(a,b)))
//State[S,C] S=>(C,S)という関数が返値 b=>f(a,b)つまりb=>cをmapに適用する
// と State[S,B]がState[S,C]に変換される flatMap(f:A=>State[S,C]):State[S,C]という型になっている
}
object State {
def unit[S, A](a: A): State[S, A] =
State(s => (a, s))
def sequenceViaFoldRight[S,A](sas: List[State[S, A]]): State[S, List[A]] =
sas.foldRight(unit[S, List[A]](List()))((f, acc) => f.map2(acc)(_ :: _))
//ここで、folrRightを復習してみた。
//def foldRight[A,B](as:List[A],z:B)(f:(A,B)=>B):Bだった。
// (f, acc) => map2(f, acc)(_ :: _) では
// f.map2(acc)(_::_)が f:State[S,A]とacc:State[S,List[A]]を結合して、新たに State[S,List[A]]を返す
//fにはsas:List[State[S,A]]から State[S,A]をひとつずつ切り取ってくる感じ?けっこう難しい。
}
case classとobjectに分けて実装しているが、その理由など、基本的なことがまだよくわかってない。
2015年6月4日木曜日
scala関数型デザイン&プログラミング P108
Exercise 6.8
flatMapを実装し、それを使ってnonNegativeLessThanを実装せよ。
def flatmap[A,B](f:Rand[A])(g:A=>Rand[B]):Rand[B] = { rng =>
val (a,rng2)=f(rng)
g(a)(rng2)
}
まず、fはRand[A]の型、つまり rng=>(a,rng2)を表す。したがって、val (a,rng2)=f(rng)となる。
つぎに、g:A=>Rand[B]より g(a)がRand[B]を表す。これにrng2を適用したg(a)(rng2)が返り値
そして、最終的に、flatMapの返り値は、{rng=>g(a)(rng2)}で、これはRand[B]
def nonNegativeLessThan2(n:Int):Rand[Int] =
flatMap(nonNegativeInt)(i=>
val mod=i%n
if (i+(n-1)-mod>=0)
unit(mode)
else
nonNegativeLessThan2(n)
)
flatMapの第1引数は、Rand[A]なので、nonNegativeInt:Rand[Int]という関数でいい。
次に、第2引数は、A=>Rand[B]という型の関数。AはInt、つまり変数iとして、これを引数として
Rand[B]が返り値になるような関数を記述するとよい。
この場合の返り値の計算は (i+(n-1)-mod)がTrueなら、unit(mode)つまり、rngが変化せず、(mode,rng)をそのまま返すような関数。falseならば、リトライする関数となる。
Exercise 6.9 flatMapを使ってmap,map2を再実装せよ。
def _map[A,B](s:Rand[A])(f:A=>B):Rand[B]=
flatMap(s)(a=>unit(f(a))
unitを使えばA=>Rand[B]の部分をうまく表せる
def _map2[A,B,C](ra:Rand[A],rb:Rand[B])(f:(A,B)=>C):Rand[C] =
flatMap(ra)(a => map(rb)(b => f(a, b)))
こちらは解答例。 ra,rbのふたつ同時には変換は無理なので mapを使うとひとつずつできる。
flatMapを実装し、それを使ってnonNegativeLessThanを実装せよ。
def flatmap[A,B](f:Rand[A])(g:A=>Rand[B]):Rand[B] = { rng =>
val (a,rng2)=f(rng)
g(a)(rng2)
}
まず、fはRand[A]の型、つまり rng=>(a,rng2)を表す。したがって、val (a,rng2)=f(rng)となる。
つぎに、g:A=>Rand[B]より g(a)がRand[B]を表す。これにrng2を適用したg(a)(rng2)が返り値
そして、最終的に、flatMapの返り値は、{rng=>g(a)(rng2)}で、これはRand[B]
def nonNegativeLessThan2(n:Int):Rand[Int] =
flatMap(nonNegativeInt)(i=>
val mod=i%n
if (i+(n-1)-mod>=0)
unit(mode)
else
nonNegativeLessThan2(n)
)
flatMapの第1引数は、Rand[A]なので、nonNegativeInt:Rand[Int]という関数でいい。
次に、第2引数は、A=>Rand[B]という型の関数。AはInt、つまり変数iとして、これを引数として
Rand[B]が返り値になるような関数を記述するとよい。
この場合の返り値の計算は (i+(n-1)-mod)がTrueなら、unit(mode)つまり、rngが変化せず、(mode,rng)をそのまま返すような関数。falseならば、リトライする関数となる。
Exercise 6.9 flatMapを使ってmap,map2を再実装せよ。
def _map[A,B](s:Rand[A])(f:A=>B):Rand[B]=
flatMap(s)(a=>unit(f(a))
unitを使えばA=>Rand[B]の部分をうまく表せる
def _map2[A,B,C](ra:Rand[A],rb:Rand[B])(f:(A,B)=>C):Rand[C] =
flatMap(ra)(a => map(rb)(b => f(a, b)))
こちらは解答例。 ra,rbのふたつ同時には変換は無理なので mapを使うとひとつずつできる。
2015年6月3日水曜日
scala関数型デザイン&プログラミング P107
def nonNegativeLessThan(n:Int):Rand[Int]=
map(nonNegativeInt) { i=>
val mod = i % n
if (i + (n-1) -mod >= 0) mod else nonNegaativeLessThan(n)(???)
i+(n-1)-mod>=0がnの最大の倍数より大きいことを意味するといっているが、わかりにくい。
i-modは、nの倍数だが、これが、最大の倍数でないならば、これにn-1をたしても、32ビット整数におさまる。(つまり>=0ということか?)しかし、最大の倍数ならば、これにn-1を足すと、<0になるということなんだろうか?そして、<0ということは、i-modが最大の倍数なので、iが最大の倍数より大きい数であり、nonNegativeLessThanをリトライする。と理解したがいいんだろうか?
map(nonNegativeInt) { i=>
val mod = i % n
if (i + (n-1) -mod >= 0) mod else nonNegaativeLessThan(n)(???)
i+(n-1)-mod>=0がnの最大の倍数より大きいことを意味するといっているが、わかりにくい。
i-modは、nの倍数だが、これが、最大の倍数でないならば、これにn-1をたしても、32ビット整数におさまる。(つまり>=0ということか?)しかし、最大の倍数ならば、これにn-1を足すと、<0になるということなんだろうか?そして、<0ということは、i-modが最大の倍数なので、iが最大の倍数より大きい数であり、nonNegativeLessThanをリトライする。と理解したがいいんだろうか?
2015年6月2日火曜日
scala関数型デザイン&プログラミング P106
Exercise 6.6 以下のシグネチャに基づいてmap2を実施せよ。この関数は、raとrbの2つのアクションと、それらの結果を結合する関数fを受け取り、それらを結合する新しいアクションを返す。
def map2[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A, B) => C): Rand[C] =
rng => {
val (a,rng2)=ra(rng)
val (b,rng3)=rb(rng2)
(f(a,b),rng3)
}
Exercise 6.7 2つのRNG遷移の組み合わせが可能であるとしたら、それらのリストを結合することも可能であるはずだ。遷移のListを1つの遷移にまとめるためのsequenceを実装せよ。それを使って、以前記述したints関数を再実装せよ。その際には、標準ライブラリのList.fill(n)(x)関数を使ってxをn回繰り返すリストを作成できる。
解答は
def sequence[A](fs: List[Rand[A]]): Rand[List[A]] =
fs.foldRight(unit( List[A]() )) ( (f, acc) => map2(f, acc)(_ :: _) )
unit(List[A]())は rng=>(List[A](),rng) で Rand[List[A]]
(f, acc) => map2(f, acc)(_ :: _) では
map2(f,acc)(_::_)が f:Rand[A]とacc:Rand[List[A]]を結合して、新たに Rand[List[A]]を返す
fにはfs:List[Rand[A]]から Rand[A]をひとつずつ切り取ってくる感じだろうか。。
確かに、これは難問
def _ints(count: Int): Rand[List[Int]] =
sequence(List.fill(count)(int))
val int:Rand[Int] = _.nextInt で乱数を発生 それをcount回繰り返すリスト(List[Rand[Int]])を作成している。 そして、それをsequenceでRand[List[Int]]に変換している。
def map2[A,B,C](ra: Rand[A], rb: Rand[B])(f: (A, B) => C): Rand[C] =
rng => {
val (a,rng2)=ra(rng)
val (b,rng3)=rb(rng2)
(f(a,b),rng3)
}
Exercise 6.7 2つのRNG遷移の組み合わせが可能であるとしたら、それらのリストを結合することも可能であるはずだ。遷移のListを1つの遷移にまとめるためのsequenceを実装せよ。それを使って、以前記述したints関数を再実装せよ。その際には、標準ライブラリのList.fill(n)(x)関数を使ってxをn回繰り返すリストを作成できる。
解答は
def sequence[A](fs: List[Rand[A]]): Rand[List[A]] =
fs.foldRight(unit( List[A]() )) ( (f, acc) => map2(f, acc)(_ :: _) )
unit(List[A]())は rng=>(List[A](),rng) で Rand[List[A]]
(f, acc) => map2(f, acc)(_ :: _) では
map2(f,acc)(_::_)が f:Rand[A]とacc:Rand[List[A]]を結合して、新たに Rand[List[A]]を返す
fにはfs:List[Rand[A]]から Rand[A]をひとつずつ切り取ってくる感じだろうか。。
確かに、これは難問
def _ints(count: Int): Rand[List[Int]] =
sequence(List.fill(count)(int))
val int:Rand[Int] = _.nextInt で乱数を発生 それをcount回繰り返すリスト(List[Rand[Int]])を作成している。 そして、それをsequenceでRand[List[Int]]に変換している。
登録:
投稿 (Atom)