虽然明白泛函编程风格中最重要的就是对一个管子里的元素进行操作。这个管子就是这么一个东西:F[A],我们说F是一个针对元素A的高阶类型,其实F就是一个装载A类型元素的管子,A类型是相对低阶,或者说是基础的类型。泛函编程风格就是在F内部用对付A类的函数对里面的元素进行操作。但在之前现实编程中确总是没能真正体会这种编程模式畅顺的用法:到底应该在哪里用?怎么用?可能内心里还是没能摆脱OOP的思维方式吧。在前面Stream设计章节里,我们采用了封装形式的数据结构设计,把数据结构uncons放进了特质申明里:
trait Stream[+A] { def uncons: Option[(A, Stream[A])] def isEmpty: Boolean = uncons.isEmpty } object Stream { def empty[A]: Stream[A] = new Stream[A] { def uncons = None } def cons[A](h: => A, t: => Stream[A]): Stream[A] = new Stream[A] { def uncons = Some((h,t)) } def apply[A](as: A*): Stream[A] = { if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*)) } }
我们用tuple(A, Stream[A])来代表一个完整的Stream并把它放进一个Option里,本意是空的Stream就可以用None来表示。这个Option就像是那个附加的套子把我们的目标类型(A, Stream[A])套成了F[A]类型。其实我们的目的是对管子里的A类型进行操作,特别是对A类型元素进行模式匹配。但是在之前的设计里我们却对F[A]这个戴着套子的类型进行了模式匹配。静下来回顾一下觉着还是必须想办法尽量多用些泛函的方式来做。
先看看这个map函数,我们在前面曾经为Option编写了这个函数:(oa:Option[A]).map[B](f: A => B): Option[B]。我们可以向map传入一个操作A级别类型的函数,比如一段A级别类型的模式匹配方式代码。Option map返回的结果是Option[B],是一个高阶类型,但我们可以很方便的用getOrElse来取得这个返回Option里面的元素。看个例子比较一下:
//戴着套子进行模式匹配 def toList: List[A] = uncons match { case None => Nil case Some((h,t)) => h :: t.toList }
//用map操作 def toList: List[A] = uncons.map { case (h,t) => h :: t.toList } getOrElse(Nil)
//戴着套子 def take(n: Int): Stream[A] = { if ( n == 0 ) empty else uncons match { case None => empty case Some((h,t)) => cons(h,t.take(n-1)) } }
//用map操作 def take(n: Int): Stream[A] = { if ( n == 0 ) empty else uncons map { case (h,t) => cons(h,t.take(n-1)) } getOrElse(empty) }
//戴着套子 def takeWhile(f: A => Boolean): Stream[A] = { uncons match { case None => empty case Some((h,t)) => if ( f(h) ) cons(h,t.takeWhile(f)) else empty } }
//用map操作 def takeWhile(f: A => Boolean): Stream[A] = { uncons map { case (h,t) => if ( f(h) ) cons(h,t.takeWhile(f)) else empty } getOrElse empty }
//高阶类型操作 def foldRight[B](z: B)(op: (A, => B) => B): B = { uncons match { case None => z case Some((h,t)) => op(h,t.foldRight(z)(op)) } }
//monadic style def foldRight[B](z: B)(op: (A, => B) => B): B = { uncons map { case (h,t) => op(h,t.foldRight(z)(op)) } getOrElse z }
//没用map方式 def unfold[A,S](z: S)(f: S => Option[(A,S)]): Stream[A] ={ f(z) match { case None => empty case Some((a,s)) => cons(a,unfold(s)(f)) } }
def mapByUnfold[B](f: A => B): Stream[B] = { unfold(uncons) { case Some((h,t)) => Some((f(h),Some((t.headOption.getOrElse(h), t.tail.tailOption.getOrElse(empty))))) case _ => None } }
def zipWithByUnfold[B,C](b: Stream[B])(f: (A,B) => C): Stream[C] = { unfold((uncons,b.uncons)) { case (Some((ha,ta)),Some((hb,tb))) => Some(f(ha,hb),(Some((ta.head,ta.tail)),Some((tb.head,tb.tail)))) case _ => None } }
def unfoldWithMap[A,S](z: S)(f: S => Option[(A,S)]): Stream[A] ={ f(z) map { case (a,s) => cons(a,unfold(s)(f)) } getOrElse empty }
def mapByUnfoldWithMap[B](f: A => B): Stream[B] = { unfold(this) { s => this.uncons map { case (h,t) => (f(h),t) } } }
def zipWithByUnfoldWithMap[B,C](b: Stream[B])(f: (A,B) => C): Stream[C] = { //起始状态是tuple(Stream[A],Stream[B]),状态转换函数>>> (s1,s2) => Option(a, (s1,s2)) unfold((this,b)) { s => { for { a <- s._1.uncons //用flatMap从Option[(A,Stream[A])]取出元素 >>> (A,Stream[A]) b <- s._2.uncons //用flatMap从Option[(B,Stream[B])]取出元素 >>> (B,Stream[B]) } yield { ( f(a._1, b._1), (a._2, b._2) ) //返回新的状态:C >>> (f(a,b),(ta,tb)) } } } }