lucproglangcourse / lucproglangcourse.github.io

Lecture notes for COMP 371/471: Programming languages course at Loyola University Chicago with a focus on functional languages and projects mostly in Scala
MIT License
1 stars 2 forks source link

add countGroup example #5

Closed klaeufer closed 3 years ago

klaeufer commented 5 years ago

@channel Wrapping up the example from class... sealed trait Shape

case class Rectangle(width: Int, height: Int) extends Shape

case class Location(x: Int, y: Int, shape: Shape) extends Shape { require(shape != null, "null shape in location") }

case class Group(shapes: Shape*) extends Shape { require(shapes != null, "null shape in location") // TODO is this sufficient? }

val r = Rectangle(20, 40) val q = Rectangle(20, 40) val p = Rectangle(20, 30)

val g = Group(r, Group(q, p), Location(10, 15, r))

def countGroup(s: Shape): Int = s match { case Rectangle(w, h) => 0 case Location(x, y, c) => ??? case Group(shapes @ _*) => ??? }

countGroup(r) @channel As I mentioned, the ??? is a placeholder for "not yet implemented". So countGroup(r) returns 0 as expected. But it would raise a NYI exception for group or location nodes. Now we need to apply recursive thinking: For location, the child might have group nodes. For group, the current node is a group node, plus the children might have group nodes. Hence... def countGroup(s: Shape): Int = s match { case Rectangle(w, h) => 0 case Location(x, y, c) => countGroup(c) case Group(shapes @ *) => var sum = 1 for (c <- shapes) sum += countGroup(c) sum } Now countGroup(g) returns 2 as expected. This is a Java-style, imperative implementation. @channel Or equivalently using the foreach method instead of the so-called for comprehension: case Group(shapes @ ) => var sum = 1 shapes.foreach { c => sum += countGroup(c) } sum @channel Now...drum roll...the opportunity to make it functional/applicative/immutable. case Group(shapes @ _) => 1 + shapes.map { c => countGroup(c) } .sum where map transforms each item in a collection with the result of applying the given function to the item (which design pattern is that?) and sum adds all the items in a collection. @channel How would you compare these three implementations in terms of whatever criteria you can think of?