├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── misc.xml
└── modules.xml
├── README.md
├── rock-the-jvm-scala-beginners.iml
└── src
├── exercises
├── Maybe.scala
└── MyList.scala
├── lectures
├── part1basics
│ ├── CBNvsCBV.scala
│ ├── DefaultArgs.scala
│ ├── Expressions.scala
│ ├── Functions.scala
│ ├── Recursion.scala
│ ├── StringOps.scala
│ └── ValuesVariablesTypes.scala
├── part2oop
│ ├── AbstractDataTypes.scala
│ ├── AnonymousClasses.scala
│ ├── CaseClasses.scala
│ ├── Exceptions.scala
│ ├── Generics.scala
│ ├── Inheritance.scala
│ ├── MethodNotations.scala
│ ├── OOBasics.scala
│ ├── Objects.scala
│ ├── PackagingAndImports.scala
│ └── package.scala
├── part3fp
│ ├── AnonymousFunctions.scala
│ ├── HOFsCurries.scala
│ ├── HandlingFailure.scala
│ ├── MapFlatmapFilterFor.scala
│ ├── Options.scala
│ ├── Sequences.scala
│ ├── TuplesAndMaps.scala
│ └── WhatsAFunction.scala
└── part4pm
│ ├── AllThePatterns.scala
│ ├── PatternMatching.scala
│ └── PatternsEverywhere.scala
└── playground
├── Cinderella.scala
├── JavaPlayground.java
├── PrinceCharming.scala
└── ScalaPlayground.scala
/.gitignore:
--------------------------------------------------------------------------------
1 |
# Created by https://www.gitignore.io/api/scala,intellij
2 |
3 | ### Intellij ###
4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
5 |
6 | # Created by https://www.gitignore.io/api/scala,intellij
7 |
8 | ### Intellij ###
9 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
10 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
11 |
12 | # User-specific stuff:
13 | .idea/**/workspace.xml
14 | .idea/**/tasks.xml
15 | .idea/dictionaries
16 |
17 | # Sensitive or high-churn files:
18 | .idea/**/dataSources/
19 | .idea/**/dataSources.ids
20 | .idea/**/dataSources.xml
21 | .idea/**/dataSources.local.xml
22 | .idea/**/sqlDataSources.xml
23 | .idea/**/dynamic.xml
24 | .idea/**/uiDesigner.xml
25 |
26 | # Gradle:
27 | .idea/**/gradle.xml
28 | .idea/**/libraries
29 |
30 | # CMake
31 | cmake-build-debug/
32 |
33 | # Mongo Explorer plugin:
34 | .idea/**/mongoSettings.xml
35 |
36 | ## File-based project format:
37 | *.iws
38 |
39 | ## Plugin-specific files:
40 |
41 | # IntelliJ
42 | /out/
43 |
44 | # mpeltonen/sbt-idea plugin
45 | .idea_modules/
46 |
47 | # JIRA plugin
48 | atlassian-ide-plugin.xml
49 |
50 | # Cursive Clojure plugin
51 | .idea/replstate.xml
52 |
53 | # Ruby plugin and RubyMine
54 | /.rakeTasks
55 |
56 | # Crashlytics plugin (for Android Studio and IntelliJ)
57 | com_crashlytics_export_strings.xml
58 | crashlytics.properties
59 | crashlytics-build.properties
60 | fabric.properties
61 |
62 | ### Intellij Patch ###
63 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
64 |
65 | # *.iml
66 | # modules.xml
67 | # .idea/misc.xml
68 | # *.ipr
69 |
70 | # Sonarlint plugin
71 | .idea/sonarlint
72 |
73 | ### Scala ###
74 | *.class
75 | *.log
76 |
77 |
78 | # End of https://www.gitignore.io/api/scala,intellij
79 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | rock-the-jvm-scala-beginners
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | scala-sdk-2.11.7
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 | 1.8
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 1.8
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## The official repository for the Scala 2 for beginners course
3 |
4 | Powered by [Rock the JVM!](https://rockthejvm.com)
5 |
6 | This repository contains the code we wrote during [Rock the JVM's Scala beginners course](https://rockthejvm.com/course/scala-old). Unless explicitly mentioned, the code in this repository is exactly what was caught on camera.
7 |
8 | How to install:
9 | - either clone the repo or download as zip
10 | - open with IntelliJ as it's a simple IDEA project
11 |
12 | If you have changes to suggest to this repo, either
13 | - submit a GitHub issue
14 | - tell me in the course Q/A forum
15 | - submit a pull request!
16 |
--------------------------------------------------------------------------------
/rock-the-jvm-scala-beginners.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/exercises/Maybe.scala:
--------------------------------------------------------------------------------
1 | package exercises
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | abstract class Maybe[+T] {
7 |
8 | def map[B](f: T => B): Maybe[B]
9 | def flatMap[B](f: T => Maybe[B]): Maybe[B]
10 | def filter(p: T => Boolean): Maybe[T]
11 | }
12 |
13 | case object MaybeNot extends Maybe[Nothing] {
14 | def map[B](f: Nothing => B): Maybe[B] = MaybeNot
15 | def flatMap[B](f: Nothing => Maybe[B]): Maybe[B] = MaybeNot
16 | def filter(p: Nothing => Boolean): Maybe[Nothing] = MaybeNot
17 | }
18 |
19 | case class Just[+T](value: T) extends Maybe[T] {
20 |
21 | def map[B](f: T => B): Maybe[B] = Just(f(value))
22 | def flatMap[B](f: T => Maybe[B]): Maybe[B] = f(value)
23 | def filter(p: T => Boolean): Maybe[T] =
24 | if (p(value)) this
25 | else MaybeNot
26 |
27 | }
28 |
29 | object MaybeTest extends App {
30 | val just3 = Just(3)
31 | println(just3)
32 | println(just3.map(_ * 2))
33 | println(just3.flatMap(x => Just(x % 2 == 0)))
34 | println(just3.filter(_ % 2 == 0))
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/exercises/MyList.scala:
--------------------------------------------------------------------------------
1 | package exercises
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | abstract class MyList[+A] {
7 |
8 | /*
9 | head = first element of the list
10 | tail = remainder of the list
11 | isEmpty = is this list empty
12 | add(int) => new list with this element added
13 | toString => a string representation of the list
14 | */
15 |
16 | def head: A
17 | def tail: MyList[A]
18 | def isEmpty: Boolean
19 | def add[B >: A](element: B): MyList[B]
20 | def printElements: String
21 | // polymorphic call
22 | override def toString: String = "[" + printElements + "]"
23 |
24 | // higher-order functions
25 | def map[B](transformer: A => B): MyList[B]
26 | def flatMap[B](transformer: A => MyList[B]): MyList[B]
27 | def filter(predicate: A => Boolean): MyList[A]
28 |
29 | // concatenation
30 | def ++[B >: A](list: MyList[B]): MyList[B]
31 |
32 | // hofs
33 | def foreach(f: A => Unit): Unit
34 | def sort(compare: (A, A) => Int): MyList[A]
35 | def zipWith[B, C](list: MyList[B], zip:(A, B) => C): MyList[C]
36 | def fold[B](start: B)(operator: (B, A) => B): B
37 | }
38 |
39 | case object Empty extends MyList[Nothing] {
40 | def head: Nothing = throw new NoSuchElementException
41 | def tail: MyList[Nothing] = throw new NoSuchElementException
42 | def isEmpty: Boolean = true
43 | def add[B >: Nothing](element: B): MyList[B] = new Cons(element, Empty)
44 | def printElements: String = ""
45 |
46 | def map[B](transformer: Nothing => B): MyList[B] = Empty
47 | def flatMap[B](transformer: Nothing => MyList[B]): MyList[B] = Empty
48 | def filter(predicate: Nothing => Boolean): MyList[Nothing] = Empty
49 |
50 | def ++[B >: Nothing](list: MyList[B]): MyList[B] = list
51 |
52 | // hofs
53 | def foreach(f: Nothing => Unit): Unit = ()
54 | def sort(compare: (Nothing, Nothing) => Int) = Empty
55 | def zipWith[B, C](list: MyList[B], zip: (Nothing, B) => C): MyList[C] =
56 | if (!list.isEmpty) throw new RuntimeException("Lists do not have the same length")
57 | else Empty
58 | def fold[B](start: B)(operator: (B, Nothing) => B): B = start
59 | }
60 |
61 | case class Cons[+A](h: A, t: MyList[A]) extends MyList[A] {
62 | def head: A = h
63 | def tail: MyList[A] = t
64 | def isEmpty: Boolean = false
65 | def add[B >: A](element: B): MyList[B] = new Cons(element, this)
66 | def printElements: String =
67 | if(t.isEmpty) "" + h
68 | else h + " " + t.printElements
69 |
70 | /*
71 | [1,2,3].filter(n % 2 == 0) =
72 | [2,3].filter(n % 2 == 0) =
73 | = new Cons(2, [3].filter(n % 2 == 0))
74 | = new Cons(2, Empty.filter(n % 2 == 0))
75 | = new Cons(2, Empty)
76 | */
77 | def filter(predicate: A => Boolean): MyList[A] =
78 | if (predicate(h)) new Cons(h, t.filter(predicate))
79 | else t.filter(predicate)
80 |
81 | /*
82 | [1,2,3].map(n * 2)
83 | = new Cons(2, [2,3].map(n * 2))
84 | = new Cons(2, new Cons(4, [3].map(n * 2)))
85 | = new Cons(2, new Cons(4, new Cons(6, Empty.map(n * 2))))
86 | = new Cons(2, new Cons(4, new Cons(6, Empty))))
87 | */
88 | def map[B](transformer: A => B): MyList[B] =
89 | new Cons(transformer(h), t.map(transformer))
90 |
91 | /*
92 | [1,2] ++ [3,4,5]
93 | = new Cons(1, [2] ++ [3,4,5])
94 | = new Cons(1, new Cons(2, Empty ++ [3,4,5]))
95 | = new Cons(1, new Cons(2, new Cons(3, new Cons(4, new Cons(5)))))
96 | */
97 | def ++[B >: A](list: MyList[B]): MyList[B] = new Cons(h, t ++ list)
98 | /*
99 | [1,2].flatMap(n => [n, n+1])
100 | = [1,2] ++ [2].flatMap(n => [n, n+1])
101 | = [1,2] ++ [2,3] ++ Empty.flatMap(n => [n, n+1])
102 | = [1,2] ++ [2,3] ++ Empty
103 | = [1,2,2,3]
104 | */
105 | def flatMap[B](transformer: A => MyList[B]): MyList[B] =
106 | transformer(h) ++ t.flatMap(transformer)
107 |
108 | // hofs
109 | def foreach(f: A => Unit): Unit = {
110 | f(h)
111 | t.foreach(f)
112 | }
113 |
114 | def sort(compare: (A, A) => Int): MyList[A] = {
115 | def insert(x: A, sortedList: MyList[A]): MyList[A] =
116 | if (sortedList.isEmpty) new Cons(x, Empty)
117 | else if (compare(x, sortedList.head) <= 0) new Cons(x, sortedList)
118 | else new Cons(sortedList.head, insert(x, sortedList.tail))
119 |
120 | val sortedTail = t.sort(compare)
121 | insert(h, sortedTail)
122 | }
123 |
124 | def zipWith[B, C](list: MyList[B], zip: (A, B) => C): MyList[C] =
125 | if (list.isEmpty) throw new RuntimeException("Lists do not have the same length")
126 | else new Cons(zip(h, list.head), t.zipWith(list.tail, zip))
127 |
128 | /*
129 | [1,2,3].fold(0)(+) =
130 | = [2,3].fold(1)(+) =
131 | = [3].fold(3)(+) =
132 | = [].fold(6)(+)
133 | = 6
134 | */
135 | def fold[B](start: B)(operator: (B, A) => B): B =
136 | t.fold(operator(start, h))(operator)
137 |
138 | }
139 |
140 | object ListTest extends App {
141 | val listOfIntegers: MyList[Int] = new Cons(1, new Cons(2, new Cons(3, Empty)))
142 | val cloneListOfIntegers: MyList[Int] = new Cons(1, new Cons(2, new Cons(3, Empty)))
143 | val anotherListOfIntegers: MyList[Int] = new Cons(4, new Cons(5, Empty))
144 | val listOfStrings: MyList[String] = new Cons("Hello", new Cons("Scala", Empty))
145 |
146 | println(listOfIntegers.toString)
147 | println(listOfStrings.toString)
148 |
149 | println(listOfIntegers.map(_ * 2).toString)
150 |
151 | println(listOfIntegers.filter(_ % 2 == 0).toString)
152 |
153 | println((listOfIntegers ++ anotherListOfIntegers).toString)
154 | println(listOfIntegers.flatMap(elem => new Cons(elem, new Cons(elem + 1, Empty))).toString)
155 |
156 | println(cloneListOfIntegers == listOfIntegers)
157 |
158 | listOfIntegers.foreach(println)
159 | println(listOfIntegers.sort((x, y) => y - x))
160 | println(anotherListOfIntegers.zipWith[String, String](listOfStrings, _ + "-" + _))
161 | println(listOfIntegers.fold(0)(_ + _))
162 |
163 | // for comprehensions
164 | val combinations = for {
165 | n <- listOfIntegers
166 | string <- listOfStrings
167 | } yield n + "-" + string
168 | println(combinations)
169 | }
170 |
--------------------------------------------------------------------------------
/src/lectures/part1basics/CBNvsCBV.scala:
--------------------------------------------------------------------------------
1 | package lectures.part1basics
2 |
3 | /**
4 | * Created by Daniel on 07-May-18.
5 | */
6 | object CBNvsCBV extends App {
7 |
8 | def calledByValue(x: Long): Unit = {
9 | println("by value: " + 1257387745764245L)
10 | println("by value: " + 1257387745764245L)
11 | }
12 |
13 | def calledByName(x: => Long): Unit = {
14 | println("by name: " + System.nanoTime())
15 | println("by name: " + System.nanoTime())
16 | }
17 |
18 | calledByValue(1257387745764245L)
19 | calledByName(System.nanoTime())
20 |
21 | def infinite(): Int = 1 + infinite()
22 | def printFirst(x: Int, y: => Int) = println(x)
23 |
24 | // printFirst(infinite(), 34)
25 | printFirst(34, infinite())
26 | }
27 |
--------------------------------------------------------------------------------
/src/lectures/part1basics/DefaultArgs.scala:
--------------------------------------------------------------------------------
1 | package lectures.part1basics
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object DefaultArgs extends App {
7 |
8 | def trFact(n: Int, acc: Int = 1): Int =
9 | if (n <= 1) acc
10 | else trFact(n-1, n*acc)
11 |
12 | val fact10 = trFact(10, 2)
13 |
14 | def savePicture(format: String = "jpg", width: Int = 1920, height: Int = 1080): Unit = println("saving picture")
15 | savePicture(width = 800)
16 |
17 | /*
18 | 1. pass in every leading argument
19 | 2. name the arguments
20 | */
21 |
22 | savePicture(height = 600, width = 800, format = "bmp")
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/lectures/part1basics/Expressions.scala:
--------------------------------------------------------------------------------
1 | package lectures.part1basics
2 |
3 | /**
4 | * Created by Daniel on 07-May-18.
5 | */
6 | object Expressions extends App {
7 |
8 | val x = 1 + 2 // EXPRESSION
9 | println(x)
10 |
11 | println(2 + 3 * 4)
12 | // + - * / & | ^ << >> >>> (right shift with zero extension)
13 |
14 | println(1 == x)
15 | // == != > >= < <=
16 |
17 | println(!(1 == x))
18 | // ! && ||
19 |
20 | var aVariable = 2
21 | aVariable += 3 // also works with -= *= /= ..... side effects
22 | println(aVariable)
23 |
24 | // Instructions (DO) vs Expressions (VALUE)
25 |
26 | // IF expression
27 | val aCondition = true
28 | val aConditionedValue = if(aCondition) 5 else 3 // IF EXPRESSION
29 | println(aConditionedValue)
30 | println(if(aCondition) 5 else 3)
31 | println(1 + 3)
32 |
33 | var i = 0
34 | val aWhile = while (i < 10) {
35 | println(i)
36 | i += 1
37 | }
38 |
39 | // NEVER WRITE THIS AGAIN.
40 |
41 | // EVERYTHING in Scala is an Expression!
42 |
43 | val aWeirdValue = (aVariable = 3) // Unit === void
44 | println(aWeirdValue)
45 |
46 | // side effects: println(), whiles, reassigning
47 |
48 | // Code blocks
49 |
50 | val aCodeBlock = {
51 | val y = 2
52 | val z = y + 1
53 |
54 | if (z > 2) "hello" else "goodbye"
55 | }
56 |
57 | // 1. difference between "hello world" vs println("hello world")?
58 | // 2.
59 |
60 | val someValue = {
61 | 2 < 3
62 | }
63 | println(someValue)
64 |
65 | val someOtherValue = {
66 | if(someValue) 239 else 986
67 | 42
68 | }
69 | println(someOtherValue)
70 |
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/lectures/part1basics/Functions.scala:
--------------------------------------------------------------------------------
1 | package lectures.part1basics
2 |
3 | /**
4 | * Created by Daniel on 07-May-18.
5 | */
6 | object Functions extends App {
7 |
8 | def aFunction(a: String, b: Int): String = {
9 | a + " " + b
10 | }
11 |
12 | println(aFunction("hello", 3))
13 |
14 | def aParameterlessFunction(): Int = 42
15 | println(aParameterlessFunction())
16 | println(aParameterlessFunction)
17 |
18 | def aRepeatedFunction(aString: String, n: Int): String = {
19 | if (n == 1) aString
20 | else aString + aRepeatedFunction(aString, n-1)
21 | }
22 |
23 | println(aRepeatedFunction("hello",3))
24 |
25 | // WHEN YOU NEED LOOPS, USE RECURSION.
26 |
27 | def aFunctionWithSideEffects(aString: String): Unit = println(aString)
28 |
29 | def aBigFunction(n: Int): Int = {
30 | def aSmallerFunction(a: Int, b: Int): Int = a + b
31 |
32 | aSmallerFunction(n, n-1)
33 | }
34 |
35 | /*
36 | 1. A greeting function (name, age) => "Hi, my name is $name and I am $age years old."
37 | 2. Factorial function 1 * 2 * 3 * .. * n
38 | 3. A Fibonacci function
39 | f(1) = 1
40 | f(2) = 1
41 | f(n) = f(n - 1) + f(n - 2)
42 | 4. Tests if a number is prime.
43 | */
44 |
45 | def greetingForKids(name: String, age: Int): String =
46 | "Hi, my name is " + name + " and I am " + age + " years old."
47 | println(greetingForKids("David", 12))
48 |
49 | def factorial(n: Int): Int =
50 | if (n <= 0) 1
51 | else n * factorial(n-1)
52 |
53 | println(factorial(5))
54 |
55 | def fibonacci(n: Int): Int =
56 | if (n <= 2) 1
57 | else fibonacci(n-1) + fibonacci(n-2)
58 |
59 | // 1 1 2 3 5 8 13 21
60 | println(fibonacci(8))
61 |
62 | def isPrime(n: Int): Boolean = {
63 | def isPrimeUntil(t: Int): Boolean =
64 | if (t <= 1) true
65 | else n % t != 0 && isPrimeUntil(t-1)
66 |
67 | isPrimeUntil(n / 2)
68 | }
69 | println(isPrime(37))
70 | println(isPrime(2003))
71 | println(isPrime(37 * 17))
72 | }
73 |
--------------------------------------------------------------------------------
/src/lectures/part1basics/Recursion.scala:
--------------------------------------------------------------------------------
1 | package lectures.part1basics
2 |
3 | import scala.annotation.tailrec
4 |
5 | /**
6 | * Created by Daniel on 07-May-18.
7 | */
8 | object Recursion extends App {
9 |
10 | def factorial(n: Int): Int =
11 | if (n <= 1) 1
12 | else {
13 | println("Computing factorial of " + n + " - I first need factorial of " + (n-1))
14 | val result = n * factorial(n-1)
15 | println("Computed factorial of " + n)
16 |
17 | result
18 | }
19 |
20 | println(factorial(10))
21 | // println(factorial(5000))
22 |
23 | def anotherFactorial(n: Int): BigInt = {
24 | @tailrec
25 | def factHelper(x: Int, accumulator: BigInt): BigInt =
26 | if (x <= 1) accumulator
27 | else factHelper(x - 1, x * accumulator) // TAIL RECURSION = use recursive call as the LAST expression
28 |
29 | factHelper(n, 1)
30 | }
31 |
32 | /*
33 | anotherFactorial(10) = factHelper(10, 1)
34 | = factHelper(9, 10 * 1)
35 | = factHelper(8, 9 * 10 * 1)
36 | = factHelper(7, 8 * 9 * 10 * 1)
37 | = ...
38 | = factHelper(2, 3 * 4 * ... * 10 * 1)
39 | = factHelper(1, 1 * 2 * 3 * 4 * ... * 10)
40 | = 1 * 2 * 3 * 4 * ... * 10
41 |
42 | */
43 |
44 | println(anotherFactorial(20000))
45 |
46 | // WHEN YOU NEED LOOPS, USE _TAIL_ RECURSION.
47 |
48 | /*
49 | 1. Concatenate a string n times
50 | 2. IsPrime function tail recursive
51 | 3. Fibonacci function, tail recursive.
52 | */
53 |
54 | @tailrec
55 | def concatenateTailrec(aString: String, n: Int, accumulator: String): String =
56 | if (n <= 0) accumulator
57 | else concatenateTailrec(aString, n-1, aString + accumulator)
58 |
59 | println(concatenateTailrec("hello", 3, ""))
60 |
61 |
62 | def isPrime(n: Int): Boolean = {
63 | @tailrec
64 | def isPrimeTailrec(t: Int, isStillPrime: Boolean): Boolean =
65 | if (!isStillPrime) false
66 | else if (t <= 1) true
67 | else isPrimeTailrec(t - 1, n % t != 0 && isStillPrime)
68 |
69 | isPrimeTailrec(n / 2, true)
70 | }
71 |
72 | println(isPrime(2003))
73 | println(isPrime(629))
74 |
75 | def fibonacci(n: Int): Int = {
76 | def fiboTailrec(i: Int, last: Int, nextToLast: Int): Int =
77 | if(i >= n) last
78 | else fiboTailrec(i + 1, last + nextToLast, last)
79 |
80 | if (n <= 2) 1
81 | else fiboTailrec(2, 1, 1)
82 | }
83 |
84 | println(fibonacci(8)) // 1 1 2 3 5 8 13, 21
85 | }
86 |
--------------------------------------------------------------------------------
/src/lectures/part1basics/StringOps.scala:
--------------------------------------------------------------------------------
1 | package lectures.part1basics
2 |
3 | /**
4 | * Created by Daniel on 07-May-18.
5 | */
6 | object StringOps extends App {
7 |
8 | val str: String = "Hello, I am learning Scala"
9 |
10 | println(str.charAt(2))
11 | println(str.substring(7, 11))
12 | println(str.split(" ").toList)
13 | println(str.startsWith("Hello"))
14 | println(str.replace(" ", "-"))
15 | println(str.toLowerCase())
16 | println(str.length)
17 |
18 | val aNumberString = "2"
19 | val aNumber = aNumberString.toInt
20 | println('a' +: aNumberString :+ 'z')
21 | println(str.reverse)
22 | println(str.take(2))
23 |
24 | // Scala-specific: String interpolators.
25 |
26 | // S-interpolators
27 | val name = "David"
28 | val age = 12
29 | val greeting = s"Hello, my name is $name and I am $age years old"
30 | val anotherGreeting = s"Hello, my name is $name and I will be turning ${age + 1} years old."
31 | println(anotherGreeting)
32 |
33 | // F-interpolators
34 | val speed = 1.2f
35 | val myth = f"$name can eat $speed%2.2f burgers per minute"
36 | println(myth)
37 |
38 | // raw-interpolator
39 | println(raw"This is a \n newline")
40 | val escaped = "This is a \n newline"
41 | println(raw"$escaped")
42 | }
43 |
--------------------------------------------------------------------------------
/src/lectures/part1basics/ValuesVariablesTypes.scala:
--------------------------------------------------------------------------------
1 | package lectures.part1basics
2 |
3 | /**
4 | * Created by Daniel on 07-May-18.
5 | */
6 | object ValuesVariablesTypes extends App {
7 |
8 | val x: Int = 42
9 | println(x)
10 |
11 | // VALS ARE IMMUTABLE
12 |
13 | // COMPILER can infer types
14 |
15 | val aString: String = "hello"
16 | val anotherString = "goodbye"
17 |
18 | val aBoolean: Boolean = false
19 | val aChar: Char = 'a'
20 | val anInt: Int = x
21 | val aShort: Short = 4613
22 | val aLong: Long = 5273985273895237L
23 | val aFloat: Float = 2.0f
24 | val aDouble: Double = 3.14
25 |
26 | // variables
27 | var aVariable: Int = 4
28 |
29 | aVariable = 5 // side effects
30 | }
31 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/AbstractDataTypes.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object AbstractDataTypes extends App {
7 |
8 | // abstract
9 | abstract class Animal {
10 | val creatureType: String = "wild"
11 | def eat: Unit
12 | }
13 |
14 | class Dog extends Animal {
15 | override val creatureType: String = "Canine"
16 | def eat: Unit = println("crunch crunch")
17 | }
18 |
19 | // traits
20 | trait Carnivore {
21 | def eat(animal: Animal): Unit
22 | val preferredMeal: String = "fresh meat"
23 | }
24 |
25 | trait ColdBlooded
26 | class Crocodile extends Animal with Carnivore with ColdBlooded {
27 | override val creatureType: String = "croc"
28 | def eat: Unit = println("nomnomnom")
29 | def eat(animal: Animal): Unit = println(s"I'm a croc and I'm eating ${animal.creatureType}")
30 | }
31 |
32 | val dog = new Dog
33 | val croc = new Crocodile
34 | croc.eat(dog)
35 |
36 | // traits vs abstract classes
37 | // 1 - traits do not have constructor parameters
38 | // 2 - multiple traits may be inherited by the same class
39 | // 3 - traits = behavior, abstract class = "thing"
40 | }
41 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/AnonymousClasses.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object AnonymousClasses extends App {
7 |
8 | abstract class Animal {
9 | def eat: Unit
10 | }
11 |
12 | // anonymous class
13 | val funnyAnimal: Animal = new Animal {
14 | override def eat: Unit = println("ahahahahahaah")
15 | }
16 | /*
17 | equivalent with
18 |
19 | class AnonymousClasses$$anon$1 extends Animal {
20 | override def eat: Unit = println("ahahahahahaah")
21 | }
22 | val funnyAnimal: Animal = new AnonymousClasses$$anon$1
23 | */
24 |
25 | println(funnyAnimal.getClass)
26 |
27 | class Person(name: String) {
28 | def sayHi: Unit = println(s"Hi, my name is $name, how can I help?")
29 | }
30 |
31 | val jim = new Person("Jim") {
32 | override def sayHi: Unit = println(s"Hi, my name is Jim, how can I be of service?")
33 | }
34 |
35 | /*
36 | 1. Generic trait MyPredicate[-T] with a little method test(T) => Boolean
37 | 2. Generic trait MyTransformer[-A, B] with a method transform(A) => B
38 | 3. MyList:
39 | - map(transformer) => MyList
40 | - filter(predicate) => MyList
41 | - flatMap(transformer from A to MyList[B]) => MyList[B]
42 |
43 | class EvenPredicate extends MyPredicate[Int]
44 | class StringToIntTransformer extends MyTransformer[String, Int]
45 |
46 | [1,2,3].map(n * 2) = [2,4,6]
47 | [1,2,3,4].filter(n % 2) = [2,4]
48 | [1,2,3].flatMap(n => [n, n+1]) => [1,2,2,3,3,4]
49 | */
50 | }
51 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/CaseClasses.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object CaseClasses extends App {
7 |
8 | /*
9 | equals, hashCode, toString
10 | */
11 |
12 | case class Person(name: String, age: Int)
13 |
14 | // 1. class parameters are fields
15 | val jim = new Person("Jim", 34)
16 | println(jim.name)
17 |
18 | // 2. sensible toString
19 | // println(instance) = println(instance.toString) // syntactic sugar
20 | println(jim)
21 |
22 | // 3. equals and hashCode implemented OOTB
23 | val jim2 = new Person("Jim", 34)
24 | println(jim == jim2)
25 |
26 | // 4. CCs have handy copy method
27 | val jim3 = jim.copy(age = 45)
28 | println(jim3)
29 |
30 | // 5. CCs have companion objects
31 | val thePerson = Person
32 | val mary = Person("Mary", 23)
33 |
34 | // 6. CCs are serializable
35 | // Akka
36 |
37 | // 7. CCs have extractor patterns = CCs can be used in PATTERN MATCHING
38 |
39 | case object UnitedKingdom {
40 | def name: String = "The UK of GB and NI"
41 | }
42 |
43 | /*
44 | Expand MyList - use case classes and case objects
45 | */
46 | }
47 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/Exceptions.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object Exceptions extends App {
7 |
8 | val x: String = null
9 | // println(x.length)
10 | // this ^^ will crash with a NPE
11 |
12 | // 1. throwing exceptions
13 | // val aWeirdValue: String = throw new NullPointerException
14 |
15 | // throwable classes extend the Throwable class.
16 | // Exception and Error are the major Throwable subtypes
17 |
18 | // 2. how to catch exceptions
19 | def getInt(withExceptions: Boolean): Int =
20 | if (withExceptions) throw new RuntimeException("No int for you!")
21 | else 42
22 |
23 | val potentialFail = try {
24 | // code that might throw
25 | getInt(false)
26 | } catch {
27 | case e: RuntimeException => 43
28 | } finally {
29 | // code that will get executed NO MATTER WHAT
30 | // optional
31 | // does not influence the return type of this expression
32 | // use finally only for side effects
33 | println("finally")
34 | }
35 |
36 | println(potentialFail)
37 |
38 | // 3. how to define your own exceptions
39 | class MyException extends Exception
40 | val exception = new MyException
41 |
42 | // throw exception
43 |
44 | /*
45 | 1. Crash your program with an OutOfMemoryError
46 | 2. Crash with SOError
47 | 3. PocketCalculator
48 | - add(x,y)
49 | - subtract(x,y)
50 | - multiply(x,y)
51 | - divide(x,y)
52 |
53 | Throw
54 | - OverflowException if add(x,y) exceeds Int.MAX_VALUE
55 | - UnderflowException if subtract(x,y) exceeds Int.MIN_VALUE
56 | - MathCalculationException for division by 0
57 | */
58 | // OOM
59 | // val array = Array.ofDim(Int.MaxValue)
60 |
61 | // SO
62 | // def infinite: Int = 1 + infinite
63 | // val noLimit = infinite
64 |
65 | class OverflowException extends RuntimeException
66 | class UnderflowException extends RuntimeException
67 | class MathCalculationException extends RuntimeException("Division by 0")
68 |
69 | object PocketCalculator {
70 | def add(x: Int, y: Int) = {
71 | val result = x + y
72 |
73 | if (x > 0 && y > 0 && result < 0) throw new OverflowException
74 | else if (x < 0 && y < 0 && result > 0) throw new UnderflowException
75 | else result
76 | }
77 |
78 | def subtract(x: Int, y: Int) = {
79 | val result = x - y
80 | if (x > 0 && y < 0 && result < 0) throw new OverflowException
81 | else if (x < 0 && y > 0 && result > 0) throw new UnderflowException
82 | else result
83 | }
84 |
85 | def multiply(x: Int, y: Int) = {
86 | val result = x * y
87 | if (x > 0 && y > 0 && result < 0) throw new OverflowException
88 | else if (x < 0 && y < 0 && result < 0) throw new OverflowException
89 | else if (x > 0 && y < 0 && result > 0) throw new UnderflowException
90 | else if (x < 0 && y > 0 && result > 0) throw new UnderflowException
91 | else result
92 | }
93 |
94 | def divide(x: Int, y: Int) = {
95 | if (y == 0) throw new MathCalculationException
96 | else x / y
97 | }
98 |
99 | }
100 |
101 | println(PocketCalculator.add(Int.MaxValue, 10))
102 | println(PocketCalculator.divide(2, 0))
103 | }
104 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/Generics.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object Generics extends App {
7 |
8 | class MyList[+A] {
9 | // use the type A
10 | def add[B >: A](element: B): MyList[B] = ???
11 | /*
12 | A = Cat
13 | B = Animal
14 | */
15 | }
16 |
17 | class MyMap[Key, Value]
18 |
19 | val listOfIntegers = new MyList[Int]
20 | val listOfStrings = new MyList[String]
21 |
22 | // generic methods
23 | object MyList {
24 | def empty[A]: MyList[A] = ???
25 | }
26 | val emptyListOfIntegers = MyList.empty[Int]
27 |
28 | // variance problem
29 | class Animal
30 | class Cat extends Animal
31 | class Dog extends Animal
32 |
33 | // 1. yes, List[Cat] extends List[Animal] = COVARIANCE
34 | class CovariantList[+A]
35 | val animal: Animal = new Cat
36 | val animalList: CovariantList[Animal] = new CovariantList[Cat]
37 | // animalList.add(new Dog) ??? HARD QUESTION => we return a list of Animals
38 |
39 | // 2. NO = INVARIANCE
40 | class InvariantList[A]
41 | val invariantAnimalList: InvariantList[Animal] = new InvariantList[Animal]
42 |
43 | // 3. Hell, no! CONTRAVARIANCE
44 | class Trainer[-A]
45 | val trainer: Trainer[Cat] = new Trainer[Animal]
46 |
47 | // bounded types
48 | class Cage[A <: Animal](animal: A)
49 | val cage = new Cage(new Dog)
50 |
51 | class Car
52 | // generic type needs proper bounded type
53 | // val newCage = new Cage(new Car)
54 |
55 |
56 | // expand MyList to be generic
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/Inheritance.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object Inheritance extends App {
7 |
8 | // single class inheritance
9 | sealed class Animal {
10 | val creatureType = "wild"
11 | def eat = println("nomnom")
12 | }
13 |
14 | class Cat extends Animal {
15 | def crunch = {
16 | eat
17 | println("crunch crunch")
18 | }
19 | }
20 |
21 | val cat = new Cat
22 | cat.crunch
23 |
24 |
25 | // constructors
26 | class Person(name: String, age: Int) {
27 | def this(name: String) = this(name, 0)
28 | }
29 | class Adult(name: String, age: Int, idCard: String) extends Person(name)
30 |
31 | // overriding
32 | class Dog(override val creatureType: String) extends Animal {
33 | // override val creatureType = "domestic"
34 | override def eat = {
35 | super.eat
36 | println("crunch, crunch")
37 | }
38 | }
39 | val dog = new Dog("K9")
40 | dog.eat
41 | println(dog.creatureType)
42 |
43 |
44 | // type substitution (broad: polymorphism)
45 | val unknownAnimal: Animal = new Dog("K9")
46 | unknownAnimal.eat
47 |
48 | // overRIDING vs overLOADING
49 |
50 | // super
51 |
52 | // preventing overrides
53 | // 1 - use final on member
54 | // 2 - use final on the entire class
55 | // 3 - seal the class = extend classes in THIS FILE, prevent extension in other files
56 | }
57 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/MethodNotations.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object MethodNotations extends App {
7 |
8 | class Person(val name: String, favoriteMovie: String, val age: Int = 0) {
9 | def likes(movie: String): Boolean = movie == favoriteMovie
10 | def +(person: Person): String = s"${this.name} is hanging out with ${person.name}"
11 | def +(nickname: String): Person = new Person(s"$name ($nickname)", favoriteMovie)
12 | def unary_! : String = s"$name, what the heck?!"
13 | def unary_+ : Person = new Person(name, favoriteMovie, age + 1)
14 | def isAlive: Boolean = true
15 | def apply(): String = s"Hi, my name is $name and I like $favoriteMovie"
16 | def apply(n: Int): String = s"$name watched $favoriteMovie $n times"
17 | def learns(thing: String) = s"$name is learning $thing"
18 | def learnsScala = this learns "Scala"
19 | }
20 |
21 |
22 | val mary = new Person("Mary", "Inception")
23 | println(mary.likes("Inception"))
24 | println(mary likes "Inception") // equivalent
25 | // infix notation = operator notation (syntactic sugar)
26 |
27 | // "operators" in Scala
28 | val tom = new Person("Tom", "Fight Club")
29 | println(mary + tom)
30 | println(mary.+(tom))
31 |
32 | println(1 + 2)
33 | println(1.+(2))
34 |
35 | // ALL OPERATORS ARE METHODS.
36 | // Akka actors have ! ?
37 |
38 | // prefix notation
39 | val x = -1 // equivalent with 1.unary_-
40 | val y = 1.unary_-
41 | // unary_ prefix only works with - + ~ !
42 |
43 | println(!mary)
44 | println(mary.unary_!)
45 |
46 | // postfix notation
47 | println(mary.isAlive)
48 | println(mary isAlive)
49 |
50 | // apply
51 | println(mary.apply())
52 | println(mary()) // equivalent
53 |
54 | /*
55 | 1. Overload the + operator
56 | mary + "the rockstar" => new person "Mary (the rockstar)"
57 |
58 | 2. Add an age to the Person class
59 | Add a unary + operator => new person with the age + 1
60 | +mary => mary with the age incrementer
61 |
62 | 3. Add a "learns" method in the Person class => "Mary learns Scala"
63 | Add a learnsScala method, calls learns method with "Scala".
64 | Use it in postfix notation.
65 |
66 | 4. Overload the apply method
67 | mary.apply(2) => "Mary watched Inception 2 times"
68 | */
69 |
70 | println((mary + "the Rockstar").apply())
71 | println((+mary).age)
72 | println(mary learnsScala)
73 | println(mary(10))
74 |
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/OOBasics.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object OOBasics extends App {
7 |
8 | // constructor
9 | class Person(name: String, val age: Int = 0) {
10 | // body
11 | val x = 2
12 |
13 | println(1 + 3)
14 |
15 | // method
16 | def greet(name: String): Unit = println(s"${this.name} says: Hi, $name")
17 |
18 | // overloading
19 | def greet(): Unit = println(s"Hi, I am $name")
20 |
21 | // multiple constructors
22 | def this(name: String) = this(name, 0)
23 | def this() = this("John Doe")
24 | }
25 |
26 | val person = new Person("John", 26)
27 | println(person.x)
28 | person.greet("Daniel")
29 | person.greet()
30 |
31 | val author = new Writer("Charles", "Dickens", 1812)
32 | val imposter = new Writer("Charles", "Dickens", 1812)
33 | val novel = new Novel("Great Expectations", 1861, author)
34 |
35 | println(novel.authorAge)
36 | println(novel.isWrittenBy(imposter))
37 |
38 | val counter = new Counter
39 | counter.inc.print
40 | counter.inc.inc.inc.print
41 | counter.inc(10).print
42 | }
43 |
44 |
45 |
46 | /*
47 | Novel and a Writer
48 |
49 | Writer: first name, surname, year
50 | - method fullname
51 |
52 | Novel: name, year of release, author
53 | - authorAge
54 | - isWrittenBy(author)
55 | - copy (new year of release) = new instance of Novel
56 |
57 |
58 | */
59 |
60 | class Writer(firstName: String, surname: String, val year: Int) {
61 | def fullName: String = firstName + " " + surname
62 | }
63 |
64 | class Novel(name: String, year: Int, author: Writer) {
65 | def authorAge = year - author.year
66 | def isWrittenBy(author: Writer) = author == this.author
67 | def copy(newYear: Int): Novel = new Novel(name, newYear, author)
68 | }
69 |
70 | /*
71 | Counter class
72 | - receives an int value
73 | - method current count
74 | - method to increment/decrement => new Counter
75 | - overload inc/dec to receive an amount
76 | */
77 | class Counter(val count: Int = 0) {
78 | def inc = {
79 | println("incrementing")
80 | new Counter(count + 1) // immutability
81 | }
82 |
83 | def dec = {
84 | println("decrementing")
85 | new Counter(count - 1)
86 | }
87 |
88 | def inc(n: Int): Counter = {
89 | if (n <= 0) this
90 | else inc.inc(n-1)
91 | }
92 |
93 | def dec(n: Int): Counter =
94 | if (n <= 0) this
95 | else dec.dec(n-1)
96 |
97 | def print = println(count)
98 | }
99 |
100 | // class parameters are NOT FIELDS
--------------------------------------------------------------------------------
/src/lectures/part2oop/Objects.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object Objects extends App {
7 |
8 | // SCALA DOES NOT HAVE CLASS-LEVEL FUNCTIONALITY ("static")
9 | object Person { // type + its only instance
10 | // "static"/"class" - level functionality
11 | val N_EYES = 2
12 | def canFly: Boolean = false
13 |
14 | // factory method
15 | def apply(mother: Person, father: Person): Person = new Person("Bobbie")
16 | }
17 | class Person(val name: String) {
18 | // instance-level functionality
19 | }
20 | // COMPANIONS
21 |
22 | println(Person.N_EYES)
23 | println(Person.canFly)
24 |
25 | // Scala object = SINGLETON INSTANCE
26 | val mary = new Person("Mary")
27 | val john = new Person("John")
28 | println(mary == john)
29 |
30 | val person1 = Person
31 | val person2 = Person
32 | println(person1 == person2)
33 |
34 | val bobbie = Person(mary, john)
35 | // Scala Applications = Scala object with
36 | // def main(args: Array[String]): Unit
37 |
38 |
39 | val k = 6.67e-11
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/PackagingAndImports.scala:
--------------------------------------------------------------------------------
1 | package lectures.part2oop
2 |
3 | import playground.{PrinceCharming, Cinderella => Princess}
4 |
5 | import java.util.Date
6 | import java.sql.{Date => SqlDate}
7 |
8 | /**
9 | * Created by Daniel.
10 | */
11 | object PackagingAndImports extends App {
12 |
13 | // package members are accessible by their simple name
14 | val writer = new Writer("Daniel", "RockTheJVM", 2018)
15 |
16 | // import the package
17 | val princess = new Princess // playground.Cinderella = fully qualified name
18 |
19 | // packages are in hierarchy
20 | // matching folder structure.
21 |
22 | // package object
23 | sayHello
24 | println(SPEED_OF_LIGHT)
25 |
26 | // imports
27 | val prince = new PrinceCharming
28 |
29 | // 1. use FQ names
30 | val date = new Date
31 | val sqlDate = new SqlDate(2018, 5, 4)
32 | // 2. use aliasing
33 |
34 | // default imports
35 | // java.lang - String, Object, Exception
36 | // scala - Int, Nothing, Function
37 | // scala.Predef - println, ???
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/lectures/part2oop/package.scala:
--------------------------------------------------------------------------------
1 | package lectures
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | package object part2oop {
7 |
8 | def sayHello: Unit = println("Hello, Scala")
9 | val SPEED_OF_LIGHT = 299792458
10 | }
11 |
--------------------------------------------------------------------------------
/src/lectures/part3fp/AnonymousFunctions.scala:
--------------------------------------------------------------------------------
1 | package lectures.part3fp
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object AnonymousFunctions extends App {
7 |
8 | // anonymous function (LAMBDA)
9 | val doubler: Int => Int = (x: Int) => x * 2
10 |
11 | // multiple params in a lambda
12 | val adder: (Int, Int) => Int = (a: Int, b: Int) => a + b
13 |
14 | // no params
15 | val justDoSomething: () => Int = () => 3
16 |
17 | // careful
18 | println(justDoSomething) // function itself
19 | println(justDoSomething()) // call
20 |
21 | // curly braces with lambdas
22 | val stringToInt = { (str: String) =>
23 | str.toInt
24 | }
25 |
26 | // MOAR syntactic sugar
27 | val niceIncrementer: Int => Int = _ + 1 // equivalent to x => x + 1
28 | val niceAdder: (Int, Int) => Int = _ + _ // equivalent to (a,b) => a + b
29 |
30 | /*
31 | 1. MyList: replace all FunctionX calls with lambdas
32 | 2. Rewrite the "special" adder as an anonymous function
33 | */
34 |
35 | val superAdd = (x: Int) => (y: Int) => x + y
36 | println(superAdd(3)(4))
37 | }
38 |
--------------------------------------------------------------------------------
/src/lectures/part3fp/HOFsCurries.scala:
--------------------------------------------------------------------------------
1 | package lectures.part3fp
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object HOFsCurries extends App {
7 |
8 | val superFunction: (Int, (String, (Int => Boolean)) => Int) => (Int => Int) = null
9 | // higher order function (HOF)
10 |
11 | // map, flatMap, filter in MyList
12 |
13 | // function that applies a function n times over a value x
14 | // nTimes(f, n, x)
15 | // nTimes(f, 3, x) = f(f(f(x))) = nTimes(f, 2, f(x)) = f(f(f(x)))
16 | // nTimes(f, n, x) = f(f(...f(x))) = nTimes(f, n-1, f(x))
17 | def nTimes(f: Int => Int, n: Int, x: Int): Int =
18 | if (n <= 0) x
19 | else nTimes(f, n-1, f(x))
20 |
21 | val plusOne = (x: Int) => x + 1
22 | println(nTimes(plusOne, 10, 1))
23 |
24 | // ntb(f,n) = x => f(f(f...(x)))
25 | // increment10 = ntb(plusOne, 10) = x => plusOne(plusOne....(x))
26 | // val y = increment10(1)
27 | def nTimesBetter(f: Int => Int, n: Int): (Int => Int) =
28 | if (n <= 0) (x: Int) => x
29 | else (x: Int) => nTimesBetter(f, n-1)(f(x))
30 |
31 | val plus10 = nTimesBetter(plusOne, 10)
32 | println(plus10(1))
33 |
34 | // curried functions
35 | val superAdder: Int => (Int => Int) = (x: Int) => (y: Int) => x + y
36 | val add3 = superAdder(3) // y => 3 + y
37 | println(add3(10))
38 | println(superAdder(3)(10))
39 |
40 | // functions with multiple parameter lists
41 | def curriedFormatter(c: String)(x: Double): String = c.format(x)
42 |
43 | val standardFormat: (Double => String) = curriedFormatter("%4.2f")
44 | val preciseFormat: (Double => String) = curriedFormatter("%10.8f")
45 |
46 | println(standardFormat(Math.PI))
47 | println(preciseFormat(Math.PI))
48 |
49 | /*
50 | 1. Expand MyList
51 | - foreach method A => Unit
52 | [1,2,3].foreach(x => println(x))
53 |
54 | - sort function ((A, A) => Int) => MyList
55 | [1,2,3].sort((x, y) => y - x) => [3,2,1]
56 |
57 | - zipWith (list, (A, A) => B) => MyList[B]
58 | [1,2,3].zipWith([4,5,6], x * y) => [1 * 4, 2 * 5, 3 * 6] = [4,10,18]
59 |
60 | - fold(start)(function) => a value
61 | [1,2,3].fold(0)(x + y) = 6
62 |
63 | 2. toCurry(f: (Int, Int) => Int) => (Int => Int => Int)
64 | fromCurry(f: (Int => Int => Int)) => (Int, Int) => Int
65 |
66 | 3. compose(f,g) => x => f(g(x))
67 | andThen(f,g) => x => g(f(x))
68 | */
69 |
70 | def toCurry(f: (Int, Int) => Int): (Int => Int => Int) =
71 | x => y => f(x, y)
72 |
73 | def fromCurry(f: (Int => Int => Int)): (Int, Int) => Int =
74 | (x, y) => f(x)(y)
75 |
76 | // FunctionX
77 | def compose[A,B,T](f: A => B, g: T => A): T => B =
78 | x => f(g(x))
79 |
80 | def andThen[A,B,C](f: A => B, g: B => C): A => C =
81 | x => g(f(x))
82 |
83 | def superAdder2: (Int => Int => Int) = toCurry(_ + _)
84 | def add4 = superAdder2(4)
85 | println(add4(17))
86 |
87 | val simpleAdder = fromCurry(superAdder)
88 | println(simpleAdder(4,17))
89 |
90 | val add2 = (x: Int) => x + 2
91 | val times3 = (x: Int) => x * 3
92 |
93 | val composed = compose(add2, times3)
94 | val ordered = andThen(add2, times3)
95 |
96 | println(composed(4))
97 | println(ordered(4))
98 | }
99 |
--------------------------------------------------------------------------------
/src/lectures/part3fp/HandlingFailure.scala:
--------------------------------------------------------------------------------
1 | package lectures.part3fp
2 |
3 | import scala.util.{Random, Try, Failure, Success}
4 |
5 | /**
6 | * Created by Daniel.
7 | */
8 | object HandlingFailure extends App {
9 |
10 | // create success and failure
11 | val aSuccess = Success(3)
12 | val aFailure = Failure(new RuntimeException("SUPER FAILURE"))
13 |
14 | println(aSuccess)
15 | println(aFailure)
16 |
17 | def unsafeMethod(): String = throw new RuntimeException("NO STRING FOR YOU BUSTER")
18 |
19 | // Try objects via the apply method
20 | val potentialFailure = Try(unsafeMethod())
21 | println(potentialFailure)
22 |
23 | // syntax sugar
24 | val anotherPotentialFailure = Try {
25 | // code that might throw
26 | }
27 |
28 | // utilities
29 | println(potentialFailure.isSuccess)
30 |
31 | // orElse
32 | def backupMethod(): String = "A valid result"
33 | val fallbackTry = Try(unsafeMethod()).orElse(Try(backupMethod()))
34 | println(fallbackTry)
35 |
36 | // IF you design the API
37 | def betterUnsafeMethod(): Try[String] = Failure(new RuntimeException)
38 | def betterBackupMethod(): Try[String] = Success("A valid result")
39 | val betterFallback = betterUnsafeMethod() orElse betterBackupMethod()
40 |
41 | // map, flatMap, filter
42 | println(aSuccess.map(_ * 2))
43 | println(aSuccess.flatMap(x => Success(x * 10)))
44 | println(aSuccess.filter(_ > 10))
45 | // => for-comprehensions
46 |
47 | /*
48 | Exercise
49 | */
50 | val host = "localhost"
51 | val port = "8080"
52 | def renderHTML(page: String) = println(page)
53 |
54 | class Connection {
55 | def get(url: String): String = {
56 | val random = new Random(System.nanoTime())
57 | if (random.nextBoolean()) "..."
58 | else throw new RuntimeException("Connection interrupted")
59 | }
60 |
61 | def getSafe(url: String): Try[String] = Try(get(url))
62 | }
63 |
64 | object HttpService {
65 | val random = new Random(System.nanoTime())
66 |
67 | def getConnection(host: String, port: String): Connection =
68 | if (random.nextBoolean()) new Connection
69 | else throw new RuntimeException("Someone else took the port")
70 |
71 | def getSafeConnection(host: String, port: String): Try[Connection] = Try(getConnection(host, port))
72 | }
73 |
74 | // if you get the html page from the connection, print it to the console i.e. call renderHTML
75 | val possibleConnection = HttpService.getSafeConnection(host, port)
76 | val possibleHTML = possibleConnection.flatMap(connection => connection.getSafe("/home"))
77 | possibleHTML.foreach(renderHTML)
78 |
79 | // shorthand version
80 | HttpService.getSafeConnection(host, port)
81 | .flatMap(connection => connection.getSafe("/home"))
82 | .foreach(renderHTML)
83 |
84 | // for-comprehension version
85 | for {
86 | connection <- HttpService.getSafeConnection(host, port)
87 | html <- connection.getSafe("/home")
88 | } renderHTML(html)
89 |
90 | /*
91 | try {
92 | connection = HttpService.getConnection(host, port)
93 | try {
94 | page = connection.get("/home")
95 | renderHTML(page)
96 | } catch (some other exception) {
97 |
98 | }
99 | } catch (exception) {
100 |
101 | }
102 | */
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/src/lectures/part3fp/MapFlatmapFilterFor.scala:
--------------------------------------------------------------------------------
1 | package lectures.part3fp
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object MapFlatmapFilterFor extends App {
7 |
8 | val list = List(1,2,3)
9 | println(list.head)
10 | println(list.tail)
11 |
12 | // map
13 | println(list.map(_ + 1))
14 | println(list.map(_ + " is a number"))
15 |
16 | // filter
17 | println(list.filter(_ % 2 == 0))
18 |
19 | // flatMap
20 | val toPair = (x: Int) => List(x, x+1)
21 | println(list.flatMap(toPair))
22 |
23 | // print all combinations between two lists
24 | val numbers = List(1,2,3,4)
25 | val chars = List('a','b','c','d')
26 | val colors = List("black", "white")
27 |
28 | // List("a1", "a2"... "d4")
29 |
30 | // "iterating"
31 | val combinations = numbers.filter(_ % 2 == 0).flatMap(n => chars.flatMap(c => colors.map(color => "" + c + n + "-" + color)))
32 | println(combinations)
33 |
34 |
35 | // foreach
36 | list.foreach(println)
37 |
38 | // for-comprehensions
39 | val forCombinations = for {
40 | n <- numbers if n % 2 == 0
41 | c <- chars
42 | color <- colors
43 | } yield "" + c + n + "-" + color
44 | println(forCombinations)
45 |
46 | for {
47 | n <- numbers
48 | } println(n)
49 |
50 | // syntax overload
51 | list.map { x =>
52 | x * 2
53 | }
54 |
55 | /*
56 | 1. MyList supports for comprehensions?
57 | map(f: A => B) => MyList[B]
58 | filter(p: A => Boolean) => MyList[A]
59 | flatMap(f: A => MyList[B]) => MyList[B]
60 | 2. A small collection of at most ONE element - Maybe[+T]
61 | - map, flatMap, filter
62 | */
63 |
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/lectures/part3fp/Options.scala:
--------------------------------------------------------------------------------
1 | package lectures.part3fp
2 |
3 | import java.util.Random
4 |
5 | /**
6 | * Created by Daniel.
7 | */
8 | object Options extends App {
9 |
10 | val myFirstOption: Option[Int] = Some(4)
11 | val noOption: Option[Int] = None
12 |
13 | println(myFirstOption)
14 |
15 | // WORK with unsafe APIs
16 | def unsafeMethod(): String = null
17 | // val result = Some(null) // WRONG
18 | val result = Option(unsafeMethod()) // Some or None
19 | println(result)
20 |
21 | // chained methods
22 | def backupMethod(): String = "A valid result"
23 | val chainedResult = Option(unsafeMethod()).orElse(Option(backupMethod()))
24 |
25 | // DESIGN unsafe APIs
26 | def betterUnsafeMethod(): Option[String] = None
27 | def betterBackupMethod(): Option[String] = Some("A valid result")
28 | val betterChainedResult = betterUnsafeMethod() orElse betterBackupMethod()
29 |
30 |
31 | // functions on Options
32 | println(myFirstOption.isEmpty)
33 | println(myFirstOption.get) // USAFE - DO NOT USE THIS
34 |
35 | // map, flatMap, filter
36 | println(myFirstOption.map(_ * 2))
37 | println(myFirstOption.filter(x => x > 10))
38 | println(myFirstOption.flatMap(x => Option(x * 10)))
39 |
40 | // for-comprehensions
41 |
42 | /*
43 | Exercise.
44 | */
45 | val config: Map[String, String] = Map(
46 | // fetched from elsewhere
47 | "host" -> "176.45.36.1",
48 | "port" -> "80"
49 | )
50 |
51 | class Connection {
52 | def connect = "Connected" // connect to some server
53 | }
54 | object Connection {
55 | val random = new Random(System.nanoTime())
56 |
57 | def apply(host: String, port: String): Option[Connection] =
58 | if (random.nextBoolean()) Some(new Connection)
59 | else None
60 | }
61 |
62 | // try to establish a connection, if so - print the connect method
63 | val host = config.get("host")
64 | val port = config.get("port")
65 | /*
66 | if (h != null)
67 | if (p != null)
68 | return Connection.apply(h, p)
69 |
70 | return null
71 | */
72 | val connection = host.flatMap(h => port.flatMap(p => Connection.apply(h, p)))
73 | /*
74 | if (c != null)
75 | return c.connect
76 | return null
77 | */
78 | val connectionStatus = connection.map(c => c.connect)
79 | // if (connectionStatus == null) println(None) else print (Some(connectionstatus.get))
80 | println(connectionStatus)
81 | /*
82 | if (status != null)
83 | println(status)
84 | */
85 | connectionStatus.foreach(println)
86 |
87 |
88 | // chained calls
89 | config.get("host")
90 | .flatMap(host => config.get("port")
91 | .flatMap(port => Connection(host, port))
92 | .map(connection => connection.connect))
93 | .foreach(println)
94 |
95 | // for-comprehensions
96 | val forConnectionStatus = for {
97 | host <- config.get("host")
98 | port <- config.get("port")
99 | connection <- Connection(host, port)
100 | } yield connection.connect
101 | forConnectionStatus.foreach(println)
102 |
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/src/lectures/part3fp/Sequences.scala:
--------------------------------------------------------------------------------
1 | package lectures.part3fp
2 |
3 | import scala.util.Random
4 |
5 | /**
6 | * Created by Daniel.
7 | */
8 | object Sequences extends App {
9 |
10 | // Seq
11 | val aSequence = Seq(1,3,2,4)
12 | println(aSequence)
13 | println(aSequence.reverse)
14 | println(aSequence(2))
15 | println(aSequence ++ Seq(7,5,6))
16 | println(aSequence.sorted)
17 |
18 | // Ranges
19 | val aRange: Seq[Int] = 1 until 10
20 | aRange.foreach(println)
21 |
22 | (1 to 10).foreach(x => println("Hello"))
23 |
24 | // lists
25 | val aList = List(1,2,3)
26 | val prepended = 42 +: aList :+ 89
27 | println(prepended)
28 |
29 | val apples5 = List.fill(5)("apple")
30 | println(apples5)
31 | println(aList.mkString("-|-"))
32 |
33 | // arrays
34 | val numbers = Array(1,2,3,4)
35 | val threeElements = Array.ofDim[String](3)
36 | threeElements.foreach(println)
37 |
38 | // mutation
39 | numbers(2) = 0 // syntax sugar for numbers.update(2, 0)
40 | println(numbers.mkString(" "))
41 |
42 | // arrays and seq
43 | val numbersSeq: Seq[Int] = numbers // implicit conversion
44 | println(numbersSeq)
45 |
46 | // vectors
47 | val vector: Vector[Int] = Vector(1,2,3)
48 | println(vector)
49 |
50 | // vectors vs lists
51 |
52 | val maxRuns = 1000
53 | val maxCapacity = 1000000
54 |
55 | def getWriteTime(collection: Seq[Int]): Double = {
56 | val r = new Random
57 | val times = for {
58 | it <- 1 to maxRuns
59 | } yield {
60 | val currentTime = System.nanoTime()
61 | collection.updated(r.nextInt(maxCapacity), r.nextInt())
62 | System.nanoTime() - currentTime
63 | }
64 |
65 | times.sum * 1.0 / maxRuns
66 | }
67 |
68 | val numbersList = (1 to maxCapacity).toList
69 | val numbersVector = (1 to maxCapacity).toVector
70 |
71 | // keeps reference to tail
72 | // updating an element in the middle takes long
73 | println(getWriteTime(numbersList))
74 | // depth of the tree is small
75 | // needs to replace an entire 32-element chunk
76 | println(getWriteTime(numbersVector))
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/lectures/part3fp/TuplesAndMaps.scala:
--------------------------------------------------------------------------------
1 | package lectures.part3fp
2 |
3 | import scala.annotation.tailrec
4 |
5 | /**
6 | * Created by Daniel.
7 | */
8 | object TuplesAndMaps extends App {
9 |
10 | // tuples = finite ordered "lists"
11 | val aTuple = (2, "hello, Scala") // Tuple2[Int, String] = (Int, String)
12 |
13 | println(aTuple._1) // 2
14 | println(aTuple.copy(_2 = "goodbye Java"))
15 | println(aTuple.swap) // ("hello, Scala", 2)
16 |
17 | // Maps - keys -> values
18 | val aMap: Map[String, Int] = Map()
19 |
20 | val phonebook = Map(("Jim", 555), "Daniel" -> 789, ("JIM", 9000)).withDefaultValue(-1)
21 | // a -> b is sugar for (a, b)
22 | println(phonebook)
23 |
24 | // map ops
25 | println(phonebook.contains("Jim"))
26 | println(phonebook("Mary"))
27 |
28 | // add a pairing
29 | val newPairing = "Mary" -> 678
30 | val newPhonebook = phonebook + newPairing
31 | println(newPhonebook)
32 |
33 | // functionals on maps
34 | // map, flatMap, filter
35 | println(phonebook.map(pair => pair._1.toLowerCase -> pair._2))
36 |
37 | // filterKeys
38 | println(phonebook.filterKeys(x => x.startsWith("J")))
39 | // mapValues
40 | println(phonebook.mapValues(number => "0245-" + number))
41 |
42 | // conversions to other collections
43 | println(phonebook.toList)
44 | println(List(("Daniel", 555)).toMap)
45 | val names = List("Bob", "James", "Angela", "Mary", "Daniel", "Jim")
46 | println(names.groupBy(name => name.charAt(0)))
47 |
48 | /*
49 | 1. What would happen if I had two original entries "Jim" -> 555 and "JIM" -> 900
50 |
51 | !!! careful with mapping keys.
52 |
53 | 2. Overly simplified social network based on maps
54 | Person = String
55 | - add a person to the network
56 | - remove
57 | - friend (mutual)
58 | - unfriend
59 |
60 | - number of friends of a person
61 | - person with most friends
62 | - how many people have NO friends
63 | - if there is a social connection between two people (direct or not)
64 | */
65 | def add(network: Map[String, Set[String]], person: String): Map[String, Set[String]] =
66 | network + (person -> Set())
67 |
68 | def friend(network: Map[String, Set[String]], a: String, b: String): Map[String, Set[String]] = {
69 | val friendsA = network(a)
70 | val friendsB = network(b)
71 |
72 | network + (a -> (friendsA + b)) + (b -> (friendsB + a))
73 | }
74 |
75 | def unfriend(network: Map[String, Set[String]], a: String, b: String): Map[String, Set[String]] = {
76 | val friendsA = network(a)
77 | val friendsB = network(b)
78 |
79 | network + (a -> (friendsA - b)) + (b -> (friendsB - a))
80 | }
81 |
82 | def remove(network: Map[String, Set[String]], person: String): Map[String, Set[String]] = {
83 | def removeAux(friends: Set[String], networkAcc: Map[String, Set[String]]): Map[String, Set[String]] =
84 | if (friends.isEmpty) networkAcc
85 | else removeAux(friends.tail, unfriend(networkAcc, person, friends.head))
86 |
87 | val unfriended = removeAux(network(person), network)
88 | unfriended - person
89 | }
90 |
91 | val empty: Map[String, Set[String]] = Map()
92 | val network = add(add(empty, "Bob"), "Mary")
93 | println(network)
94 | println(friend(network, "Bob", "Mary"))
95 | println(unfriend(friend(network, "Bob", "Mary"), "Bob", "Mary"))
96 | println(remove(friend(network, "Bob", "Mary"), "Bob"))
97 |
98 | // Jim,Bob,Mary
99 | val people = add(add(add(empty, "Bob"), "Mary"), "Jim")
100 | val jimBob = friend(people, "Bob", "Jim")
101 | val testNet = friend(jimBob, "Bob", "Mary")
102 |
103 | println(testNet)
104 |
105 | def nFriends(network: Map[String, Set[String]], person: String): Int =
106 | if (!network.contains(person)) 0
107 | else network(person).size
108 |
109 | println(nFriends(testNet, "Bob"))
110 |
111 | def mostFriends(network: Map[String, Set[String]]): String =
112 | network.maxBy(pair => pair._2.size)._1
113 |
114 | println(mostFriends(testNet))
115 |
116 | def nPeopleWithNoFriends(network: Map[String, Set[String]]): Int =
117 | network.count(_._2.isEmpty)
118 |
119 | println(nPeopleWithNoFriends(testNet))
120 |
121 | def socialConnection(network: Map[String, Set[String]], a: String, b: String): Boolean = {
122 | @tailrec
123 | def bfs(target: String, consideredPeople: Set[String], discoveredPeople: Set[String]): Boolean = {
124 | if (discoveredPeople.isEmpty) false
125 | else {
126 | val person = discoveredPeople.head
127 | if (person == target) true
128 | else if (consideredPeople.contains(person)) bfs(target, consideredPeople, discoveredPeople.tail)
129 | else bfs(target, consideredPeople + person, discoveredPeople.tail ++ network(person))
130 | }
131 | }
132 |
133 | bfs(b, Set(), network(a) + a)
134 | }
135 |
136 | println(socialConnection(testNet, "Mary", "Jim"))
137 | println(socialConnection(network, "Mary", "Bob"))
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/src/lectures/part3fp/WhatsAFunction.scala:
--------------------------------------------------------------------------------
1 | package lectures.part3fp
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object WhatsAFunction extends App {
7 |
8 | // DREAM: use functions as first class elements
9 | // problem: oop
10 |
11 | val doubler = new MyFunction[Int, Int] {
12 | override def apply(element: Int): Int = element * 2
13 | }
14 |
15 | println(doubler(2))
16 |
17 | // function types = Function1[A, B]
18 | val stringToIntConverter = new Function1[String, Int] {
19 | override def apply(string: String): Int = string.toInt
20 | }
21 |
22 | println(stringToIntConverter("3") + 4)
23 |
24 | val adder: ((Int, Int) => Int) = new Function2[Int, Int, Int] {
25 | override def apply(a: Int, b: Int): Int = a + b
26 | }
27 |
28 | // Function types Function2[A, B, R] === (A,B) => R
29 |
30 | // ALL SCALA FUNCTIONS ARE OBJECTS
31 |
32 | /*
33 | 1. a function which takes 2 strings and concatenates them
34 | 2. transform the MyPredicate and MyTransformer into function types
35 | 3. define a function which takes an int and returns another function which takes an int and returns an int
36 | - what's the type of this function
37 | - how to do it
38 | */
39 |
40 | def concatenator: (String, String) => String = new Function2[String, String, String] {
41 | override def apply(a: String, b: String): String = a + b
42 | }
43 | println(concatenator("Hello ", "Scala"))
44 |
45 | // Function1[Int, Function1[Int, Int]]
46 | val superAdder: Function1[Int, Function1[Int, Int]] = new Function1[Int, Function1[Int, Int]] {
47 | override def apply(x: Int): Function1[Int, Int] = new Function1[Int, Int] {
48 | override def apply(y: Int): Int = x + y
49 | }
50 | }
51 |
52 | val adder3 = superAdder(3)
53 | println(adder3(4))
54 | println(superAdder(3)(4)) // curried function
55 |
56 | }
57 |
58 | trait MyFunction[A, B] {
59 | def apply(element: A): B
60 | }
--------------------------------------------------------------------------------
/src/lectures/part4pm/AllThePatterns.scala:
--------------------------------------------------------------------------------
1 | package lectures.part4pm
2 |
3 | import exercises.{Empty, Cons, MyList}
4 |
5 | /**
6 | * Created by Daniel.
7 | */
8 | object AllThePatterns extends App {
9 | //
10 | // // 1 - constants
11 | // val x: Any = "Scala"
12 | // val constants = x match {
13 | // case 1 => "a number"
14 | // case "Scala" => "THE Scala"
15 | // case true => "The Truth"
16 | // case AllThePatterns => "A singleton object"
17 | // }
18 | //
19 | // // 2 - match anything
20 | // // 2.1 wildcard
21 | // val matchAnything = x match {
22 | // case _ =>
23 | // }
24 | //
25 | // // 2.2 variable
26 | // val matchAVariable = x match {
27 | // case something => s"I've found $something"
28 | // }
29 | //
30 | // // 3 - tuples
31 | // val aTuple = (1,2)
32 | // val matchATuple = aTuple match {
33 | // case (1, 1) =>
34 | // case (something, 2) => s"I've found $something"
35 | // }
36 | //
37 | // val nestedTuple = (1, (2, 3))
38 | // val matchANestedTuple = nestedTuple match {
39 | // case (_, (2, v)) =>
40 | // }
41 | // // PMs can be NESTED!
42 | //
43 | // // 4 - case classes - constructor pattern
44 | // // PMs can be nested with CCs as well
45 | // val aList: MyList[Int] = Cons(1, Cons(2, Empty))
46 | // val matchAList = aList match {
47 | // case Empty =>
48 | // case Cons(head, Cons(subhead, subtail)) =>
49 | // }
50 | //
51 | // // 5 - list patterns
52 | // val aStandardList = List(1,2,3,42)
53 | // val standardListMatching = aStandardList match {
54 | // case List(1, _, _, _) => // extractor - advanced
55 | // case List(1, _*) => // list of arbitrary length - advanced
56 | // case 1 :: List(_) => // infix pattern
57 | // case List(1,2,3) :+ 42 => // infix pattern
58 | // }
59 | //
60 | // // 6 - type specifiers
61 | // val unknown: Any = 2
62 | // val unknownMatch = unknown match {
63 | // case list: List[Int] => // explicit type specifier
64 | // case _ =>
65 | // }
66 | //
67 | // // 7 - name binding
68 | // val nameBindingMatch = aList match {
69 | // case nonEmptyList @ Cons(_, _) => // name binding => use the name later(here)
70 | // case Cons(1, rest @ Cons(2, _)) => // name binding inside nested patterns
71 | // }
72 | //
73 | // // 8 - multi-patterns
74 | // val multipattern = aList match {
75 | // case Empty | Cons(0, _) => // compound pattern (multi-pattern)
76 | // }
77 | //
78 | // // 9 - if guards
79 | // val secondElementSpecial = aList match {
80 | // case Cons(_, Cons(specialElement, _)) if specialElement % 2 == 0 =>
81 | // }
82 |
83 | // ALL.
84 |
85 | /*
86 | Question.
87 | */
88 |
89 | val numbers = List(1,2,3)
90 | val numbersMatch = numbers match {
91 | case listOfStrings: List[String] => "a list of strings"
92 | case listOfNumbers: List[Int] => "a list of numbers"
93 | case _ => ""
94 | }
95 |
96 | println(numbersMatch)
97 | // JVM trick question
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/lectures/part4pm/PatternMatching.scala:
--------------------------------------------------------------------------------
1 | package lectures.part4pm
2 |
3 | import scala.util.Random
4 |
5 | /**
6 | * Created by Daniel.
7 | */
8 | object PatternMatching extends App {
9 |
10 | // switch on steroids
11 | val random = new Random
12 | val x = random.nextInt(10)
13 |
14 | val description = x match {
15 | case 1 => "the ONE"
16 | case 2 => "double or nothing"
17 | case 3 => "third time is the charm"
18 | case _ => "something else" // _ = WILDCARD
19 | }
20 |
21 | println(x)
22 | println(description)
23 |
24 | // 1. Decompose values
25 | case class Person(name: String, age: Int)
26 | val bob = Person("Bob", 20)
27 |
28 | val greeting = bob match {
29 | case Person(n, a) if a < 21 => s"Hi, my name is $n and I can't drink in the US"
30 | case Person(n, a) => s"Hi, my name is $n and I am $a years old"
31 | case _ => "I don't know who I am"
32 | }
33 | println(greeting)
34 |
35 | /*
36 | 1. cases are matched in order
37 | 2. what if no cases match? MatchError
38 | 3. type of the PM expressoion? unified type of all the types in all the cases
39 | 4. PM works really well with case classes*
40 |
41 | */
42 |
43 | // PM on sealed hierarchies
44 | sealed class Animal
45 | case class Dog(breed: String) extends Animal
46 | case class Parrot(greeting: String) extends Animal
47 |
48 | val animal: Animal = Dog("Terra Nova")
49 | animal match {
50 | case Dog(someBreed) => println(s"Matched a dog of the $someBreed breed")
51 | }
52 |
53 | // match everything
54 | val isEven = x match {
55 | case n if n % 2 == 0 => true
56 | case _ => false
57 | }
58 | // WHY?!
59 | val isEvenCond = if (x % 2 == 0) true else false // ?!
60 | val isEvenNormal = x % 2 == 0
61 |
62 | /*
63 | Exercise
64 | simple function uses PM
65 | takes an Expr => human readable form
66 |
67 | Sum(Number(2), Number(3)) => 2 + 3
68 | Sum(Number(2), Number(3), Number(4)) => 2 + 3 + 4
69 | Prod(Sum(Number(2), Number(1)), Number(3)) = (2 + 1) * 3
70 | Sum(Prod(Number(2), Number(1)), Number(3)) = 2 * 1 + 3
71 | */
72 | trait Expr
73 | case class Number(n: Int) extends Expr
74 | case class Sum(e1: Expr, e2: Expr) extends Expr
75 | case class Prod(e1: Expr, e2: Expr) extends Expr
76 |
77 | def show(e: Expr): String = e match {
78 | case Number(n) => s"$n"
79 | case Sum(e1, e2) => show(e1) + " + " + show(e2)
80 | case Prod(e1, e2) => {
81 | def maybeShowParentheses(exp: Expr) = exp match {
82 | case Prod(_, _) => show(exp)
83 | case Number(_) => show(exp)
84 | case _ => "(" + show(exp) + ")"
85 | }
86 |
87 | maybeShowParentheses(e1) + " * " + maybeShowParentheses(e2)
88 | }
89 | }
90 |
91 | println(show(Sum(Number(2), Number(3))))
92 | println(show(Sum(Sum(Number(2), Number(3)), Number(4))))
93 | println(show(Prod(Sum(Number(2), Number(1)), Number(3))))
94 | println(show(Prod(Sum(Number(2), Number(1)), Sum(Number(3), Number(4)))))
95 | println(show(Sum(Prod(Number(2), Number(1)), Number(3))))
96 | }
97 |
--------------------------------------------------------------------------------
/src/lectures/part4pm/PatternsEverywhere.scala:
--------------------------------------------------------------------------------
1 | package lectures.part4pm
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object PatternsEverywhere extends App {
7 |
8 | // big idea #1
9 | try {
10 | // code
11 | } catch {
12 | case e: RuntimeException => "runtime"
13 | case npe: NullPointerException => "npe"
14 | case _ => "something else"
15 | }
16 |
17 | // catches are actually MATCHES
18 | /*
19 | try {
20 | // code
21 | } catch (e) {
22 | e match {
23 | case e: RuntimeException => "runtime"
24 | case npe: NullPointerException => "npe"
25 | case _ => "something else"
26 | }
27 | }
28 | */
29 |
30 | // big idea #2
31 | val list = List(1,2,3,4)
32 | val evenOnes = for {
33 | x <- list if x % 2 == 0 // ?!
34 | } yield 10 * x
35 |
36 | // generators are also based on PATTERN MATCHING
37 | val tuples = List((1,2), (3,4))
38 | val filterTuples = for {
39 | (first, second) <- tuples
40 | } yield first * second
41 | // case classes, :: operators, ...
42 |
43 | // big idea #3
44 | val tuple = (1,2,3)
45 | val (a, b, c) = tuple
46 | println(b)
47 | // multiple value definitions based on PATTERN MATCHING
48 | // ALL THE POWER
49 |
50 | val head :: tail = list
51 | println(head)
52 | println(tail)
53 |
54 | // big idea #4 - NEW
55 | // partial function based on PATTERN MATCHING
56 | val mappedList = list.map {
57 | case v if v % 2 == 0 => v + " is even"
58 | case 1 => "the one"
59 | case _ => "something else"
60 | } // partial function literal
61 |
62 | val mappedList2 = list.map { x => x match {
63 | case v if v % 2 == 0 => v + " is even"
64 | case 1 => "the one"
65 | case _ => "something else"
66 | }
67 | }
68 | println(mappedList)
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/playground/Cinderella.scala:
--------------------------------------------------------------------------------
1 | package playground
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | class Cinderella {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/playground/JavaPlayground.java:
--------------------------------------------------------------------------------
1 | package playground;
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | public class JavaPlayground {
7 |
8 |
9 | public static void main(String args[]) {
10 | System.out.println(Person.N_EYES);
11 | }
12 | }
13 |
14 | class Person {
15 | public static final int N_EYES = 2;
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/src/playground/PrinceCharming.scala:
--------------------------------------------------------------------------------
1 | package playground
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | class PrinceCharming {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/playground/ScalaPlayground.scala:
--------------------------------------------------------------------------------
1 | package playground
2 |
3 | /**
4 | * Created by Daniel.
5 | */
6 | object ScalaPlayground extends App {
7 | println("Hello, Scala!")
8 | }
9 |
--------------------------------------------------------------------------------