├── .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 | 42 | 43 | 44 | 45 | 46 | 47 | 1.8 48 | 49 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 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 | --------------------------------------------------------------------------------