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.


Recommended>>
1、School of Information organized to watch The Fifth Space cyber security film
2、Spring Recharge Series Classification of Study Notes 04 for Hongyi Lis 2017 Machine Learning Course
3、Todays break
4、You can play SEO without being technical
5、Blockchains first highpriced domain name is now online Japanese blockchain company buys gasecom for hundreds of millions of dollars

    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号