├── .gitignore ├── README.md ├── build.sbt ├── lecture slides ├── week 1 │ ├── 01 - Recap Functions and Pattern Matching.pdf │ ├── 02 - Recap Collectinos.pdf │ ├── 03 - Queries with For.pdf │ ├── 04 - Translation of For.pdf │ ├── 05 - Functional Random Generators.pdf │ └── 06 - Monads.pdf ├── week 2 │ ├── 01 - Structural Induction of Trees.pdf │ ├── 02 - Streams.pdf │ ├── 03 - Lazy Evaluation.pdf │ ├── 04 - Computing with Infinite Sequences.pdf │ └── 05 - Case Study.pdf └── week 3 │ ├── 01 - Functions and State.pdf │ ├── 02 - Identity and Change.pdf │ ├── 03 - Loops.pdf │ ├── 04 - Extended Example - Discrete Event Simulation.pdf │ ├── 05 - Discrete Event Simulation - API and Usage.pdf │ └── 06 - Discrete Event Simulation - Impl and Test.pdf └── src └── main └── scala ├── week0 └── HelloWorld.sc ├── week1 ├── books.sc ├── forexprs.sc └── generators.sc ├── week2 ├── Pouring.scala ├── infstreams.sc ├── lazyvals.sc ├── pouringtest.sc └── streams.sc ├── week3 ├── BankAccount.scala ├── account.sc └── dicreteEventSimulator │ ├── Circuits.scala │ ├── Gates.scala │ ├── Parameters.scala │ ├── Simulation.scala │ └── test.sc └── week4 ├── frp ├── BankAccount.scala ├── Signal.scala ├── StackableVariable.scala ├── Var.scala └── test.sc └── observer ├── BankAccount.scala ├── Publisher.scala └── test.sc /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache 6 | .history 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | 15 | # Scala-IDE specific 16 | .scala_dependencies 17 | .worksheet 18 | .cache-main 19 | .classpath 20 | .project 21 | .settings 22 | /bin/ 23 | 24 | # Intellij specific 25 | .idea 26 | 27 | # Mac OS specific 28 | .DS_Store 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # progfun2-code 2 | 3 | Crowd source for code used in the lectures of **Functional Program Design in Scala** 4 | 5 | Pull requests are welcome! 6 | 7 | ## Contributors 8 | 9 | - Week 1: [@leanton](https://github.com/leanton) [@PawelPamula](https://github.com/PawelPamula) 10 | - Week 2: [@leanton](https://github.com/leanton) 11 | - Week 3: [@leanton](https://github.com/leanton) 12 | - Week 4: [@alamit](https://github.com/alamit) 13 | - Week 5: TODO 14 | - Week 6: TODO 15 | 16 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "progfun2-code" 2 | 3 | version := "1.0" 4 | 5 | scalaVersion := "2.11.8" 6 | -------------------------------------------------------------------------------- /lecture slides/week 1/01 - Recap Functions and Pattern Matching.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 1/01 - Recap Functions and Pattern Matching.pdf -------------------------------------------------------------------------------- /lecture slides/week 1/02 - Recap Collectinos.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 1/02 - Recap Collectinos.pdf -------------------------------------------------------------------------------- /lecture slides/week 1/03 - Queries with For.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 1/03 - Queries with For.pdf -------------------------------------------------------------------------------- /lecture slides/week 1/04 - Translation of For.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 1/04 - Translation of For.pdf -------------------------------------------------------------------------------- /lecture slides/week 1/05 - Functional Random Generators.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 1/05 - Functional Random Generators.pdf -------------------------------------------------------------------------------- /lecture slides/week 1/06 - Monads.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 1/06 - Monads.pdf -------------------------------------------------------------------------------- /lecture slides/week 2/01 - Structural Induction of Trees.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 2/01 - Structural Induction of Trees.pdf -------------------------------------------------------------------------------- /lecture slides/week 2/02 - Streams.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 2/02 - Streams.pdf -------------------------------------------------------------------------------- /lecture slides/week 2/03 - Lazy Evaluation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 2/03 - Lazy Evaluation.pdf -------------------------------------------------------------------------------- /lecture slides/week 2/04 - Computing with Infinite Sequences.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 2/04 - Computing with Infinite Sequences.pdf -------------------------------------------------------------------------------- /lecture slides/week 2/05 - Case Study.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 2/05 - Case Study.pdf -------------------------------------------------------------------------------- /lecture slides/week 3/01 - Functions and State.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 3/01 - Functions and State.pdf -------------------------------------------------------------------------------- /lecture slides/week 3/02 - Identity and Change.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 3/02 - Identity and Change.pdf -------------------------------------------------------------------------------- /lecture slides/week 3/03 - Loops.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 3/03 - Loops.pdf -------------------------------------------------------------------------------- /lecture slides/week 3/04 - Extended Example - Discrete Event Simulation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 3/04 - Extended Example - Discrete Event Simulation.pdf -------------------------------------------------------------------------------- /lecture slides/week 3/05 - Discrete Event Simulation - API and Usage.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 3/05 - Discrete Event Simulation - API and Usage.pdf -------------------------------------------------------------------------------- /lecture slides/week 3/06 - Discrete Event Simulation - Impl and Test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengyun/progfun2-code/aa1e30338be5ce78d655d4da8f847efe99ea9ee0/lecture slides/week 3/06 - Discrete Event Simulation - Impl and Test.pdf -------------------------------------------------------------------------------- /src/main/scala/week0/HelloWorld.sc: -------------------------------------------------------------------------------- 1 | object HelloWorld { 2 | val greeting = "Hello, Functional Program Design in Scala" 3 | print(greeting) 4 | } -------------------------------------------------------------------------------- /src/main/scala/week1/books.sc: -------------------------------------------------------------------------------- 1 | object books { 2 | 3 | case class Book(title: String, authors: List[String]) 4 | 5 | val books = Set( 6 | Book( 7 | title = "Structure and Interpretation of Computer Programs", 8 | authors = List("Abelson, Harald", "Sussman, Gerald J.")), 9 | Book( 10 | title = "Introduction to Functional Programming", 11 | authors = List("Bird, Richard", "Wadler, Phil")), 12 | Book( 13 | title = "Effective Java", 14 | authors = List("Bloch, Joshua")), 15 | Book( 16 | title = "Effective Java 2", 17 | authors = List("Bloch, Joshua")), 18 | Book( 19 | title = "Java Puzzlers", 20 | authors = List("Bloch, Joshua", "Gafter, Neal")), 21 | Book( 22 | title = "Programming in Scala", 23 | authors = List("Odersky, Martin", "Spoon, Lex", "Venners, Bill"))) 24 | 25 | 26 | for (b <- books; a <- b.authors if a startsWith "Bloch, ") 27 | yield b.title 28 | 29 | for { 30 | b1 <- books 31 | b2 <- books 32 | if b1.title < b2.title 33 | a1 <- b1.authors 34 | a2 <- b2.authors 35 | if a1 == a2 36 | } yield a1 37 | } -------------------------------------------------------------------------------- /src/main/scala/week1/forexprs.sc: -------------------------------------------------------------------------------- 1 | object forexprs { 2 | def mapFun[T, U](xs: List[T], f: T => U): List[U] = 3 | for (x <- xs) yield f(x) 4 | 5 | def flatMap[T, U](xs: List[T], f: T => Iterable[U]): List[U] = 6 | for (x <- xs; y <- f(x)) yield y 7 | 8 | def filter[T](xs: List[T], p: T => Boolean): List[T] = 9 | for (x <- xs if p(x)) yield x 10 | 11 | 12 | case class Book(title: String, authors: List[String]) 13 | 14 | val books = Set( 15 | Book( 16 | title = "Structure and Interpretation of Computer Programs", 17 | authors = List("Abelson, Harald", "Sussman, Gerald J.")), 18 | Book( 19 | title = "Introduction to Functional Programming", 20 | authors = List("Bird, Richard", "Wadler, Phil")), 21 | Book( 22 | title = "Effective Java", 23 | authors = List("Bloch, Joshua")), 24 | Book( 25 | title = "Effective Java 2", 26 | authors = List("Bloch, Joshua")), 27 | Book( 28 | title = "Java Puzzlers", 29 | authors = List("Bloch, Joshua", "Gafter, Neal")), 30 | Book( 31 | title = "Programming in Scala", 32 | authors = List("Odersky, Martin", "Spoon, Lex", "Venners, Bill"))) 33 | 34 | // Original 35 | for (b <- books; a <- b.authors if a startsWith "Bird") yield b.title 36 | 37 | 38 | // First substitution 39 | books.flatMap(b => for (a <- b.authors if a startsWith "Bird") yield b.title) 40 | 41 | // Second substitution 42 | books.flatMap(b => for (a <- b.authors.withFilter(a => a startsWith "Bird")) yield b.title) 43 | 44 | // Third substitution 45 | books.flatMap(b => b.authors.withFilter(a => a startsWith "Bird").map(_ => b.title)) 46 | } -------------------------------------------------------------------------------- /src/main/scala/week1/generators.sc: -------------------------------------------------------------------------------- 1 | object generators { 2 | trait Generator[+T] { 3 | self => 4 | 5 | def generate: T 6 | 7 | def map[S](f: T => S): Generator[S] = new Generator[S] { 8 | override def generate: S = f(self.generate) 9 | } 10 | 11 | def flatMap[S](f: T => Generator[S]): Generator[S] = new Generator[S] { 12 | override def generate: S = f(self.generate).generate 13 | } 14 | } 15 | 16 | val integers = new Generator[Int] { 17 | val rand = new java.util.Random 18 | 19 | override def generate: Int = rand.nextInt 20 | } 21 | 22 | val booleans = for (x <- integers) yield x > 0 23 | 24 | def pairs[T, U](t: Generator[T], u: Generator[U]) = for { 25 | x <- t 26 | y <- u 27 | } yield (x, y) 28 | 29 | def single[T](x: T): Generator[T] = new Generator[T] { 30 | override def generate: T = x 31 | } 32 | 33 | def choose(lo: Int, hi: Int): Generator[Int] = 34 | for (x <- integers) yield lo + Math.abs(x) % (hi - lo) 35 | 36 | def oneOf[T](xs: T*): Generator[T] = 37 | for (idx <- choose(0, xs.length)) yield xs(idx) 38 | 39 | def lists: Generator[List[Int]] = for { 40 | isEmpty <- booleans 41 | list <- if (isEmpty) emptyLists else nonEmptyLists 42 | } yield list 43 | 44 | def emptyLists = single(Nil) 45 | 46 | def nonEmptyLists = for { 47 | head <- integers 48 | tail <- lists 49 | } yield head :: tail 50 | 51 | 52 | trait Tree 53 | 54 | case class Inner(left: Tree, right: Tree) extends Tree 55 | 56 | case class Leaf(x: Int) extends Tree 57 | 58 | def leafs: Generator[Leaf] = for { 59 | x <- integers 60 | } yield Leaf(x) 61 | 62 | def inners: Generator[Inner] = for { 63 | l <- trees 64 | r <- trees 65 | } yield Inner(l, r) 66 | 67 | def trees: Generator[Tree] = for { 68 | isLeaf <- booleans 69 | tree <- if (isLeaf) leafs else inners 70 | } yield tree 71 | 72 | def test[T](r: Generator[T], noTimes: Int = 100)(test: T => Boolean): Unit = { 73 | for (_ <- 0 until noTimes) { 74 | val value = r.generate 75 | assert(test(value), "Test failed for: " + value) 76 | } 77 | println("Test passed " + noTimes + " times") 78 | } 79 | 80 | test(pairs(lists, lists)) { 81 | case (xs, ys) => (xs ++ ys).length > xs.length 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/scala/week2/Pouring.scala: -------------------------------------------------------------------------------- 1 | package week2 2 | 3 | class Pouring(capacity: Vector[Int]) { 4 | 5 | // States 6 | 7 | type State = Vector[Int] 8 | val initialState = capacity map (x => 0) 9 | 10 | // Moves 11 | 12 | trait Move { 13 | def change(state: State): State 14 | } 15 | 16 | case class Empty(glass: Int) extends Move { 17 | def change(state: State) = state updated(glass, 0) 18 | } 19 | 20 | case class Fill(glass: Int) extends Move { 21 | def change(state: State) = state updated(glass, capacity(glass)) 22 | } 23 | 24 | case class Pour(from: Int, to: Int) extends Move { 25 | def change(state: State) = { 26 | val amount = state(from) min (capacity(to) - state(to)) 27 | state updated(from, state(from) - amount) updated(to, state(to) + amount) 28 | } 29 | } 30 | 31 | val glasses = capacity.indices 32 | 33 | val moves = 34 | (for (g <- glasses) yield Empty(g)) ++ 35 | (for (g <- glasses) yield Fill(g)) ++ 36 | (for (from <- glasses; to <- glasses if from != to) yield Pour(from, to)) 37 | 38 | // Paths 39 | 40 | class Path(history: List[Move], val endState: State) { 41 | def extend(move: Move) = new Path(move :: history, move change endState) 42 | 43 | override def toString = (history.reverse mkString " ") + "--> " + endState 44 | } 45 | 46 | val initialPath = new Path(Nil, initialState) 47 | 48 | def from(paths: Set[Path], explored: Set[State]): Stream[Set[Path]] = 49 | if (paths.isEmpty) Stream.empty 50 | else { 51 | val more = for { 52 | path <- paths 53 | next <- moves map path.extend 54 | if !(explored contains next.endState) 55 | } yield next 56 | paths #:: from(more, explored ++ (more map (_.endState))) 57 | } 58 | 59 | val pathSets = from(Set(initialPath), Set(initialState)) 60 | 61 | def solutions(target: Int): Stream[Path] = 62 | for { 63 | pathSet <- pathSets 64 | path <- pathSet 65 | if path.endState contains target 66 | } yield path 67 | } 68 | -------------------------------------------------------------------------------- /src/main/scala/week2/infstreams.sc: -------------------------------------------------------------------------------- 1 | object infstreams { 2 | 3 | def from(n: Int): Stream[Int] = n #:: from(n + 1) 4 | 5 | 6 | val nats = from(0) 7 | val m4s = nats map (_ * 4) 8 | 9 | (m4s take 100).toList 10 | 11 | 12 | def sieve(s: Stream[Int]): Stream[Int] = 13 | s.head #:: sieve(s.tail filter (_ % s.head != 0)) 14 | 15 | val primes = sieve(from(2)) 16 | 17 | (primes take 100).toList 18 | 19 | 20 | def sqrtStream(x: Double): Stream[Double] = { 21 | def improve(guess: Double) = (guess + x / guess) / 2 22 | lazy val guesses: Stream[Double] = 1 #:: (guesses map improve) 23 | guesses 24 | } 25 | 26 | def isGoodEnough(guess: Double, x: Double) = 27 | math.abs((guess * guess - x) / x) < 0.0001 28 | 29 | (sqrtStream(4) filter (isGoodEnough(_, 4)) take 10).toList 30 | 31 | 32 | val N = 3 33 | val xs = from(1) map (_ * N) 34 | val ys = from(1) filter (_ % N == 0) 35 | } -------------------------------------------------------------------------------- /src/main/scala/week2/lazyvals.sc: -------------------------------------------------------------------------------- 1 | object lazyvals { 2 | 3 | def expr = { 4 | val x = { 5 | print("x"); 1 6 | } 7 | lazy val y = { 8 | print("y"); 2 9 | } 10 | def z = { 11 | print("z"); 3 12 | } 13 | z + y + x + z + y + x 14 | } 15 | 16 | expr 17 | 18 | } -------------------------------------------------------------------------------- /src/main/scala/week2/pouringtest.sc: -------------------------------------------------------------------------------- 1 | import week2.Pouring 2 | 3 | object pouringtest { 4 | 5 | val problem = new Pouring(Vector(4, 9)) 6 | problem.moves 7 | 8 | problem.solutions(6) 9 | } -------------------------------------------------------------------------------- /src/main/scala/week2/streams.sc: -------------------------------------------------------------------------------- 1 | object streams { 2 | val xs = Stream.cons(1, Stream.cons(2, Stream.empty)) 3 | Stream(1, 2, 3) 4 | 5 | 1 #:: 2 #:: Stream.empty 6 | 7 | (1 to 1000).toStream 8 | 9 | 10 | def streamRange(lo: Int, hi: Int): Stream[Int] = { 11 | print(lo + " ") 12 | if (lo >= hi) Stream.empty 13 | else Stream.cons(lo, streamRange(lo + 1, hi)) 14 | } 15 | 16 | def listRange(lo: Int, hi: Int): List[Int] = 17 | if (lo >= hi) Nil 18 | else lo :: listRange(lo + 1, hi) 19 | 20 | def isPrime(i: Int): Boolean = 21 | (2 until i) forall (i % _ != 0) 22 | 23 | ((1000 to 10000).toStream filter isPrime) (1) 24 | 25 | streamRange(1, 10).take(3).toList 26 | } -------------------------------------------------------------------------------- /src/main/scala/week3/BankAccount.scala: -------------------------------------------------------------------------------- 1 | package week3 2 | 3 | class BankAccount { 4 | 5 | private var balance = 0 6 | 7 | def deposit(amount: Int): Unit = { 8 | if (amount > 0) balance = balance + amount 9 | } 10 | 11 | def withdraw(amount: Int): Int = { 12 | if (0 < amount && amount <= balance) { 13 | balance -= amount 14 | balance 15 | } else throw new Error("insufficient funds") 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/scala/week3/account.sc: -------------------------------------------------------------------------------- 1 | import week3.BankAccount 2 | 3 | object account { 4 | val acct = new BankAccount 5 | acct.deposit(50) 6 | acct.withdraw(20) 7 | acct.withdraw(20) 8 | acct.withdraw(15) 9 | 10 | } -------------------------------------------------------------------------------- /src/main/scala/week3/dicreteEventSimulator/Circuits.scala: -------------------------------------------------------------------------------- 1 | package week3.dicreteEventSimulator 2 | 3 | import week3.discreteEventSimulator.Gates 4 | 5 | abstract class Circuits extends Gates { 6 | 7 | def halfAdder(a: Wire, b: Wire, s: Wire, c: Wire) { 8 | val d, e = new Wire 9 | orGate(a, b, d) 10 | andGate(a, b, c) 11 | inverter(c, e) 12 | andGate(d, e, s) 13 | } 14 | 15 | def fullAdder(a: Wire, b: Wire, cin: Wire, sum: Wire, cout: Wire) { 16 | val s, c1, c2 = new Wire 17 | halfAdder(a, cin, s, c1) 18 | halfAdder(b, s, sum, c2) 19 | orGate(c1, c2, cout) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/week3/dicreteEventSimulator/Gates.scala: -------------------------------------------------------------------------------- 1 | package week3.discreteEventSimulator 2 | 3 | abstract class Gates extends Simulation { 4 | 5 | def InverterDelay: Int 6 | 7 | def AndGateDelay: Int 8 | 9 | def OrGateDelay: Int 10 | 11 | class Wire { 12 | 13 | private var sigVal = false 14 | private var actions: List[Action] = List() 15 | 16 | def getSignal = sigVal 17 | 18 | def setSignal(s: Boolean) = 19 | if (s != sigVal) { 20 | sigVal = s 21 | actions foreach (_ ()) 22 | } 23 | 24 | def addAction(a: Action) = { 25 | actions = a :: actions 26 | a() 27 | } 28 | } 29 | 30 | def inverter(input: Wire, output: Wire): Unit = { 31 | def invertAction(): Unit = { 32 | val inputSig = input.getSignal 33 | afterDelay(InverterDelay) { 34 | output setSignal !inputSig 35 | } 36 | } 37 | input addAction invertAction 38 | } 39 | 40 | def andGate(in1: Wire, in2: Wire, output: Wire) = { 41 | def andAction() = { 42 | val in1Sig = in1.getSignal 43 | val in2Sig = in2.getSignal 44 | afterDelay(AndGateDelay) { 45 | output setSignal (in1Sig & in2Sig) 46 | } 47 | } 48 | in1 addAction andAction 49 | in2 addAction andAction 50 | } 51 | 52 | /** Design orGate analogously to andGate */ 53 | def orGateAlt(in1: Wire, in2: Wire, output: Wire): Unit = { 54 | def orAction() = { 55 | val in1Sig = in1.getSignal 56 | val in2Sig = in2.getSignal 57 | afterDelay(OrGateDelay) { 58 | output setSignal (in1Sig | in2Sig) 59 | } 60 | } 61 | in1 addAction orAction 62 | in2 addAction orAction 63 | } 64 | 65 | def probe(name: String, wire: Wire): Unit = { 66 | def probeAction(): Unit = { 67 | println(s"$name $currentTime value = ${wire.getSignal}") 68 | } 69 | wire addAction probeAction 70 | } 71 | 72 | /** Design orGate in terms of andGate, inverter */ 73 | def orGate(in1: Wire, in2: Wire, output: Wire): Unit = { 74 | val notIn1, notIn2, notOut = new Wire 75 | inverter(in1, notIn1) 76 | inverter(in2, notIn2) 77 | andGate(notIn1, notIn2, notOut) 78 | inverter(notOut, output) 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/scala/week3/dicreteEventSimulator/Parameters.scala: -------------------------------------------------------------------------------- 1 | package week3.dicreteEventSimulator 2 | 3 | trait Parameters { 4 | def InverterDelay = 2 5 | def AndGateDelay = 3 6 | def OrGateDelay = 5 7 | } -------------------------------------------------------------------------------- /src/main/scala/week3/dicreteEventSimulator/Simulation.scala: -------------------------------------------------------------------------------- 1 | package week3.discreteEventSimulator 2 | 3 | abstract class Simulation { 4 | 5 | type Action = () => Unit 6 | 7 | case class Event(time: Int, action: Action) 8 | 9 | private var curtime = 0 10 | 11 | def currentTime: Int = curtime 12 | 13 | private var agenda: List[Event] = List() 14 | 15 | private def insert(ag: List[Event], item: Event): List[Event] = ag match { 16 | case first :: rest if first.time <= item.time => first :: insert(rest, item) 17 | case _ => item :: ag 18 | } 19 | 20 | def afterDelay(delay: Int)(block: => Unit): Unit = { 21 | val item = Event(currentTime + delay, () => block) 22 | agenda = insert(agenda, item) 23 | } 24 | 25 | def run() { 26 | afterDelay(0) { 27 | println(s"*** simulation started, time = $currentTime ***") 28 | } 29 | loop() 30 | } 31 | 32 | private def loop(): Unit = agenda match { 33 | case first :: rest => 34 | agenda = rest 35 | curtime = first.time 36 | first.action() 37 | loop() 38 | case Nil => 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/scala/week3/dicreteEventSimulator/test.sc: -------------------------------------------------------------------------------- 1 | import week3.dicreteEventSimulator.{Circuits, Parameters} 2 | 3 | object test { 4 | object sim extends Circuits with Parameters 5 | import sim._ 6 | val in1, in2, sum, carry = new Wire 7 | halfAdder(in1, in2, sum, carry) 8 | probe("sum", sum) 9 | probe("carry", carry) 10 | 11 | in1.setSignal(true) 12 | run() 13 | 14 | in2.setSignal(true) 15 | run() 16 | 17 | in2.setSignal(false) 18 | run() 19 | } -------------------------------------------------------------------------------- /src/main/scala/week4/frp/BankAccount.scala: -------------------------------------------------------------------------------- 1 | package week4.frp 2 | 3 | class BankAccount { 4 | 5 | var balance = Var(0) 6 | 7 | def deposit(amount: Int): Unit = { 8 | if (amount > 0) { 9 | val b = balance() 10 | balance() = b + amount 11 | } 12 | } 13 | 14 | def withdraw(amount: Int): Unit = { 15 | if (0 < amount && amount <= balance()) { 16 | val b = balance() 17 | balance() = b - amount 18 | } else throw new Error("insufficient funds") 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/main/scala/week4/frp/Signal.scala: -------------------------------------------------------------------------------- 1 | package week4.frp 2 | 3 | import scala.util.DynamicVariable 4 | 5 | /** 6 | * Created by adrienlamit on 11/02/17. 7 | */ 8 | 9 | object Signal { 10 | private val caller = new DynamicVariable[Signal[_]](NoSignal) 11 | def apply[T](expr: => T): Signal[T] = new Signal(expr) 12 | } 13 | 14 | class Signal[T](expr: => T) { 15 | 16 | import Signal._ 17 | 18 | private var myExpr: () => T = _ 19 | private var myValue: T = _ 20 | private var observers: Set[Signal[_]] = Set.empty 21 | 22 | update(expr) 23 | 24 | protected def update(expr: => T): Unit = { 25 | myExpr = () => expr 26 | computeValue() 27 | } 28 | 29 | protected def computeValue(): Unit = { 30 | 31 | // What happens here is that : 32 | // - this is added to the callers stack 33 | // - if myExpr() is of type Signal[_] 34 | // then this is added to myExpr()'s 35 | // observers because myExpr().apply() 36 | // is called with this on top of the 37 | // caller stack. 38 | // Stack is useful for complex dependencies. 39 | // - if myExpr() is e.g a Int literal 40 | // then basically myExpr()'s observers will 41 | // add nothing because stack is empty 42 | // 43 | // See class StackableVariable[T]'s implementation 44 | // to convince yourself. 45 | val newValue = caller.withValue(this)(myExpr()) 46 | 47 | if (myValue != newValue) { 48 | myValue = newValue 49 | val obs = observers 50 | observers = Set.empty 51 | obs foreach (_.computeValue()) 52 | } 53 | } 54 | 55 | def apply(): T = { 56 | observers += caller.value 57 | assert(!caller.value.observers.contains(this), "cyclic signal definition") 58 | myValue 59 | } 60 | 61 | } 62 | 63 | object NoSignal extends Signal[Nothing](???) { 64 | override protected def computeValue(): Unit = () 65 | } -------------------------------------------------------------------------------- /src/main/scala/week4/frp/StackableVariable.scala: -------------------------------------------------------------------------------- 1 | package week4.frp 2 | 3 | /** 4 | * Created by adrienlamit on 26/02/17. 5 | */ 6 | class StackableVariable[T](init: T) { 7 | 8 | private var values: List[T] = List(init) 9 | 10 | def value: T = values.head 11 | 12 | def withValue[R](newValue: T)(op: => R): R = { 13 | values = newValue :: values 14 | try op finally values = values.tail 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/scala/week4/frp/Var.scala: -------------------------------------------------------------------------------- 1 | package week4.frp 2 | 3 | object Var { 4 | def apply[T](expr: => T): Var[T] = new Var(expr) 5 | } 6 | 7 | class Var[T](expr: => T) extends Signal[T](expr) { 8 | override def update(expr: => T): Unit = super.update(expr) 9 | } 10 | -------------------------------------------------------------------------------- /src/main/scala/week4/frp/test.sc: -------------------------------------------------------------------------------- 1 | import week4.frp.{BankAccount, Signal, Var} 2 | 3 | object test { 4 | 5 | def consolidated(accts: List[BankAccount]): Signal[Int] = { 6 | Signal(accts.map(_.balance()).sum) 7 | } 8 | 9 | val a = new BankAccount 10 | val b = new BankAccount 11 | val c = consolidated(List(a, b)) 12 | 13 | c() 14 | 15 | a deposit 20 16 | 17 | c() 18 | 19 | val v = Var(0) 20 | val x = Signal(v()) 21 | x() 22 | v() = 2 23 | x() 24 | 25 | } -------------------------------------------------------------------------------- /src/main/scala/week4/observer/BankAccount.scala: -------------------------------------------------------------------------------- 1 | package week4.observer 2 | 3 | class BankAccount extends Publisher { 4 | 5 | private var balance: Int = 0 6 | 7 | def currentBalance: Int = balance 8 | 9 | def deposit(amount: Int): Unit = 10 | if(amount > 0) { 11 | balance += amount 12 | publish() 13 | } 14 | else () 15 | 16 | def withdraw(amount: Int): Unit = { 17 | require(amount > 0 && amount <= balance) 18 | balance -= amount 19 | publish() 20 | } 21 | 22 | } 23 | 24 | class Consolidator(observed: List[BankAccount]) extends Subscriber { 25 | observed foreach (_.subscribe(this)) 26 | 27 | private var total: Int = _ 28 | compute() 29 | 30 | private def compute(): Unit = { 31 | total = observed map (_.currentBalance) sum 32 | } 33 | 34 | override def handler(publisher: Publisher): Unit = compute() 35 | 36 | def totalBalance = total 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/week4/observer/Publisher.scala: -------------------------------------------------------------------------------- 1 | package week4.observer 2 | 3 | 4 | trait Subscriber { 5 | def handler(publisher: Publisher): Unit 6 | } 7 | 8 | trait Publisher { 9 | 10 | private var subscribers: Set[Subscriber] = Set.empty 11 | 12 | def subscribe(subscriber: Subscriber): Unit = 13 | subscribers += subscriber 14 | 15 | def unsubscribe(subscriber: Subscriber): Unit = 16 | subscribers -= subscriber 17 | 18 | def publish(): Unit = 19 | subscribers foreach (_.handler(this)) 20 | } -------------------------------------------------------------------------------- /src/main/scala/week4/observer/test.sc: -------------------------------------------------------------------------------- 1 | import week4.observer.{BankAccount, Consolidator} 2 | 3 | object test { 4 | 5 | val a = new BankAccount 6 | val b = new BankAccount 7 | val c = new Consolidator(List(a, b)) 8 | 9 | c.totalBalance 10 | a deposit 20 11 | c.totalBalance 12 | b deposit 30 13 | c.totalBalance 14 | 15 | } --------------------------------------------------------------------------------