Skip to content

Commit f475f65

Browse files
committed
add multiple examples and explanations to lists and related topics
1 parent 561d904 commit f475f65

File tree

8 files changed

+170
-64
lines changed

8 files changed

+170
-64
lines changed

src/main/scala/_010_functions/_02_1_HigherOrderFunctions.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package _010_functions
22

33
import scala.language.postfixOps
44

5+
/**
6+
* a function is "higher-order" if it takes one or more other functions as a parameters.
7+
*/
58
object _02_1_HigherOrderFunctions extends App {
69
// Functions with functions (Higher order functions)
710
// function2...

src/main/scala/_010_functions/_03_Currying.scala

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ package _010_functions
33
/**
44
* Currying is the technique of transforming a function that takes multiple arguments
55
* into a function that take a single argument
6+
*
7+
* ▶ When designing a polymorphic method that takes some non-function arguments and a function argument, place the
8+
* function argument last in a curried parameter list on its own. That way, the method's correct instance type can
9+
* be inferred from the non-function arguments, and that type can in turn be used to type check the function argument.
10+
* The net effect is that users of the method will be able to give less type information and write function literals in
11+
* more compact ways.
612
*/
713
object _03_Currying extends App {
814
{
@@ -33,8 +39,8 @@ object _03_Currying extends App {
3339
def baz(x: Int, y: Int)(z: Int): Int = x + y + z
3440

3541
val f3 = foo _ // => function3
36-
val f1 = bar _ // => function1
37-
val f2 = baz _ // => function2
42+
val f1 = bar _ // => function1
43+
val f2 = baz _ // => function2
3844

3945
val g1 = foo(5, _: Int, _: Int)
4046
val g2 = bar(5) _

src/main/scala/_020_collections/_01_Lists.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@ package _020_collections
22

33
import java.time.LocalTime
44

5+
/**
6+
* > Lists are quite similar to arrays, but there are two important differences.
7+
* >> First, lists are immutable. That is, elements of a list cannot be changed by assignment.
8+
* >> Second, lists have a recursive structure (i.e., a linked list), whereas arrays are flat.
9+
*
10+
* > The list type in Scala is covariant. This means that for each pair of types S and T, if S is a subtype of T, then
11+
* List[S] is a subtype of List[T]. For instance, List[String] is a subtype of List[Object]
12+
* >> This is natural because every list of strings can also be seen as a list of objects.
13+
* >> The empty list has type List[Nothing].
14+
* >> So the empty list object, which has type List[Nothing], can also be seen as an object of every other list type of
15+
* the form List[T] (Nothing is the bottom type of every other type in Scala).
16+
*
17+
*
18+
* >> IMPORTANT:
19+
* Study '16.10 Understanding Scala's type inference algorithm', From: Programming in Scala, Third Edition.
20+
*/
521
object _01_Lists extends App {
622
val a = List(1, 2, 3, 4, 5)
723
val a2 = List.apply(1, 2, 3, 4, 5)
@@ -24,6 +40,13 @@ object _01_Lists extends App {
2440
println(s"a.tail: ${a.tail}")
2541
println(s"a.tail.head: ${a.tail.head}")
2642
println(s"a.tail.tail.head: ${a.tail.tail.head}")
43+
/**
44+
* > Unlike head and tail, which both run in constant time, init and last need to traverse the whole list to compute
45+
* their result.
46+
* >> As a result, they take time proportional to the length of the list.
47+
* ▶ It's a good idea to organize your data so that most accesses are at the head of a list, rather than the last
48+
* element.
49+
*/
2750
println(s"a.init: ${a.init}")
2851
println(s"a.last: ${a.last}")
2952

@@ -32,12 +55,23 @@ object _01_Lists extends App {
3255
println(s"a(3): ${a(3)}")
3356
println(s"a.min: ${a.min}")
3457
println(s"a.max: ${a.max}")
58+
/**
59+
* > On lists, unlike arrays, length is a relatively expensive operation.
60+
* >> It needs to traverse the whole list to find its end, and therefore
61+
* >> takes time proportional to the number of elements in the list.
62+
* >> That's why it's not a good idea to replace a test such as xs.isEmpty by xs.length == 0.
63+
* >> The result of the two tests is equivalent, but the second one is slower, in particular if the list xs is long.
64+
*/
3565
println(s"a.isEmpty: ${a.isEmpty}")
3666
println(s"a.nonEmpty: ${a.nonEmpty}")
3767
println(s"a.updated(3, 100): ${a.updated(3, 100)}")
3868

3969
println("===================")
4070

71+
println(s"a indices ==> ${a.indices}")
72+
73+
println("===================")
74+
4175
print("a.mkString(\",\"): ")
4276
println(a.mkString(","))
4377

@@ -66,6 +100,10 @@ object _01_Lists extends App {
66100

67101
println("===================")
68102

103+
/**
104+
* If at some point in the computation an algorithm demands frequent accesses to the end of a list, it's sometimes
105+
* better to reverse the list first and work with the result instead.
106+
*/
69107
println(s"a.reverse: ${a.reverse}")
70108

71109
println("===================")
Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,48 @@
11
package _021_collections_with_functions
22

3+
import scala.language.postfixOps
4+
35
object _02_Filter_FilterNot_Exsits_Functions extends App {
46
val a = 1 to 10
5-
println(a.filter(x => x % 2 == 0))
6-
println(a.filter(_ % 2 == 0))
7-
println
8-
println
9-
println(a.filterNot(x => x % 2 == 0))
10-
println(a.filterNot(_ % 2 == 0))
11-
println
12-
println
13-
println(a.exists(x => x % 2 == 0))
14-
println(a.exists(_ % 2 == 0))
15-
println
16-
println
7+
println(s"a.filter(x => x % 2 == 0) ==> ${a.filter(x => x % 2 == 0)}")
8+
println(s"a.filter(_ % 2 == 0) ==> ${a.filter(_ % 2 == 0)}")
9+
println()
10+
11+
println(s"a.filterNot(x => x % 2 == 0) ==> ${a.filterNot(x => x % 2 == 0)}")
12+
println(s"a.filterNot(_ % 2 == 0) ==> ${a.filterNot(_ % 2 == 0)}")
13+
println()
14+
15+
println(s"a.exists(x => x % 2 == 0) ==> ${a.exists(x => x % 2 == 0)}")
16+
println(s"a.exists(_ % 2 == 0) ==> ${a.exists(_ % 2 == 0)}")
17+
println()
18+
19+
println(s"a.forall(x => x % 2 == 0) ==> ${a.forall(x => x % 2 == 0)}")
20+
println(s"a.forall(_ % 2 == 0) ==> ${a.forall(_ % 2 == 0)}")
21+
println()
1722

1823
// you may use Regex for this!
19-
def filterVowels(s: String) = s.toLowerCase.filter(c => Set('a', 'e', 'i', 'o', 'u').contains(c))
24+
def filterVowels(s: String): String = s.toLowerCase.filter(c => Set('a', 'e', 'i', 'o', 'u').contains(c))
2025

26+
print("filterVowels(\"Orange\") ==> ")
2127
println(filterVowels("Orange"))
22-
println
23-
println
28+
println()
29+
2430
val b = Set("Brown", "Red", "Green", "purple", "Gray", "Yellow")
25-
println(b.filter(s => filterVowels(s).length > 1))
31+
println(s"b.filter(s => filterVowels(s).length > 1) ==> ${b.filter(s => filterVowels(s).length > 1)}")
32+
println()
2633

27-
println
28-
println
2934
val m = Map(1 -> "One", 2 -> "Two", 3 -> "Three", 4 -> "Four")
30-
println(m.filterKeys(_ % 2 == 0))
31-
32-
println
33-
println
34-
println(Some(5).filter(_ % 2 == 0)) // THIS IS WOW!
35-
println(Some(4).filter(_ % 2 == 0))
36-
37-
println
38-
println
39-
val xs = Array(1, 2, 3, 44)
40-
println((xs map (2 *)).toList) // convert to list only to show contents
41-
42-
println
43-
println
44-
val s = "Hello World"
45-
println(s filter (_.isUpper))
46-
}
35+
println(s"m.filterKeys(_ % 2 == 0) ==> ${m.filterKeys(_ % 2 == 0)}")
36+
println()
37+
38+
println(s"Some(5).filter(_ % 2 == 0) ==> ${Some(5).filter(_ % 2 == 0)}") // THIS IS WOW!
39+
println(s"Some(4).filter(_ % 2 == 0) ==> ${Some(4).filter(_ % 2 == 0)}")
40+
println()
41+
42+
// convert to list only to show contents
43+
println(s"(Array(1, 2, 3, 44) map (2 *)).toList ==> ${(Array(1, 2, 3, 44) map (2 *)).toList}")
44+
println()
45+
46+
print("\"Hello World\" filter (_.isUpper) ==> ")
47+
println("Hello World" filter (_.isUpper))
48+
}
Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,36 @@
11
package _021_collections_with_functions
22

3+
import scala.language.postfixOps
4+
5+
/**
6+
* > The zip and unzip methods provide one way to operate on multiple lists together.
7+
* > Useful special case is to zip a list with its index. This is done most efficiently with the zipWithIndex
8+
* method, which pairs every element of a list with the position where it appears in the list.
9+
* > The zipped method on tuples generalizes several common operations to work on multiple lists instead of just one.
10+
* >> One such operation is map. The map method for two zipped lists maps pairs of elements rather than individual
11+
* elements. One pair is for the first element of each list, another pair is for the second element of each list, and so on
12+
*/
313
object _07_ZipAndUnzip extends App {
414
val a = List(1, 2, 3, 4)
515
val b = List(5, 6, 7, 8)
616
val z = a zip b
17+
718
println("zip: " + z)
19+
println()
820

9-
println((1 to 5) zip (6 to 9))
10-
println((1 to 2) zip (6 to 9))
21+
println(s"(1 to 5) zip (6 to 9) ==> ${(1 to 5) zip (6 to 9)}")
22+
println(s"(1 to 2) zip (6 to 9) ==> ${(1 to 2) zip (6 to 9)}")
23+
println()
24+
25+
println(s"a zipWithIndex ==> ${a zipWithIndex}")
26+
println()
1127

1228
println("unzip: " + z.unzip)
29+
println()
30+
31+
val intPair = (List(10, 20), List(3, 4, 5))
32+
val stringLengths = (List("abc", "de"), List(3, 2))
33+
println(s"intPair.zipped.map(_ * _) ==> ${intPair.zipped.map(_ * _)}")
34+
println(s"stringLengths.zipped.forall(_.length == _) ==> ${stringLengths.zipped.forall(_.length == _)}")
35+
println(s"stringLengths.zipped.exists(_.length != _) ==> ${stringLengths.zipped.exists(_.length != _)}")
1336
}

src/main/scala/_021_collections_with_functions/_99_MoreFunctions.scala

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import scala.language.postfixOps
55
object _99_MoreFunctions extends App {
66
val a = List(1, 2, 3, 4, 5, 6)
77
// val b = a partition (x => x % 2 == 0)
8+
/** xs partition p equals (xs filter p, xs filter (!p(_))) */
89
val b = a partition (_ % 2 == 0)
910
println("===== partition =====")
1011
println(b)
@@ -15,76 +16,105 @@ object _99_MoreFunctions extends App {
1516
println(a filter (_ % 2 == 0))
1617
println(a filterNot (_ % 2 == 0))
1718

18-
println
19+
println()
20+
/** The find method is also similar to filter, but it returns the first element satisfying a given predicate,
21+
* rather than all such elements. */
22+
println("===== find =====")
23+
println(a find (_ % 2 == 0))
24+
25+
println()
1926

2027
val colours = List("Red", "Orange", "Green", "Grey", "Purple", "Pink")
2128
// val gb = colours.groupBy(x => x.head)
2229
val gb = colours groupBy (_.head)
2330
println("===== groupBy =====")
2431
println(gb)
2532

26-
println
33+
println()
2734

2835
println("===== sort =====")
2936
println(colours sortWith (_.length < _.length))
3037
println(colours sortBy (_.head))
3138
println(colours sorted)
3239

33-
println
40+
println()
3441

3542
println("===== take =====")
3643
println((1 to 100).take(10).toList)
3744
println((1 to 100).takeRight(10).toList)
3845
println((1 to 100).takeWhile(_ < 10).toList)
3946

40-
println
47+
println()
4148

4249
println("===== drop =====")
4350
println((1 to 100).drop(90).toList)
4451
println((1 to 100).dropRight(90).toList)
4552
println((1 to 100).dropWhile(_ < 90).toList)
4653

47-
println
48-
println("*------*")
49-
println
54+
println()
55+
56+
println("===== splitAt =====")
57+
println("> xs splitAt n equals (xs take n, xs drop n)")
58+
println(">> However, splitAt avoids traversing the list xs twice.")
59+
println((1 to 100).splitAt(50))
60+
println((1 to 100).splitAt(20))
61+
62+
println()
5063

5164
println("===== span =====")
65+
/** xs span p equals (xs takeWhile p, xs dropWhile p)
66+
* Like splitAt, span avoids traversing the list xs twice */
5267
println("> span is takeWhile + dropWhile")
5368
println((1 to 100).span(_ < 10))
5469
println((1 to 100).span(_ < 90))
5570

56-
println
71+
println()
5772

5873
println("===== reverse =====")
5974
println((1 to 10).reverse.toList)
6075

61-
println
76+
println()
6277

6378
println("===== distinct =====")
6479
println(List(1, 1, 2, 3, 4, 5, 6, 6, 4, 7, 8, 9, 10, 9, 10).distinct)
6580

66-
println
81+
println()
6782

6883
println("===== empty =====")
6984
println(List.empty)
7085
println(List.empty[Int])
7186
println(List.empty[String])
7287
println(List.empty[List[Int]])
7388

74-
println
89+
println()
7590

7691
println("===== exists =====")
7792
println(a exists (_ >= 5))
7893
println(a exists (_ <= 5))
7994

80-
println
95+
println()
8196

8297
println("===== forall =====")
8398
println(a forall (_ >= 5))
8499
println(a forall (_ <= 5))
85100
println(a forall (_ <= 6))
86101

87-
println
102+
println()
103+
104+
println("===== fill =====")
105+
println(List.fill(10)('a'))
106+
println(List.fill(10)(5))
107+
println(List.fill(10)(" "))
108+
println(List.fill(2, 3)('b'))
109+
110+
println()
111+
112+
println("===== tabulate =====")
113+
/** The tabulate method creates a list whose elements are computed according to a supplied function. */
114+
println(List.tabulate(5)(n => n * n))
115+
println(List.tabulate(5, 5)(_ * _))
116+
117+
println()
88118

89119
println("===== collect =====")
90120
/* collect will apply a partial function to all elements
@@ -96,7 +126,7 @@ object _99_MoreFunctions extends App {
96126
.collect { case x: Int if x % 2 == 0 => x * 2 }
97127
println(collectResult)
98128

99-
println
129+
println()
100130

101131
println("===== scan =====")
102132
/* scan is like a reduce but maintains a running total
@@ -105,7 +135,7 @@ object _99_MoreFunctions extends App {
105135
val scanResult = List(1, 2, 3).scan(1) { (total, next) => total * next }
106136
println(scanResult)
107137

108-
println
138+
println()
109139

110140
println("===== view =====")
111141
/* view will not immediately evaluate a chain until a terminal
@@ -115,7 +145,7 @@ object _99_MoreFunctions extends App {
115145
val viewResult = (1 to 1000000000).view.map(x => x * 2).take(10).mkString(", ")
116146
println(viewResult)
117147

118-
println
148+
println()
119149

120150
println("===== by name example =====")
121151
val f = List.fill(10) {
@@ -125,8 +155,4 @@ object _99_MoreFunctions extends App {
125155
}
126156

127157
println(f)
128-
129-
println
130-
println("*------*")
131-
println
132158
}

0 commit comments

Comments
 (0)