Guest User

Untitled

a guest
Nov 25th, 2017
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.14 KB | None | 0 0
  1. package com.iwinner.mm.scala
  2.  
  3. sealed trait List[+A] // `List` data type, parameterized on a type, `A`
  4. case object Nil extends List[Nothing] // A `List` data constructor representing the empty list
  5. /* Another data constructor, representing nonempty lists. Note that `tail` is another `List[A]`,
  6. which may be `Nil` or another `Cons`.
  7. */
  8. case class Cons[+A](head: A, tail: List[A]) extends List[A]
  9.  
  10. object List { // `List` companion object. Contains functions for creating and working with lists.
  11. def sum(ints: List[Int]): Int = ints match { // A function that uses pattern matching to add up a list of integers
  12. case Nil => 0 // The sum of the empty list is 0.
  13. case Cons(x, xs) => x + sum(xs) // The sum of a list starting with `x` is `x` plus the sum of the rest of the list.
  14. }
  15.  
  16. def product(ds: List[Double]): Double = ds match {
  17. case Nil => 1.0
  18. case Cons(0.0, _) => 0.0
  19. case Cons(x, xs) => x * product(xs)
  20. }
  21.  
  22. def apply[A](as: A*): List[A] = // Variadic function syntax
  23. if (as.isEmpty) Nil
  24. else Cons(as.head, apply(as.tail: _*))
  25.  
  26. val x = List(1, 2, 3, 4, 5) match {
  27. case Cons(x, Cons(2, Cons(4, _))) => x
  28. case Nil => 42
  29. case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
  30. case Cons(h, t) => h + sum(t)
  31. case _ => 101
  32. }
  33.  
  34. def append[A](a1: List[A], a2: List[A]): List[A] =
  35. a1 match {
  36. case Nil => a2
  37. case Cons(h, t) => Cons(h, append(t, a2))
  38. }
  39.  
  40. def foldRight[A, B](as: List[A], z: B)(f: (A, B) => B): B = // Utility functions
  41. as match {
  42. case Nil => z
  43. case Cons(x, xs) => f(x, foldRight(xs, z)(f))
  44. }
  45.  
  46. def sum2(ns: List[Int]) =
  47. foldRight(ns, 0)((x, y) => x + y)
  48.  
  49. def product2(ns: List[Double]) =
  50. foldRight(ns, 1.0)(_ * _) // `_ * _` is more concise notation for `(x,y) => x * y`; see sidebar
  51.  
  52. /*
  53. 3. The third case is the first that matches, with `x` bound to 1 and `y` bound to 2.
  54. */
  55.  
  56. /*
  57. Although we could return `Nil` when the input list is empty, we choose to throw an exception instead. This is
  58. a somewhat subjective choice. In our experience, taking the tail of an empty list is often a bug, and silently
  59. returning a value just means this bug will be discovered later, further from the place where it was introduced.
  60. It's generally good practice when pattern matching to use `_` for any variables you don't intend to use on the
  61. right hand side of a pattern. This makes it clear the value isn't relevant.
  62. */
  63. def tail[A](l: List[A]): List[A] =
  64. l match {
  65. case Nil => sys.error("tail of empty list")
  66. case Cons(_, t) => t
  67. }
  68.  
  69. /*
  70. If a function body consists solely of a match expression, we'll often put the match on the same line as the
  71. function signature, rather than introducing another level of nesting.
  72. */
  73. def setHead[A](l: List[A], h: A): List[A] = l match {
  74. case Nil => sys.error("setHead on empty list")
  75. case Cons(_, t) => Cons(h, t)
  76. }
  77.  
  78. /*
  79. Again, it's somewhat subjective whether to throw an exception when asked to drop more elements than the list
  80. contains. The usual default for `drop` is not to throw an exception, since it's typically used in cases where this
  81. is not indicative of a programming error. If you pay attention to how you use `drop`, it's often in cases where the
  82. length of the input list is unknown, and the number of elements to be dropped is being computed from something else.
  83. If `drop` threw an exception, we'd have to first compute or check the length and only drop up to that many elements.
  84. */
  85. def drop[A](l: List[A], n: Int): List[A] =
  86. if (n <= 0) l
  87. else l match {
  88. case Nil => Nil
  89. case Cons(_, t) => drop(t, n - 1)
  90. }
  91.  
  92. /*
  93. Somewhat overkill, but to illustrate the feature we're using a _pattern guard_, to only match a `Cons` whose head
  94. satisfies our predicate, `f`. The syntax is to add `if <cond>` after the pattern, before the `=>`, where `<cond>` can
  95. use any of the variables introduced by the pattern.
  96. */
  97. def dropWhile[A](l: List[A], f: A => Boolean): List[A] =
  98. l match {
  99. case Cons(h, t) if f(h) => dropWhile(t, f)
  100. case _ => l
  101. }
  102.  
  103. /*
  104. Note that we're copying the entire list up until the last element. Besides being inefficient, the natural recursive
  105. solution will use a stack frame for each element of the list, which can lead to stack overflows for
  106. large lists (can you see why?). With lists, it's common to use a temporary, mutable buffer internal to the
  107. function (with lazy lists or streams, which we discuss in chapter 5, we don't normally do this). So long as the
  108. buffer is allocated internal to the function, the mutation is not observable and RT is preserved.
  109. Another common convention is to accumulate the output list in reverse order, then reverse it at the end, which
  110. doesn't require even local mutation. We'll write a reverse function later in this chapter.
  111. */
  112. def init[A](l: List[A]): List[A] =
  113. l match {
  114. case Nil => sys.error("init of empty list")
  115. case Cons(_, Nil) => Nil
  116. case Cons(h, t) => Cons(h, init(t))
  117. }
  118. def init2[A](l: List[A]): List[A] = {
  119. import collection.mutable.ListBuffer
  120. val buf = new ListBuffer[A]
  121. @annotation.tailrec
  122. def go(cur: List[A]): List[A] = cur match {
  123. case Nil => sys.error("init of empty list")
  124. case Cons(_, Nil) => List(buf.toList: _*)
  125. case Cons(h, t) => buf += h; go(t)
  126. }
  127. go(l)
  128. }
  129.  
  130. /*
  131. No, this is not possible! The reason is because _before_ we ever call our function, `f`, we evaluate its argument,
  132. which in the case of `foldRight` means traversing the list all the way to the end. We need _non-strict_ evaluation
  133. to support early termination---we discuss this in chapter 5.
  134. */
  135.  
  136. /*
  137. We get back the original list! Why is that? As we mentioned earlier, one way of thinking about what `foldRight` "does"
  138. is it replaces the `Nil` constructor of the list with the `z` argument, and it replaces the `Cons` constructor with
  139. the given function, `f`. If we just supply `Nil` for `z` and `Cons` for `f`, then we get back the input list.
  140. foldRight(Cons(1, Cons(2, Cons(3, Nil))), Nil:List[Int])(Cons(_,_))
  141. Cons(1, foldRight(Cons(2, Cons(3, Nil)), Nil:List[Int])(Cons(_,_)))
  142. Cons(1, Cons(2, foldRight(Cons(3, Nil), Nil:List[Int])(Cons(_,_))))
  143. Cons(1, Cons(2, Cons(3, foldRight(Nil, Nil:List[Int])(Cons(_,_)))))
  144. Cons(1, Cons(2, Cons(3, Nil)))
  145. */
  146.  
  147. def length[A](l: List[A]): Int =
  148. foldRight(l, 0)((_, acc) => acc + 1)
  149.  
  150. /*
  151. It's common practice to annotate functions you expect to be tail-recursive with the `tailrec` annotation. If the
  152. function is not tail-recursive, it will yield a compile error, rather than silently compiling the code and resulting
  153. in greater stack space usage at runtime.
  154. */
  155. @annotation.tailrec
  156. def foldLeft[A, B](l: List[A], z: B)(f: (B, A) => B): B = l match {
  157. case Nil => z
  158. case Cons(h, t) => foldLeft(t, f(z, h))(f)
  159. }
  160.  
  161. def sum3(l: List[Int]) = foldLeft(l, 0)(_ + _)
  162. def product3(l: List[Double]) = foldLeft(l, 1.0)(_ * _)
  163.  
  164. def length2[A](l: List[A]): Int = foldLeft(l, 0)((acc, h) => acc + 1)
  165.  
  166. def reverse[A](l: List[A]): List[A] = foldLeft(l, List[A]())((acc, h) => Cons(h, acc))
  167.  
  168. /*
  169. The implementation of `foldRight` in terms of `reverse` and `foldLeft` is a common trick for avoiding stack overflows
  170. when implementing a strict `foldRight` function as we've done in this chapter. (We'll revisit this in a later chapter,
  171. when we discuss laziness).
  172. The other implementations build up a chain of functions which, when called, results in the operations being performed
  173. with the correct associativity. We are calling `foldRight` with the `B` type being instantiated to `B => B`, then
  174. calling the built up function with the `z` argument. Try expanding the definitions by substituting equals for equals
  175. using a simple example, like `foldLeft(List(1,2,3), 0)(_ + _)` if this isn't clear. Note these implementations are
  176. more of theoretical interest - they aren't stack-safe and won't work for large lists.
  177. */
  178. def foldRightViaFoldLeft[A, B](l: List[A], z: B)(f: (A, B) => B): B =
  179. foldLeft(reverse(l), z)((b, a) => f(a, b))
  180.  
  181. def foldRightViaFoldLeft_1[A, B](l: List[A], z: B)(f: (A, B) => B): B =
  182. foldLeft(l, (b: B) => b)((g, a) => b => g(f(a, b)))(z)
  183.  
  184. def foldLeftViaFoldRight[A, B](l: List[A], z: B)(f: (B, A) => B): B =
  185. foldRight(l, (b: B) => b)((a, g) => b => g(f(b, a)))(z)
  186.  
  187. /*
  188. `append` simply replaces the `Nil` constructor of the first list with the second list, which is exactly the operation
  189. performed by `foldRight`.
  190. */
  191. def appendViaFoldRight[A](l: List[A], r: List[A]): List[A] =
  192. foldRight(l, r)(Cons(_, _))
  193.  
  194. /*
  195. Since `append` takes time proportional to its first argument, and this first argument never grows because of the
  196. right-associativity of `foldRight`, this function is linear in the total length of all lists. You may want to try
  197. tracing the execution of the implementation on paper to convince yourself that this works.
  198. Note that we're simply referencing the `append` function, without writing something like `(x,y) => append(x,y)`
  199. or `append(_,_)`. In Scala there is a rather arbitrary distinction between functions defined as _methods_, which are
  200. introduced with the `def` keyword, and function values, which are the first-class objects we can pass to other
  201. functions, put in collections, and so on. This is a case where Scala lets us pretend the distinction doesn't exist.
  202. In other cases, you'll be forced to write `append _` (to convert a `def` to a function value)
  203. or even `(x: List[A], y: List[A]) => append(x,y)` if the function is polymorphic and the type arguments aren't known.
  204. */
  205. def concat[A](l: List[List[A]]): List[A] =
  206. foldRight(l, Nil: List[A])(append)
  207.  
  208. def add1(l: List[Int]): List[Int] =
  209. foldRight(l, Nil: List[Int])((h, t) => Cons(h + 1, t))
  210.  
  211. def doubleToString(l: List[Double]): List[String] =
  212. foldRight(l, Nil: List[String])((h, t) => Cons(h.toString, t))
  213.  
  214. /*
  215. A natural solution is using `foldRight`, but our implementation of `foldRight` is not stack-safe. We can
  216. use `foldRightViaFoldLeft` to avoid the stack overflow (variation 1), but more commonly, with our current
  217. implementation of `List`, `map` will just be implemented using local mutation (variation 2). Again, note that the
  218. mutation isn't observable outside the function, since we're only mutating a buffer that we've allocated.
  219. */
  220. def map[A, B](l: List[A])(f: A => B): List[B] =
  221. foldRight(l, Nil: List[B])((h, t) => Cons(f(h), t))
  222.  
  223. def map_1[A, B](l: List[A])(f: A => B): List[B] =
  224. foldRightViaFoldLeft(l, Nil: List[B])((h, t) => Cons(f(h), t))
  225.  
  226. def map_2[A, B](l: List[A])(f: A => B): List[B] = {
  227. val buf = new collection.mutable.ListBuffer[B]
  228. def go(l: List[A]): Unit = l match {
  229. case Nil => ()
  230. case Cons(h, t) => buf += f(h); go(t)
  231. }
  232. go(l)
  233. List(buf.toList: _*) // converting from the standard Scala list to the list we've defined here
  234. }
  235.  
  236. /*
  237. The discussion about `map` also applies here.
  238. */
  239. def filter[A](l: List[A])(f: A => Boolean): List[A] =
  240. foldRight(l, Nil: List[A])((h, t) => if (f(h)) Cons(h, t) else t)
  241.  
  242. def filter_1[A](l: List[A])(f: A => Boolean): List[A] =
  243. foldRightViaFoldLeft(l, Nil: List[A])((h, t) => if (f(h)) Cons(h, t) else t)
  244.  
  245. def filter_2[A](l: List[A])(f: A => Boolean): List[A] = {
  246. val buf = new collection.mutable.ListBuffer[A]
  247. def go(l: List[A]): Unit = l match {
  248. case Nil => ()
  249. case Cons(h, t) => if (f(h)) buf += h; go(t)
  250. }
  251. go(l)
  252. List(buf.toList: _*) // converting from the standard Scala list to the list we've defined here
  253. }
  254.  
  255. /*
  256. This could also be implemented directly using `foldRight`.
  257. */
  258. def flatMap[A, B](l: List[A])(f: A => List[B]): List[B] =
  259. concat(map(l)(f))
  260.  
  261. def filterViaFlatMap[A](l: List[A])(f: A => Boolean): List[A] =
  262. flatMap(l)(a => if (f(a)) List(a) else Nil)
  263.  
  264. /*
  265. To match on multiple values, we can put the values into a pair and match on the pair, as shown next, and the same
  266. syntax extends to matching on N values (see sidebar "Pairs and tuples in Scala" for more about pair and tuple
  267. objects). You can also (somewhat less conveniently, but a bit more efficiently) nest pattern matches: on the
  268. right hand side of the `=>`, simply begin another `match` expression. The inner `match` will have access to all the
  269. variables introduced in the outer `match`.
  270. The discussion about stack usage from the explanation of `map` also applies here.
  271. */
  272. def addPairwise(a: List[Int], b: List[Int]): List[Int] = (a, b) match {
  273. case (Nil, _) => Nil
  274. case (_, Nil) => Nil
  275. case (Cons(h1, t1), Cons(h2, t2)) => Cons(h1 + h2, addPairwise(t1, t2))
  276. }
  277.  
  278. /*
  279. This function is usually called `zipWith`. The discussion about stack usage from the explanation of `map` also
  280. applies here. By putting the `f` in the second argument list, Scala can infer its type from the previous argument list.
  281. */
  282. def zipWith[A, B, C](a: List[A], b: List[B])(f: (A, B) => C): List[C] = (a, b) match {
  283. case (Nil, _) => Nil
  284. case (_, Nil) => Nil
  285. case (Cons(h1, t1), Cons(h2, t2)) => Cons(f(h1, h2), zipWith(t1, t2)(f))
  286. }
  287.  
  288. /*
  289. There's nothing particularly bad about this implementation,
  290. except that it's somewhat monolithic and easy to get wrong.
  291. Where possible, we prefer to assemble functions like this using
  292. combinations of other functions. It makes the code more obviously
  293. correct and easier to read and understand. Notice that in this
  294. implementation we need special purpose logic to break out of our
  295. loops early. In Chapter 5 we'll discuss ways of composing functions
  296. like this from simpler components, without giving up the efficiency
  297. of having the resulting functions work in one pass over the data.
  298.  
  299. It's good to specify some properties about these functions.
  300. For example, do you expect these expressions to be true?
  301.  
  302. (xs append ys) startsWith xs
  303. xs startsWith Nil
  304. (xs append ys append zs) hasSubsequence ys
  305. xs hasSubsequence Nil
  306. */
  307. @annotation.tailrec
  308. def startsWith[A](l: List[A], prefix: List[A]): Boolean = (l, prefix) match {
  309. case (_, Nil) => true
  310. case (Cons(h, t), Cons(h2, t2)) if h == h2 => startsWith(t, t2)
  311. case _ => false
  312. }
  313. @annotation.tailrec
  314. def hasSubsequence[A](sup: List[A], sub: List[A]): Boolean = sup match {
  315. case Nil => sub == Nil
  316. case _ if startsWith(sup, sub) => true
  317. case Cons(h, t) => hasSubsequence(t, sub)
  318. }
  319. }
Add Comment
Please, Sign In to add comment