只有collection是可以用for表示式吗? 不是的,任何实现了map, flatMap, filter的类都可以
1.整数生成器
package week2 trait Generator[+T] { def generate: T } object Test extends App { val integers = new Generator[Int] { val random = new java.util.Random() def generate = random.nextInt() } print (integers.generate) }
2.布尔值生成器
val bools = new Generator[Boolean] { def generate = integers.generate > 0 } for (i <- 1 to 10) println (bools.generate)
3. Pair生成器
val pairs = new Generator[(Int, Int)] { def generate = (integers.generate, integers.generate) } for (i <- 1 to 10) println (pairs.generate)
4. 能不能更简洁些,每次都用 new Generator很麻烦
如果能这样岂不很好
val booleans = for (x <- integers) yield x > 0 def pairs[T, U](t: Generator[T], u: Generator[U]) = for { x <- t y <- u } yield (x, y)
通过上节我们知道for循环没翻译成map,flatMat,filter来执行,要想在for循环中用integers就得定义map方法
package week1 trait Generator[+T] { self => def generate:T def map[S](f: T => S): Generator[S] = new Generator[S] { def generate = f(self.generate) } }
测试
package week1 object Test { println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet val Integers = new Generator[Int] { val rand = new java.util.Random def generate = rand.nextInt() } //> Integers : week1.Generator[Int]{val rand: java.util.Random} = week1.Test$$a //| nonfun$main$1$$anon$1@693ba66d val Bools = for {x <- Integers} yield x > 0 //> Bools : week1.Generator[Boolean] = week1.Generator$$anon$1@498665a0 Bools.generate //> res0: Boolean = true }
实现flatMap方法,这个有点抽象,我们先回忆一下for语句的展开规则
for (x <- e1) yield e2 // 被展开成 e1.map(x => e2) for (x <- e1; y <- e2 ) yield e3 // 被展开成 e1.flatMap(x => for (y <- e2) yield e3) // 进一步被展开成 e1.flatMap(x => e2.map(y => e3))
我们的目标是:
def pairs[T, U](t: Generator[T], u: Generator[U]) = for { x <- t y <- u } yield (x, y) // 会被展开成 t.flatMap(x => u.map(y => (x, y))) //进而 t.flatMap(x => new Generator[U] { def generate = (x, self.generate(y))})
那么flatMap应该做些什么呢?
将x替换成 T.this.generate(x), 交给参数
package week1 trait Generator[+T] { self => def generate:T def map[S](f: T => S): Generator[S] = new Generator[S] { def generate = f(self.generate) } def flatMap[S](f: T => Generator[S]): Generator[S] = new Generator[S] { def generate = f(self.generate).generate } }
测试
package week1 object Test { println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet val Integers = new Generator[Int] { val rand = new java.util.Random def generate = rand.nextInt() } //> Integers : week1.Generator[Int]{val rand: java.util.Random} = week1.Test$$a //| nonfun$main$1$$anon$1@33b93f89 val Bools = for {x <- Integers} yield x > 0 //> Bools : week1.Generator[Boolean] = week1.Generator$$anon$1@6f55137 Bools.generate //> res0: Boolean = true val Pairs = for (x <- Integers; y <- Bools) yield (x, y) //> Pairs : week1.Generator[(Int, Boolean)] = week1.Generator$$anon$2@bcb4598 Pairs.generate //> res1: (Int, Boolean) = (-918487409,true) }