Generic Programming (14) - trytomapthemall
While it is understood that the most important thing in the generalized style of programming is to make the elements of a tube operations。 This tube is such a thing:F[A], We said.F is an element-specificA of higher order types, indeedF It's a loadedA Tubes of type elements,A Types are relatively low order, Or rather the type of base。 The pan-functional programming style is the style of programming inF inside (part, section) usefulness deal withA functions of the class to the elements inside operations。 But in real programming before, I never really got to experience the smoothness of this programming model. usefulness law: Where the hell is it supposed to be? usefulness? how? usefulness? Maybe inside you still can't get rid ofOOP mindset way bar (loanword) (serving drinks, or providing Internet access etc)。 upfrontStream In the design chapter, we adopt usefulness Data structure design in encapsulated form, structure the datauncons Put in the trait statement:
1 trait Stream[+A] { 2 def uncons: Option[(A, Stream[A])] 3 def isEmpty: Boolean = uncons.isEmpty 4 } 5 object Stream { 6 def empty[A]: Stream[A] = new Stream[A] { 7 def uncons = None 8 } 9 def cons[A](h: => A, t: => Stream[A]): Stream[A] = new Stream[A] { 10 def uncons = Some((h,t)) 11 } 12 def apply[A](as: A*): Stream[A] = { 13 if (as.isEmpty) empty 14 else cons(as.head, apply(as.tail: _*)) 15 } 16 17 }
we usefulnesstuple(A, Stream[A]) to represent a completeStream And put it into aOption Li (surname), It was meant to be empty.Stream will be able to usefulnessNone indicating that。 this oneOption It's like that additional set puts our target type(A, Stream[A]) lasso intoF[A] types。 In fact, our aim is to have a good understanding of the tube'sA type to carry out operations, Especially forA Type elements for pattern matching。 But in the previous design we were interested inF[A] this one wear a condom Pattern matching was performed on the type of。 After a moment's reflection, I feel that I must still find a way to do as much usefulness generic way do。
Look at this first.map function, We have previously written forOption This function was written:(oa:Option[A]).map[B](f: A => B): Option[B]。 We can ask themap Pass in a operationsA Functions of level type, For example, a paragraphA Pattern matching of level types way code。Option map The result returned isOption[B], is a higher-order type, But we can easily usefulnessgetOrElse to get this returnOption The elements inside。 Look at an example to compare:
1 // Pattern matching with a sleeve on 2 def toList: List[A] = uncons match { 3 case None => Nil 4 case Some((h,t)) => h :: t.toList 5 } 6 //Operate with map 7 def toList: List[A] = uncons.map { 8 case (h,t) => h :: t.toList 9 } getOrElse(Nil)
As you can see from the above example: by using map, matching with element type level patterns and then taking them out with getOrElse. The getOrElse default is used when Stream is empty. It can make the code more concise and easy to name. See a few more examples.
1 // wear a condom 2 def take(n: Int): Stream[A] = { 3 if ( n == 0 ) empty 4 else 5 uncons match { 6 case None => empty 7 case Some((h,t)) => cons(h,t.take(n-1)) 8 } 9 } 10 // usefulnessmap operations 11 def take(n: Int): Stream[A] = { 12 if ( n == 0 ) empty 13 else 14 uncons map { 15 case (h,t) => cons(h,t.take(n-1)) 16 } getOrElse(empty) 17 } 18 // wear a condom 19 def takeWhile(f: A => Boolean): Stream[A] = { 20 uncons match { 21 case None => empty 22 case Some((h,t)) => if ( f(h) ) cons(h,t.takeWhile(f)) else empty 23 } 24 } 25 // usefulnessmap operations 26 def takeWhile(f: A => Boolean): Stream[A] = { 27 uncons map { 28 case (h,t) => if ( f(h) ) cons(h,t.takeWhile(f)) else empty 29 } getOrElse empty 30 } 31 // Higher order type operations 32 def foldRight[B](z: B)(op: (A, => B) => B): B = { 33 uncons match { 34 case None => z 35 case Some((h,t)) => op(h,t.foldRight(z)(op)) 36 } 37 } 38 //monadic style 39 def foldRight[B](z: B)(op: (A, => B) => B): B = { 40 uncons map { 41 case (h,t) => op(h,t.foldRight(z)(op)) 42 } getOrElse z 43 }
Well, the commonality is obvious when changing the way you operate. Look again at the following example and how confusing it would be if map was not used.
1 // incompetentmap way 2 def unfold[A,S](z: S)(f: S => Option[(A,S)]): Stream[A] ={ 3 f(z) match { 4 case None => empty 5 case Some((a,s)) => cons(a,unfold(s)(f)) 6 } 7 } 8 def mapByUnfold[B](f: A => B): Stream[B] = { 9 unfold(uncons) { 10 case Some((h,t)) => Some((f(h),Some((t.headOption.getOrElse(h), t.tail.tailOption.getOrElse(empty))))) 11 case _ => None 12 } 13 } 14 def zipWithByUnfold[B,C](b: Stream[B])(f: (A,B) => C): Stream[C] = { 15 unfold((uncons,b.uncons)) { 16 case (Some((ha,ta)),Some((hb,tb))) => Some(f(ha,hb),(Some((ta.head,ta.tail)),Some((tb.head,tb.tail)))) 17 case _ => None 18 } 19 }
Look at the code above, because the result of passing in the unfold function f is a higher-order type Option, which makes the overall expression not only bloated, more messy and hard to read. Try rewriting these functions using map.
1 def unfoldWithMap[A,S](z: S)(f: S => Option[(A,S)]): Stream[A] ={ 2 f(z) map { 3 case (a,s) => cons(a,unfold(s)(f)) 4 } getOrElse empty 5 } 6 def mapByUnfoldWithMap[B](f: A => B): Stream[B] = { 7 unfold(this) { s => 8 this.uncons map { 9 case (h,t) => (f(h),t) 10 } 11 } 12 }
It looks much cleaner. The other one uses flatMap:
1 def zipWithByUnfoldWithMap[B,C](b: Stream[B])(f: (A,B) => C): Stream[C] = { 2 // The starting state istuple(Stream[A],Stream[B]), State transition functions>>> (s1,s2) => Option(a, (s1,s2)) 3 unfold((this,b)) { s => { 4 for { 5 a <- s._1.uncons // usefulnessflatMap through (a gap)Option[(A,Stream[A])] Take out the elements >>> (A,Stream[A]) 6 b <- s._2.uncons // usefulnessflatMap through (a gap)Option[(B,Stream[B])] Take out the elements >>> (B,Stream[B]) 7 } yield { 8 ( f(a._1, b._1), (a._2, b._2) ) // Return to new status:C >>> (f(a,b),(ta,tb)) 9 } 10 } 11 } 12 }
At first glance it may seem quite complicated, but try to make sense of the code and the above piece of code will be a little easier to understand. A demonstration of map,flatMap is inserted in between, in the hope of moving a little closer to a generalized programming style in later design thinking.