1
1
package fpinscalalib
2
2
3
3
import org .scalatest .{FlatSpec , Matchers }
4
+ import fpinscalalib .List ._
4
5
5
6
/** @param name functional_data_structures
6
7
*/
@@ -88,7 +89,7 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
88
89
case Cons (x, Cons (2 , Cons (4 , _))) => x
89
90
case Nil => 42
90
91
case Cons (x, Cons (y, Cons (3 , Cons (4 , _)))) => x + y
91
- case Cons (h, t) => h + List . sum(t)
92
+ case Cons (h, t) => h + sum(t)
92
93
case _ => 101
93
94
}
94
95
x shouldBe res0
@@ -111,8 +112,8 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
111
112
*/
112
113
113
114
def listTakeAssert (res0 : List [Int ], res1 : List [Int ]) {
114
- List . tail(List (1 , 2 , 3 )) shouldBe res0
115
- List . tail(List (1 )) shouldBe res1
115
+ tail(List (1 , 2 , 3 )) shouldBe res0
116
+ tail(List (1 )) shouldBe res1
116
117
}
117
118
118
119
/**
@@ -130,8 +131,8 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
130
131
*/
131
132
132
133
def listSetHeadAssert (res0 : List [Int ], res1 : List [String ]) {
133
- List . setHead(List (1 , 2 , 3 ), 3 ) shouldBe res0
134
- List . setHead(List (" a" , " b" ), " c" ) shouldBe res1
134
+ setHead(List (1 , 2 , 3 ), 3 ) shouldBe res0
135
+ setHead(List (" a" , " b" ), " c" ) shouldBe res1
135
136
}
136
137
137
138
/**
@@ -150,11 +151,11 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
150
151
*/
151
152
152
153
def listDropAssert (res0 : List [Int ], res1 : List [Int ], res2 : List [Int ], res3 : List [Int ], res4 : List [Int ]) {
153
- List . drop(List (1 , 2 , 3 ), 1 ) shouldBe res0
154
- List . drop(List (1 , 2 , 3 ), 0 ) shouldBe res1
155
- List . drop(List (" a" , " b" ), 2 ) shouldBe res2
156
- List . drop(List (1 , 2 ), 3 ) shouldBe res3
157
- List . drop(Nil , 1 ) shouldBe res4
154
+ drop(List (1 , 2 , 3 ), 1 ) shouldBe res0
155
+ drop(List (1 , 2 , 3 ), 0 ) shouldBe res1
156
+ drop(List (" a" , " b" ), 2 ) shouldBe res2
157
+ drop(List (1 , 2 ), 3 ) shouldBe res3
158
+ drop(Nil , 1 ) shouldBe res4
158
159
}
159
160
160
161
/**
@@ -173,10 +174,10 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
173
174
*/
174
175
175
176
def listDropWhileAssert (res0 : List [Int ], res1 : List [Int ], res2 : List [Int ], res3 : List [Int ]) {
176
- List . dropWhile(List (1 , 2 , 3 ), (x : Int ) => x < 2 ) shouldBe res0
177
- List . dropWhile(List (1 , 2 , 3 ), (x : Int ) => x > 2 ) shouldBe res1
178
- List . dropWhile(List (1 , 2 , 3 ), (x : Int ) => x > 0 ) shouldBe res2
179
- List . dropWhile(Nil , (x : Int ) => x > 0 ) shouldBe res3
177
+ dropWhile(List (1 , 2 , 3 ), (x : Int ) => x < 2 ) shouldBe res0
178
+ dropWhile(List (1 , 2 , 3 ), (x : Int ) => x > 2 ) shouldBe res1
179
+ dropWhile(List (1 , 2 , 3 ), (x : Int ) => x > 0 ) shouldBe res2
180
+ dropWhile(Nil , (x : Int ) => x > 0 ) shouldBe res3
180
181
}
181
182
182
183
/**
@@ -196,8 +197,8 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
196
197
*/
197
198
198
199
def listInitAssert (res0 : List [Int ], res1 : List [Int ]) {
199
- List . init(List (1 , 2 , 3 )) shouldBe res0
200
- List . init(List (1 )) shouldBe res1
200
+ init(List (1 , 2 , 3 )) shouldBe res0
201
+ init(List (1 )) shouldBe res1
201
202
}
202
203
203
204
/**
@@ -250,10 +251,10 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
250
251
*/
251
252
252
253
def listFoldRightSumAssert (res0 : Int ,res1 : Int , res2 : Int , res3 : Int , res4 : Int , res5 : Int , res6 : List [Int ], res7 : Int , res8 : Int , res9 : Int , res10 : Int ) {
253
- List . foldRight(Cons (1 , Cons (2 , Cons (3 , Nil ))), 0 )((x,y) => x + y) shouldBe 6
254
- res0 + List . foldRight(Cons (2 , Cons (3 , Nil )), 0 )((x,y) => x + y) shouldBe 6
255
- res1 + res2 + List . foldRight(Cons (3 , Nil ), 0 )((x,y) => x + y) shouldBe 6
256
- res3 + res4 + res5 + List . foldRight(res6, 0 )((x,y) => x + y) shouldBe 6
254
+ foldRight(Cons (1 , Cons (2 , Cons (3 , Nil ))), 0 )((x,y) => x + y) shouldBe 6
255
+ res0 + foldRight(Cons (2 , Cons (3 , Nil )), 0 )((x,y) => x + y) shouldBe 6
256
+ res1 + res2 + foldRight(Cons (3 , Nil ), 0 )((x,y) => x + y) shouldBe 6
257
+ res3 + res4 + res5 + foldRight(res6, 0 )((x,y) => x + y) shouldBe 6
257
258
res7 + res8 + res9 + res10 shouldBe 6
258
259
}
259
260
@@ -263,7 +264,7 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
263
264
*/
264
265
265
266
def listFoldRightNilConsAssert (res0 : List [Int ]) {
266
- List . foldRight(List (1 , 2 , 3 ), Nil : List [Int ])(Cons (_, _)) shouldBe res0
267
+ foldRight(List (1 , 2 , 3 ), Nil : List [Int ])(Cons (_, _)) shouldBe res0
267
268
}
268
269
269
270
/**
@@ -276,5 +277,249 @@ object FunctionalDataStructuresSection extends FlatSpec with Matchers with org.s
276
277
277
278
length(l) shouldBe 5
278
279
}
280
+
281
+ /**
282
+ * Our implementation of `foldRight` is not tail-recursive and will result in a `StackOverflowError` for large lists
283
+ * (we say it's not `stack-safe`). Let's write another general list-recursion function, `foldLeft`, that is
284
+ * tail-recursive, using the techniques we discussed in the previous chapter:
285
+ *
286
+ * {{{
287
+ * def foldLeft[A,B](l: List[A], z: B)(f: (B, A) => B): B =
288
+ * l match {
289
+ * case Nil => z
290
+ * case Cons(h,t) => foldLeft(t, f(z,h))(f)
291
+ * }
292
+ * }}}
293
+ *
294
+ * Let's write functions `sum`, `product` and `length` of a list using `foldLeft`:
295
+ */
296
+
297
+ def listFoldLeftSumProductLengthAssert (res0 : Int , res1 : Double , res2 : Int , res3 : Int ): Unit = {
298
+ def sum3 (l : List [Int ]) = foldLeft(l, res0)(_ + _)
299
+ def product3 (l : List [Double ]) = foldLeft(l, res1)(_ * _)
300
+ def length2 [A ](l : List [A ]): Int = foldLeft(l, res2)((acc,h) => acc + res3)
301
+
302
+ def listInts = List (1 , 2 , 3 , 4 , 5 )
303
+ def listDoubles = List (1.0 , 2.0 , 3.0 )
304
+ sum3(listInts) shouldBe 15
305
+ product3(listDoubles) shouldBe 6.0
306
+ length2(listInts) shouldBe 5
307
+ }
308
+
309
+ /**
310
+ * As we saw above, we can write the previous functions we implemented using `foldRight` with `foldLeft`. Let's continue
311
+ * with `reverse`:
312
+ *
313
+ * {{{
314
+ * def reverse[A](l: List[A]): List[A] = foldLeft(l, List[A]())((acc, h) => Cons(h, acc))
315
+ * }}}
316
+ *
317
+ * In fact, we can write `foldLeft` in terms of `foldRight`, and the other way around:
318
+ *
319
+ * {{{
320
+ * def foldRightViaFoldLeft[A,B](l: List[A], z: B)(f: (A,B) => B): B =
321
+ * foldLeft(reverse(l), z)((b,a) => f(a,b))
322
+ *
323
+ * def foldLeftViaFoldRight[A,B](l: List[A], z: B)(f: (B,A) => B): B =
324
+ * foldRight(l, (b:B) => b)((a,g) => b => g(f(b,a)))(z)
325
+ * }}}
326
+ *
327
+ * The implementation of `foldRight` in terms of `reverse` and `foldLeft` is a common trick for avoiding stack overflows
328
+ * when implementing a strict `foldRight` function as we've done in this chapter. Note that the other implementation is
329
+ * more of theoretical interest - it isn't stack-safe and won't work for large lists.
330
+ *
331
+ */
332
+
333
+ /**
334
+ * Another function we can implement by using `foldRight` is `append`:
335
+ *
336
+ * {{{
337
+ * def appendViaFoldRight[A](l: List[A], r: List[A]): List[A] =
338
+ * foldRight(l, r)(Cons(_,_))
339
+ * }}}
340
+ *
341
+ * Take a look at its implementation and check how it works:
342
+ */
343
+
344
+ def listAppendAssert (res0 : List [Int ], res1 : List [Int ], res2 : List [Int ], res3 : List [Int ]): Unit = {
345
+ append(List (1 , 2 , 3 ), List (1 , 2 )) shouldBe res0
346
+ append(List (1 , 2 , 3 ), Nil ) shouldBe res1
347
+ append(Nil , List (1 , 2 )) shouldBe res2
348
+ append(Nil , Nil ) shouldBe res3
349
+ }
350
+
351
+ /**
352
+ * `foldRight` can also be useful to write a function `concat` that concatenates a list of lists into a single list.
353
+ * Take a look at its implementation:
354
+ *
355
+ * {{{
356
+ * def concat[A](l: List[List[A]]): List[A] =
357
+ * foldRight(l, Nil:List[A])(append)
358
+ * }}}
359
+ *
360
+ * Since `append` takes time proportional to its first argument, and this first argument never grows because of the
361
+ * right-associativity of `foldRight`, this function is linear in the total length of all lists.
362
+ *
363
+ */
364
+
365
+ /**
366
+ * Let's keep digging into the uses of `foldLeft` and `foldRight`, by implementing a function that transforms a list
367
+ * of integers by adding 1 to each element:
368
+ */
369
+
370
+ def listAdd1Assert (res0 : Int ): Unit = {
371
+ def add1 (l : List [Int ]): List [Int ] = foldRight(l, Nil : List [Int ])((h, t) => Cons (h + res0,t))
372
+ add1(List (1 , 2 , 3 )) shouldBe List (2 , 3 , 4 )
373
+ }
374
+
375
+ /**
376
+ * We can do something similar to turn each value in a List[Double] into a String:
377
+ *
378
+ * {{{
379
+ * def doubleToString(l: List[Double]): List[String] =
380
+ * foldRight(l, Nil:List[String])((h,t) => Cons(h.toString,t))
381
+ * }}}
382
+ */
383
+
384
+ /**
385
+ * Both `add1` and `doubleToString` modify each element in a list while maintaining its structure. We can generalize
386
+ * it in the following way:
387
+ *
388
+ * {{{
389
+ * def map[A,B](l: List[A])(f: A => B): List[B] =
390
+ * foldRight(l, Nil:List[B])((h,t) => Cons(f(h),t))
391
+ * }}}
392
+ *
393
+ * You may notice that we are using `foldRight` to implement `map`, even though it's not stack-safe. We can also
394
+ * use `foldRightViaFoldLeft` (that relies on reversing the original list), or use local mutation. Take a look at both
395
+ * alternatives:
396
+ *
397
+ * {{{
398
+ * def map_1[A,B](l: List[A])(f: A => B): List[B] =
399
+ * foldRightViaFoldLeft(l, Nil:List[B])((h,t) => Cons(f(h),t))
400
+ *
401
+ * def map_2[A,B](l: List[A])(f: A => B): List[B] = {
402
+ * val buf = new collection.mutable.ListBuffer[B]
403
+ * def go(l: List[A]): Unit = l match {
404
+ * case Nil => ()
405
+ * case Cons(h,t) => buf += f(h); go(t)
406
+ * }
407
+ * go(l)
408
+ * List(buf.toList: _*) // converting from the standard Scala list to the list we've defined here
409
+ * }
410
+ * }}}
411
+ */
412
+
413
+ /**
414
+ * Let's apply the same principle as in `map` to remove elements from a list, starting with a function to remove all
415
+ * odd numbers from a List[Int]:
416
+ */
417
+ def listRemoveOdds (res0 : Int , res1 : Int ): Unit = {
418
+ def removeOdds (l : List [Int ]): List [Int ] =
419
+ foldRight(l, Nil : List [Int ])((h, t) => if (h % res0 == res1) Cons (h, t) else t)
420
+ removeOdds(List (1 , 2 , 3 , 4 , 5 )) shouldBe List (2 , 4 )
421
+ }
422
+
423
+ /**
424
+ * Following the same principle, let's generalize the function above to be able to remove elements from a list unless
425
+ * they satisfy a given predicate:
426
+ *
427
+ * {{{
428
+ * def filter[A](l: List[A])(f: A => Boolean): List[A] =
429
+ * foldRight(l, Nil:List[A])((h,t) => if (f(h)) Cons(h,t) else t)
430
+ * }}}
431
+ *
432
+ * The same considerations regarding the different choices of implementations as in `map` apply to `filter`. The one
433
+ * above isn't stack-safe, so we should make a choice between using `foldRightViaFoldLeft` instead of `foldRight`, or
434
+ * perform local mutations.
435
+ */
436
+
437
+ /**
438
+ * We're going to implement a new function that works like `map` except that the function given will return a list
439
+ * instead of a single result, and that list should be inserted into the final resulting list:
440
+ *
441
+ * {{{
442
+ * def flatMap[A,B](l: List[A])(f: A => List[B]): List[B] =
443
+ * concat(map(l)(f))
444
+ * }}}
445
+ *
446
+ * Let's try it out:
447
+ */
448
+
449
+ def listFlatMapAssert (res0 : List [Int ]): Unit = {
450
+ flatMap(List (1 , 2 , 3 ))(i => List (i, i)) shouldBe res0
451
+ }
452
+
453
+ /**
454
+ * We can also implement `filter` using `flatMap`:
455
+ *
456
+ * {{{
457
+ * def filterViaFlatMap[A](l: List[A])(f: A => Boolean): List[A] =
458
+ * flatMap(l)(a => if (f(a)) List(a) else Nil)
459
+ * }}}
460
+ *
461
+ */
462
+
463
+ /**
464
+ * Now we're going to write a function that accepts two lists of integers and constructs a new list by adding
465
+ * corresponding elements. For example, `List(1, 2, 3)` and `List(4, 5, 6)` become `List(5, 7, 9)`:
466
+ *
467
+ * {{{
468
+ * def addPairwise(a: List[Int], b: List[Int]): List[Int] = (a,b) match {
469
+ * case (Nil, _) => Nil
470
+ * case (_, Nil) => Nil
471
+ * case (Cons(h1,t1), Cons(h2,t2)) => Cons(h1+h2, addPairwise(t1,t2))
472
+ * }
473
+ * }}}
474
+ *
475
+ * We can generalize the function above so that it's not specific to integers or addition, `zipWith`:
476
+ *
477
+ * {{{
478
+ * def zipWith[A,B,C](a: List[A], b: List[B])(f: (A,B) => C): List[C] = (a,b) match {
479
+ * case (Nil, _) => Nil
480
+ * case (_, Nil) => Nil
481
+ * case (Cons(h1,t1), Cons(h2,t2)) => Cons(f(h1,h2), zipWith(t1,t2)(f))
482
+ * }
483
+ * }}}
484
+ *
485
+ * Let's try out `zipWith` in the following exercise:
486
+ */
487
+
488
+ def listZipWithAssert (res0 : List [String ], res1 : List [String ]): Unit = {
489
+ zipWith(List (" a" , " b" , " c" ), List (" A" , " B" , " C" ))(_ + _) shouldBe res0
490
+ zipWith(List (1 , 2 , 3 ), List (4 , 5 , 6 ))(_.toString + _.toString()) shouldBe res1
491
+ }
492
+
493
+ /**
494
+ * As a final example to work with lists, let's implement a `hasSubsequence` function for checking whether a `List`
495
+ * contains another `List` as a subsequence. For instance, `List(1, 2, 3, 4)` would have `List(1, 2)`, `List(2, 3)`
496
+ * and `List(4)` as subsequences, among others:
497
+ *
498
+ * {{{
499
+ * @annotation.tailrec
500
+ * def startsWith[A](l: List[A], prefix: List[A]): Boolean = (l,prefix) match {
501
+ * case (_,Nil) => true
502
+ * case (Cons(h,t),Cons(h2,t2)) if h == h2 => startsWith(t, t2)
503
+ * case _ => false
504
+ * }
505
+ *
506
+ * @annotation.tailrec
507
+ * def hasSubsequence[A](sup: List[A], sub: List[A]): Boolean = sup match {
508
+ * case Nil => sub == Nil
509
+ * case _ if startsWith(sup, sub) => true
510
+ * case Cons(h,t) => hasSubsequence(t, sub)
511
+ * }
512
+ * }}}
513
+ *
514
+ * Take a deep look at the implementation of this function, and then try it out in the next exercise:
515
+ */
516
+
517
+ def listHasSubsequenceAssert (res0 : Boolean , res1 : Boolean , res2 : Boolean ): Unit = {
518
+ def l = List (1 , 2 , 3 , 4 , 5 )
519
+
520
+ hasSubsequence(l, List (2 , 3 )) shouldBe res0
521
+ hasSubsequence(l, List (0 , 1 )) shouldBe res1
522
+ hasSubsequence(l, Nil ) shouldBe res2
523
+ }
279
524
}
280
525
0 commit comments