Saturday, October 17, 2015

Notes for Programming in Scala 2nd Edition

Update an array

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  val greetStrings = new Array[String](3)
  greetStrings.update(0, "Hello")
  greetStrings(1) = ", " // this is transformed into .update
  greetStrings(2) = "George!"
  greetStrings.foreach {
    a => print(a)
  }
  println()
}

Operator invoked from the right side:

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  val x = List(2, 3)
  val y = 1 :: x // methods ending in `:` is invoked on the right side
  val z = x.::(1)
  y foreach (a => println(a))
  z foreach (a => println(a))
}

List operations

import scala.util.Random

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  val x = List(2, 3)
  val y = 1 :: x // methods ending in `:` is invoked on the right side
  val z = x.::(1)
  y foreach (a => println(a))
  z foreach (a => println(a))
  val z1 = x ::: y // concat two lists
  z1.foreach(a => println(a))
  // count elements that satisfy a certain predicate
  z1.count(a => a == 3)
  z1.exists(a => a == 3)
  // all elements satisfy the predicate?
  z1.forall(a => a > 0)
  println(z1.head)
  println(z1.tail)
  println(z1.init)
  println(z1.last)
  println(z1.isEmpty)
  println(z1.length)
  z1.map(x => x + 10)
  z1.mkString(", ")
  val z2 = (0 until 10).map(_ => Random.nextInt(100)).toList
  val z3 = z2.map(x => x % 7)
  // Sort by value in Z7
  println(z2.sortWith(
    (s: Int, t: Int) => (s % 7) < (t % 7)
  ))
  // Check the sort is correct
  val z4 = z2.zip(z3)
  println(z4)
  println(z4.sortWith(
    (x: (Int, Int), y: (Int, Int)) => x._2 < y._2
  ))
}

Immutable and mutable collections

import scala.util.Random

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  var jetSet: Set[String] = Set("boeing", "airbus")
  jetSet += "lear" // jetSet is replaced by a new object
  println(jetSet)
  var jetSet1 = collection.mutable.HashSet("boeing", "airbus")
  jetSet1 += "lear" // jetSet1 is updated
  println(jetSet1)

  // mutable map, access key
  import scala.collection.mutable.Map
  val treasureMap = Map[Int, String]()
  treasureMap += (1 -> "Go to island.")
  treasureMap += (2 -> "Find big X on ground.")
  treasureMap += (3 -> "Dig.")
  println(treasureMap(2))
}
import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  def lineLength(filename: String): Unit = {
    val fn = "/tmp/readme"
    // Note the .toList method at the end. Without it `lines` will be an iterator and can only be used once.
    val lines = Source.fromFile(fn).getLines().toList
    val lineLengths = lines.map(x => x.length)
    val maxLengthWidth = lineLengths.max.toString.length
    lines.foreach{
      line => {
        val msg = "%%%dd : %%s".format(maxLengthWidth).format(line.length, line)
        println(msg)
      }
    }
  }
  def main(args: Array[String]) {
    lineLength("/tmp/readme")
  }
}

Checksum app

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  class ChecksumAccumulator {
    private var sum = 0
    def add(b: Byte): Unit = {
      sum += b
    }
    def checksum(): Int = ~(sum & 0xff) + 1
  }

  // Companion object
  object ChecksumAccumulator {
    private val cache = collection.mutable.HashMap[String, Int]()
    def calculate(s: String): Int = {
      if(cache.contains(s)) cache(s)
      else {
        val acc = new ChecksumAccumulator
        for(c <- s) acc.add(c.toByte)
        val cs = acc.checksum()
        cache += (s -> cs)
        cs
      }
    }
  }
}

Multi-line string literal

You can make a multiple-line string within triple quotes:

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  val x =
    """
       |Welcome
       |    1. you
       |    2. me
       |    3. him
       |    4. her
    """.stripMargin
  println(x)
}

Auxiliary constructor

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  class Rational(n: Int, d: Int) {
    require(d != 0)
    val numer: Int = n
    val denom: Int = d
    def this(n: Int) = this(n, 1) // auxiliary constructor
    override def toString = numer +"/"+ denom
    def add(that: Rational): Rational =
      new Rational(
        numer * that.denom + that.numer * denom,
        denom * that.denom
      )
  }

  def main(args: Array[String]) {
    val x = new Rational(5)
    println(x)
  }
}

Default parameter value

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  class Rational(n: Int, d: Int = 1) {
    require(d != 0)
    val numer: Int = n
    val denom: Int = d
    override def toString = numer +"/"+ denom
    def add(that: Rational): Rational =
      new Rational(
        numer * that.denom + that.numer * denom,
        denom * that.denom
      )
  }

  def main(args: Array[String]) {
    val x = new Rational(5)
    println(x)
  }
}

Use Java class

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  val filesHere = (new java.io.File(".")).listFiles()
  filesHere.foreach(println(_))
}

Try…Catch

  import java.io.FileReader
  import java.io.FileNotFoundException
  import java.io.IOException
  try {
    val f = new FileReader("input.txt")
    // Use and close file
  } catch {
    case ex: FileNotFoundException => // Handle missing file
    case ex: IOException => // Handle other I/O error
}

Yielding a default value when something went wrong

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  import java.net.URL
  import java.net.MalformedURLException
  def urlFor(path: String) =
    try {
      new URL(path)
    } catch {
      case e: MalformedURLException =>
        new URL("http://www.scala-lang.org")
    }
  println(urlFor("http://www.google.com"))
  println(urlFor("abc#def"))
}

No break statement. Example application that searches for filenames with .scala extension

You can use a mutable state to control the flow instead.

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  def findScalaFiles(files: Array[String]): Int = {
    var i = 0
    var foundIt = false
    while (i < files.length && !foundIt) {
      if (files(i).endsWith(".scala")) foundIt = true
      else i = i + 1
    }
    if(i >= files.length) -1 else i
  }

  val files = Array("a.py", "b.pl", "c.sh", "d.scala")
  findScalaFiles(files)
}

Or use recursion:

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  object FileScalaFiles {
    private def findScalaFiles(files: Array[String], idx: Int): Int = {
      if(idx >= files.length) {
        -1
      } else if(files(idx).contains(".scala")) {
        idx
      } else {
        findScalaFiles(files, idx + 1)
      }
    }

    def findScalaFiles(files: Array[String]): Int = {
      findScalaFiles(files, 0)
    }
  }

  def main(args: Array[String]) {
    import FileScalaFiles._
    val files = Array("a.py", "b.pl", "c.sh", "d.scala")
    findScalaFiles(files)
  }
}

Multiplication table

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  def main(args: Array[String]) {
    val res = for {
      row <- 1 to 10
      col <- 1 to 10
    } yield {
        "%4d".format(row * col) + {
          if(col == 10) "\n" else ""
        }
      }
    println(res.mkString(""))
  }
}

Private method vs Local function

Private method:

  import scala.io.Source
  object LongLines {
    def processFile(filename: String, width: Int) {
      val source = Source.fromFile(filename)
      for (line <- source.getLines())
        processLine(filename, width, line)
    }
    private def processLine(filename: String,
        width: Int, line: String) {
      if (line.length > width)
        println(filename +": "+ line.trim)
    } 
}

Local function:

  def processFile(filename: String, width: Int) {
    def processLine(filename: String,
        width: Int, line: String) {
      if (line.length > width)
        println(filename +": "+ line)
  }
    val source = Source.fromFile(filename)
    for (line <- source.getLines()) {
      processLine(filename, width, line)
    }
  }

Partially applied function

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  def main(args: Array[String]) {
    def sum(a: Int, b: Int, c: Int) = {
      a + b + c
    }
    (1 to 10).map(sum(1, _: Int, 3)) // 1/3 applied
    (1 to 10).foreach(println _) // 0% applied
  }
}

Varargs (Repeated/multiple parameters of the same type)

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  def echo(xs: String*): Unit = {
    xs.foreach(println _)
  }
  echo("1", "2", "3")
  // unpacking a seq into varargs
  echo((1 to 10).map(_.toString): _*)
}

Named parameter

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  def add(a: Int, b: Int) : Int = {
    a + b
  }
  def main(args: Array[String]) {
    println(add(b = 1, a = 2))
  }
}

Tail recursion

In Scala, tail-call optimization is limited to situations in which a method or nested function calls itself directly as its last operation, without going through a function value or some other intermediary.

Tow example tail recursions that are not optimized:

  // mutually recursive
  def isEven(x: Int): Boolean =
    if (x == 0) true else isOdd(x - 1)
  def isOdd(x: Int): Boolean =
    if (x == 0) false else isEven(x - 1)

  // tail recursion through an intermediary
  val funValue = nestedFun _
  def nestedFun(x: Int) {
    if (x != 0) { println(x); funValue(x - 1) }
  }

Higher order functions

import java.io.File

import scala.io.Source

/**
 * Created by IDEA on 07/09/2015.
 */
object CrashCourse {
  val filesHere = (new File("/Users/kaiyin/personal_config_bin_files/workspace/spark-1.4.1/mllib/src/main/scala/org/apache/spark/ml")).listFiles()
  def filesMatching(matcher: String => Boolean) = {
    filesHere.map(_.getName).filter(matcher)
  }

  def filesMatchEnd(query: String) = {
    filesMatching(_.endsWith(query))
  }

  def filesMatchContain(query: String) = {
    filesMatching(_.contains(query))
  }

  def filesMatchRegex(query: String) = {
    filesMatching(_.matches(query))
  }

  def main(args: Array[String]): Unit = {
    filesMatchEnd(".scala").foreach(println _)
    filesMatchContain("tion").foreach(println _)
    filesMatchRegex("r.*n").foreach(println _)
  }
}

User defined (new) control structures

Chained function call:

object NewControl {
  def twice[A](op: A => A, x: A): A = {
    op(op(x))
  }
  twice[Int](_ * 2, 1)
}

Management of resources:

import java.io.{File, PrintWriter}
import java.util.Date

/**
 * Created by kaiyin on 9/8/15.
 */
class ScalaProg {
  def withPrintWriter(file: File)(op: PrintWriter => Unit) {
    val writer = new PrintWriter(file)
    try {
      op(writer)
    } finally {
      writer.close()
    }
  }
  val file = new File("/tmp/readme")
  withPrintWriter(file) {
    writer => writer.println(new Date())
  }
}

Call by name and call by value:

import java.io.{File, PrintWriter}
import java.util.Date

/**
 * Created by kaiyin on 9/8/15.
 */
class ScalaProg {
  def testFun(): Int = {
    println("testFun called.")
    5
  }
  def echoByValue(x: Int): Unit = {
    (1 to 3).foreach(_ => println("Echo by val: %d".format(x)))
  }
  def echoByName(x: => Int): Unit = {
    (1 to 3).foreach(_ => println("Echo by name: %d".format(x)))
  }
  def main (args: Array[String]): Unit = {
    echoByName(testFun)
    echoByValue(testFun)
  }
}

You will see the following in the console:

testFun called.
Echo by name: 5
testFun called.
Echo by name: 5
testFun called.
Echo by name: 5

Echo by val: 5
Echo by val: 5
Echo by val: 5

Another example:

import java.io.{File, PrintWriter}
import java.util.Date

/**
 * Created by kaiyin on 9/8/15.
 */
class ScalaProg {
  def test(x: => Int): Unit = {
    (1 to 3).foreach(_ => println(x))
  }
  test({println("I am called"); 5})
}

This is useful for testing snippets of code without the syntax overhead of using a lambda:

import java.io.{File, PrintWriter}
import java.util.Date

/**
 * Created by kaiyin on 9/8/15.
 */
class ScalaProg {
  val assertionEnabled = true
  def myAssert(predicate: => Boolean)  = {
    if(assertionEnabled && !predicate) {
      throw new AssertionError()
    }
  }
  myAssert (5 > 3)
  myAssert (5 > 8)
}

Field and method with the same name not allowed

class WontCompile {
private var f = 0 // Won’t compile, because a field
def f = 1
// and method have the same name
}

Final methods and classes

Overriding/Sub-classing is forbidden in these cases.

class ArrayElement extends Element {
  final override def demo() {
    println("ArrayElement's implementation invoked")
  }
}
final class ArrayElement extends Element {
  override def demo() {
    println("ArrayElement's implementation invoked")
  }
}

2D layout engine, drawing a spiral in the console

import java.io.{File, PrintWriter}
import javax.swing.JSpinner.NumberEditor
import scala.io.Source

object SpiralObj1 {
  type TextLayout = Array[String]

  def textLayout(s: String): TextLayout = Array(s)

  // fill by width and height
  def textLayout(s: String, w: Int, h: Int) = Array.fill(h)(s * w)

  def padString(s: String, n: Int): String = {
    val n1 = s.length
    val diff = n - n1
    if (diff <= 0) s
    else {
      val halfDiff = diff / 2
      List.fill(halfDiff)(" ").mkString("") + s + List.fill(n - halfDiff)(" ").mkString("")
    }
  }

  def width(t: TextLayout): Int = if(t.isEmpty) 0 else t(0).length

  def height(t: TextLayout): Int = t.length

  def above(t1: TextLayout, t2: TextLayout): TextLayout = {
    val t3 = widen(t1, width(t2))
    val t4 = widen(t2, width(t1))
    val res = t3 ++ t4
    res
  }

  def beside(t1: TextLayout, t2: TextLayout): TextLayout = {
    val t3 = heighten(t1, height(t2))
    val t4 = heighten(t2, height(t1))
    for ((s1, s2) <- t3.zip(t4)) yield s1 + s2
  }


  def widen(t: TextLayout, n: Int): TextLayout = {
    val n1 = width(t)
    if (n <= n1) t
    else {
      val halfDiff = (n - n1) / 2
      val h = height(t)
      val left = textLayout(" ", halfDiff, h)
      val right = textLayout(
        " ",
        n - halfDiff - width(t),
        h
      )
      beside(beside(left, t), right)
    }
  }

  def heighten(t: TextLayout, n: Int): TextLayout = {
    val h = height(t)
    if (n <= h) t
    else {
      val w = width(t)
      val halfDiff = (n - h) / 2
      val top = textLayout(" ", w, halfDiff)
      val bottom = textLayout(" ", w, n - halfDiff - h)
      above(
        above(top, t),
        bottom
      )
    }
  }

  def stringify(t: TextLayout): String = {
    t.mkString("\n")
  }

  val corner = textLayout("+")
  val space = textLayout(" ")

  def spiral(nEdges: Int, direction: Int): TextLayout = {
    if(nEdges == 0) corner
    else {
      val sp = spiral(nEdges - 1, (direction + 3) % 4)
      val verticalBar = textLayout("|", 1, height(sp))
      val horizontalBar = textLayout("-", width(sp), 1)
      if(direction == 0)
        above(
          beside(corner, horizontalBar),
          beside(sp, space)
        )
      else if(direction == 1)
        beside(
          above(sp, space),
          above(corner, verticalBar)
        )
      else if(direction == 2)
        above(
          beside(space, sp),
          beside(horizontalBar, corner)
        )
      else
        beside(
          above(verticalBar, corner),
          above(space, sp)
        )
    }
  }

  def spiral(nEdge: Int): TextLayout = {
    spiral(nEdge, nEdge % 4)
  }
}


object Main {
  def main(args: Array[String]) {
    import SpiralObj1._
    (0 to 10).foreach(
      x => {
        println()
        println(stringify(spiral(x)))
      }
    )
  }
}

Value and reference equality

  val x = new String("what")
  val y = x
  y == x // true, value equality
  y eq x // true, reference equality
  val z = new String("what")
  z == x // true
  z eq x // false

Traits as stackable modifications

import java.io.{File, PrintWriter}
import javax.swing.JSpinner.NumberEditor
import scala.collection.mutable.ArrayBuffer
import scala.io.Source

object Stackable {
  abstract class IntQueue {
    def get(): Int
    def put(x: Int)
  }

  class BasicIntQueue extends IntQueue {
    private val buf = new ArrayBuffer[Int]
    def get() = buf.remove(0)
    def put(x: Int) = {buf += x}
  }

  trait Doubling extends BasicIntQueue {
    override def put(x: Int) = {super.put(2 * x)}
  }

  trait Filtering extends BasicIntQueue {
    def put(x: Int, f: Int => Boolean) = {
      if(f(x)) super.put(x)
    }
  }

  trait Incrementing extends BasicIntQueue {
    override def put(x: Int) = {
      super.put(x + 1)
    }
  }
}


object Main {
  import Stackable._
  val queue = new BasicIntQueue
  queue.put(10)
  queue.put(20)
  queue.get()
  val q1 = new BasicIntQueue with Doubling
  q1.put(10)
  q1.put(20)
  q1.get()
  // Although both Doubling and Filtering extend BasicIntQueue,
  // when they are stacked, Filtering actually is subclass of Doubling in effect.
  val q2 = new BasicIntQueue with Doubling with Filtering
  q2.put(23, _ % 3 == 0) // rejected
  q2.put(22, _ % 2 == 0) // accepted
  q2.get() // 44
  val q3 = new BasicIntQueue with Incrementing with Doubling with Filtering
  q3.put(23, _ % 3 == 0) // rejected
  q3.put(22, _ % 2 == 0) // accepted
  q3.get() // 45
}

Rename imports

import collection.mutable.{Map => MMap}

def main(args: Array[String]) {
  val mutableMap = MMap.empty[Int, Int]
  val x: Unit = mutableMap.update(3, 5)
  val immutableMap = Map.empty[Int, Int]
  val y: Map[Int, Int] = immutableMap.updated(3, 5)
  val z = y.updated(5, 7)
  println(y)
  println(z)
}

Hide members of a package

// imports everything in Fruits, except Pear
import Fruits.{Pear => _, _}

Private methods


class Outer {

  class Inner {
    private def f() {
      println("f")
    }

    class InnerMost {
      f() // OK }
    }

    (new Inner).f() // error: f is not accessible
  }

}

Protected methods

package p {
          class Super {
            protected def f() { println("f") }
          }
          class Sub extends Super {
            f()
          }
          class Other {
            (new Super).f()  // error: f is not accessible
          }
}

Scope of modifier (access modifiers with qualifiers)

package society {
   package professional {
      class Executive {
         // accessible within professional
         private[professional] var workDetails = null
         // accessible within society
         private[society] var friends = null
         // accessible within the whole project
         private[this] var secrets = null

         def help(another : Executive) {
            println(another.workDetails)
            println(another.secrets) //ERROR
         }
      }
   }
}

Package object

package com.alvinalexander.myapp

package object model {

    // field
    val MAGIC_NUM = 42

    // method
    def echo(a: Any) { println(a) }

    // enumeration
    object Margin extends Enumeration {
        type Margin = Value
        val TOP, BOTTOM, LEFT, RIGHT = Value
    }

    // type definition
    type MutableMap[K, V] = scala.collection.mutable.Map[K, V]
    val MutableMap = scala.collection.mutable.Map

}

GUI

import scala.swing._
import scala.swing.event._

// need scala-swing lib
object ReactiveSwingApp extends SimpleSwingApplication {
  def top = new MainFrame {
    title = "Reactive Swing App"
    val button = new Button {
      text = "Click me"
    }
    val label = new Label {
      text = "No button clicks registered"
    }
    contents = new BoxPanel(Orientation.Vertical) {
      contents += button
      contents += label
      border = Swing.EmptyBorder(30, 30, 10, 30)
    }
    listenTo(button)
    var nClicks = 0
    reactions += {
      case ButtonClicked(b) =>
        nClicks += 1
        label.text = "Number of button clicks: "+ nClicks
    }
  }
}

Match a Seq with variable length

expr match {
  case List(0, _*) => println("found it")
  case _ =>
}

Match with typed patterns

def generalSize(x: Any) = x match {
  case s: String => s.length
  case m: Map[_, _] => m.size
  case _ => -1
}

Match with generic types, problem of type erasure

object Example {
  def isIntMap(x: Any) = x match {
    case m: Map[Int, Int] => true
    case _ => false
  }
  isIntMap(Map(1 -> 1)) // true
  isIntMap(Map(true -> true)) // true
  isIntMap(Map("a" -> "b")) // true
}

The type variables cannot possibly be check because of type erasure. The only exception is array, they are handled specially both in java and scala:

object Example {
  def isIntArray(x: Any) = x match {
    case m: Array[Int] => true
    case _ => false
  }
  isIntArray(Array(1, 2, 3)) // true
  isIntArray(Array(true, false)) // false
}

Variable binding in pattern matching

Here is an example implementation of the tail funciton:

object Example {
  def myTail[A](x: List[A]): List[A] = {
    x match {
      case Nil => Nil
      case List(_) => Nil
      case _ :: (t@List(_, _*)) => t
    }
  }
  myTail(List(1, 2, 3))
}

Exhaustive pattern matching

In general, this is impossible in Scala, because new case classes can be defined at any time and in arbitrary compilation units. For instance, nothing would prevent you from adding a fifth case class to the Expr class hierarchy in a different compilation unit from the one where the other four cases are defined.
The alternative is to make the superclass of your case classes sealed. A sealed class cannot have any new subclasses added except the ones in the same file. This is very useful for pattern matching, because it means you only need to worry about the subclasses you already know about.


object Example {
  sealed abstract class Expr
  case class Var(name: String) extends Expr
  case class Number(num: Double) extends Expr
  case class UnOp(operator: String, arg: Expr) extends Expr
  case class BinOp(operator: String,
                   left: Expr, right: Expr) extends Expr
}

object Example1 {
  import Example._
  case class Var1(name: String) extends Expr
  // this would fail, because Var1 is not matched
  // the compiler issues a warning
  def describe(e: Expr): String = e match {
    case Number(_) => "a number"
    case Var(_)    => "a variable"
    case UnOp(_, _) => "unary op"
    case BinOp(_, _, _) => "binary op"
  }
}

Option instead of null

object Example {
  val m = Map("a" -> 1, "b" -> 2)
  m get "a"
  m get "aa" // None
  (m get "aa") match {
    case None => "Nothing is with aa"
    case Some(i) => i.toString
  }
}

Case sequences as partial functions

object Example {
  val second: PartialFunction[List[Int], Int] = {
    case x :: y :: _ => y
  }
}

// The above example is translated into the following by the compiler:
new PartialFunction[List[Int], Int] {
  def apply(xs: List[Int]) = xs match {
    case x :: y :: _ => y
  }

  def isDefinedAt(xs: List[Int]) = xs match {
    case x :: y :: _ => true
    case _ => false
  }
}

Pattern matching in for expressions:

val results = List(Some("apple"), None, Some("orange"))
for (Some(fruit) <- results) println(fruit)

Expression formatter example

This is not a very good example. The operator precedence is entirely unnecessary here. I am putting it here just for completeness’ sake.

object SpiralObj {

  object ElementObj {

    object Element {

      private class ArrayElement(
                                  val contents: Array[String]
                                  ) extends Element

      private class LineElement(s: String) extends Element {
        val contents = Array(s)
      }

      private class UniformElement(
                                    ch: Char,
                                    override val width: Int,
                                    override val height: Int
                                    ) extends Element {
        private val line = ch.toString * width

        def contents = Array.fill(height)(line)
      }

      def elem(contents: Array[String]): Element = {
        new ArrayElement(contents)
      }

      def elem(s: String): Element = {
        new ArrayElement(Array(s))
      }

      def elem(chr: Char, width: Int, height: Int): Element = {
        new UniformElement(chr, width, height)
      }
    }

    abstract class Element {

      import Element.elem

      // contents to be implemented
      def contents: Array[String]

      def width: Int = contents(0).length

      def height: Int = contents.length

      def above(that: Element): Element = {
        val this1 = this widen that.width
        val that1 = that widen this.width
        elem(this1.contents ++ that1.contents)
      }

      def beside(that: Element): Element = {
        val this1 = this heighten that.height
        val that1 = that heighten this.height
        elem(
          for ((line1, line2) <- this1.contents zip that1.contents)
            yield line1 + line2
        )
      }

      def heighten(h: Int): Element = {
        if (h <= height) this
        else {
          val top = elem(' ', width, (h - height) / 2)
          val bottom = elem(' ', width, h - height - top.height)
          top above this above bottom
        }
      }

      def widen(w: Int): Element = {
        if (w <= width) this
        else {
          val left = elem(' ', (w - width) / 2, height)
          val right = elem(' ', w - width - left.width, height)
          left beside this beside right
        }
      }

      override def toString = contents mkString "\n"
    }

  }




  sealed abstract class Expr

  case class Var(name: String) extends Expr

  case class Number(num: Double) extends Expr

  case class UnOp(operator: String, arg: Expr) extends Expr

  case class BinOp(operator: String,
                   left: Expr, right: Expr) extends Expr

  class ExprFormatter {
    private val opGroups = Array(
      Set("|", "||"),
      Set("&", "&&"),
      Set("^"),
      Set("==", "!="),
      Set("<", "<=", ">", ">="),
      Set("+", "-"),
      Set("*", "%")
    )
    private val precedence = {
      val assocs = for {
        i <- 0 until opGroups.length
        op <- opGroups(i)
      } yield op -> i
      assocs.toMap
    }
    private val unaryPrecedence = opGroups.length
    private val fractionPrecedence = -1

    import ElementObj.Element
    import ElementObj.Element.elem

    def format(e: Expr): Element = format(e, 0)

    private def format(e: Expr, enclPrec: Int): Element = e match {
      case Var(name) => elem(name)
      case Number(num) => {
        def stripDot(s: String) = {
          if (s.endsWith(".0")) s.substring(0, s.length - 2)
          else s
        }
        elem(stripDot(num.toString))
      }
      case UnOp(op, arg) => elem(op) beside format(arg, unaryPrecedence)
      case BinOp("/", left, right) => {
        val top = format(left, fractionPrecedence)
        val bot = format(right, fractionPrecedence)
        val line = elem('-', top.width max bot.width, 1)
        val frac = top above line above bot
        if (enclPrec != fractionPrecedence) frac
        else elem(" ") beside frac beside elem(" ")
      }
      case BinOp(op, left, right) => {
        val opPrec = precedence(op)
        val l = format(left, opPrec)
        val r = format(right, opPrec + 1)
        val oper = l beside elem(" " + op + " ") beside r
        if (enclPrec <= opPrec) oper
        else elem("(") beside oper beside elem(")")
      }
    }
  }

  val f = new ExprFormatter
  val e1 = BinOp(
    "*",
    BinOp("/", Number(1), Number(2)),
    BinOp("+", Var("x"), Number(3))
  )
  def show(e: Expr) = {
    println()
    println(f.format(e) + "\n\n")
  }
  show(e1)
}

Insert sort

object Example {
  def insert(x: Int, xs: List[Int]): List[Int] = {
    if(xs.isEmpty || x <= xs.head) x :: xs 
    else xs.head :: insert(x, xs.tail)
  }

  def isort(xs: List[Int]): List[Int] = {
    if(xs.isEmpty) Nil 
    else insert(xs.head, isort(xs.tail))
  }

  val x = List(5, 9, 1, 2, 3)
  println(isort(x))
}

Another version using pattern matching:

object Example {
  def insert(x: Int, xs: List[Int]): List[Int] = xs match {
    case List() => x :: xs
    case y :: ys => {
      if(x <= y) x :: xs
      else y :: insert(x, ys)
    }
  }

  def isort(xs: List[Int]): List[Int] = xs match {
    case List() => Nil
    case x :: xs1 => insert(x, isort(xs1))
  }

  val x = List(5, 9, 1, 2, 3)
  println(isort(x))
}

List/Array operations

object Example {
  val x = List(5, 9, 2, 3, 1)
  x.init
  x.last
  x.head
  x.tail
  val y = List(7, 8, 11)
  (23 :: x) ::: y // prepend and concat
  List().isEmpty // true
  x.reverse
  x.take(3)
  x.drop(2)
  x.splitAt(3)
  x(4) == x.apply(4) // indexing
  x.indices // Range 0 to 4
  List(x, y).flatten
  List("apple", "orange").map(_.toCharArray).flatten
  x zip x.indices
  x.zipWithIndex // same as above
  x.zipWithIndex.unzip
  (x zip y).unzip // same as List(x, y)
  Array(1, 2, 3).sameElements(Array(1, 2, 3))
  x.mkString(start = "[", sep = ", ", end = "]")
  x.mkString(sep = ", ")
  // concat a string array into one string and add it to a string builder
  val abcde: Array[String] = "a,b,c,d,e".split(",")
  val buf = new StringBuilder
  abcde addString(buf, "(", ",", ")")
  abcde addString(buf, "[", ",", "]")
  println(buf.toString())
  // convert an array to a list and then back
  abcde.toList.toArray.sameElements(abcde)
  // copy content of an array into another array
  val newArray = new Array[String](abcde.length)
  abcde.copyToArray(newArray, 0)
  newArray.sameElements(abcde)
  println(newArray.toList)
}

Merge sort

I modified the original implementation to allow specification of the comparator as either the 1st or the 2nd parameter.

object Example {
  def merge[T](xs: List[T], ys:List[T], less: (T, T) => Boolean): List[T] = (xs, ys) match {
    case (Nil, _) => ys
    case (_, Nil) => xs
    case (x :: xs1, y :: ys1) => {
      if(less(x, y)) x :: merge(xs1, ys, less)
      else y :: merge(xs, ys1, less)
    }
  }
  def msort[T](xs: List[T])(less: (T, T) => Boolean): List[T] = {
    val n: Int = xs.length / 2
    if(n == 0) return xs
    val (xs1, xs2) = xs.splitAt(n)
    merge(msort(xs1)(less), msort(xs2)(less), less)
  }

  def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {
    val n: Int = xs.length / 2
    if(n == 0) return xs
    val (xs1, xs2) = xs.splitAt(n)
    merge(msort(less)(xs1), msort(less)(xs2), less)
  }
}

object Test {
  val x = List(12, 75, 1, 3, 19)
  Example.msort(x)(_ < _)
  // Impossible to infer T in the following case:
  // Example.msort(_ < _)(x) // this will not compile
  Example.msort((a: Int, b: Int) => a < b)(x)
  val msortInt = Example.msort((a: Int, b: Int) => a < b) _
  msortInt(x)
}

List methods

object Example {
  val words = "the quick brown fox".split(" ")
  words map (_.toList)
  words flatMap(_.toList)

  List.range(1, 5) flatMap(
      i => List.range(1, i) map (j => (i, j))
    )
  // The same as above, but clearer
  for (
    i <- List.range(1, 5);
    j <- List.range(1, i)
  ) yield (i, j)

  // foreach takes a procedure (returning Unit) as the second operand
  var sum = 0
  List(1, 2, 3, 4) foreach(sum += _)
  println(sum) // 10

  val int4 = List(1, 2, 3, 4)
  int4 filter (_ <  4) // List(1, 2, 3)
  int4 partition(_ % 2 == 0)
  int4 find (_ % 2 == 0) // Some(2), the first even number
  int4 find (_ < 0) // None
  val x = List(1, 2, 3, -1, -2, -3, 4, 5, 6)
  x.takeWhile(_ > 0)
  x.dropWhile(_ > 0)
  x span (_ > 0) // combines takeWhile and dropWhile
  x.forall(_ > 0) // all elements of x are > 0?
  x.exists(_ > 0) // some elements of x are > 0?
  x.exists(_ < 0) // some elements of x are < 0?
  // foldLeft
  (0 /: x) (_ + _) // 15, sum of x
  // foldRight, for a list this is slower than foldLeft
  (x :\ 0) (_ + _) // 15

  // Implement flatten with fold
  def flattenLeft[T](xss: List[List[T]]) =
    (List[T]() /: xss) (_ ::: _)
  def flattenRight[T](xss: List[List[T]]) =
    (xss :\ List[T]()) (_ ::: _)
  val y = List(List(1, 2), List(3, 4))
  flattenLeft(y)
  flattenRight(y)

  // Implement reverse with fold
  def reverse[T](xs: List[T]) = {
    (List[T]() /: xs) ((ys, y) => y :: ys)
  }
  reverse(int4)

  // sort with custom comparator
  List(1, -3, 4, 2, 6) sortWith(_ > _)
  List(1, -3, 4, 2, 6) sortWith(_ < _)

  // range
  List.range(1, 5) // 1, 2, 3, 4
  List.range(1, 5, 2) // 1, 3
  List.range(5, 1, -2) // 5, 3
  // fill a 3x3 2D List with 5
  List.fill(3, 3)(5)
  // tabulate a 3x3 2D List
  List.tabulate(3, 3)((i, j) => i * j)
  // Implement fill with tabulate
  def fill[T](n1: Int, n2: Int)(x: T): List[List[T]] = {
    List.tabulate(n1, n2)((_, _) => x)
  }
  fill(3, 3)(5)

  // concat
  List.concat(List(1, 2, 3), List(4, 5, 6))

  // zip
  (List(1, 2), List(3, 4), List(5, 6)).zipped.map(_ * _ * _) // 15, 48
  (List("abc", "de"), List(3, 2)).zipped.map(_.length == _)
  (List("abc", "de"), List(3, 2)).zipped.forall(_.length == _)
}

Collection drills

object Example {
  // ListBuffer for O(1) prepend and append
  import scala.collection.mutable.ListBuffer
  val buf = new ListBuffer[Int]
  buf += 1
  buf += 2  // append
  3 +=: buf // prepend
  buf.toList

  import scala.collection.mutable.ArrayBuffer
  val abuf = new ArrayBuffer[Int]()
  abuf += 12
  abuf += 15
  // prepend is probably slow, since ArrayBuffer is just a wrapper around array
  17 +=: abuf
  abuf.toList

  // StringOps
  def hasUpperCase(s: String) = s.exists(_.isUpper)
  hasUpperCase("Abuse")
  hasUpperCase("bizar")

  // Set and Map are immutable by default:
  object Predef {
    type Map[A, +B] = collection.immutable.Map[A, B]
    type Set[A] = collection.immutable.Set[A]
    val Map = collection.immutable.Map
    val Set = collection.immutable.Set
    // ...
  }

  // Using sets
  import collection.mutable
  val words = "See Spot run. run, spot. run!".split("[.,!? ]+")
  val wordSet = mutable.Set.empty[String]
  // get all the unique words
  for (word <- words) {
    wordSet += word.toLowerCase()
  }
  print(wordSet)

  val nums = Set(1, 2, 3)
  nums + 5 // new set with 1, 2, 3, 5
  nums - 3 // new set with 1, 2
  nums ++ List(5, 6) // new set with 1, 2, 3, 5, 6
  nums -- List(1, 2) // new set with 3
  nums & Set(1, 3, 7) // new set with 1, 3
  nums.size
  nums.contains(3)

  // using mutable set
  val x = mutable.Set.empty[String]
  x += "the" // x with "the"
  x -= "the" // x is empty now
  x ++= List("do", "re", "mi") // x with do, re, mi
  x --= List("do", "re") // x with mi
  x.clear() // x is empty again
  x

  // counts word frequency with a map
  val s = """AMSTERDAM -
            |Volkert van der G., de moordenaar van Pim Fortuyn, heeft doelbewust een voorwaarde van zijn voorwaardelijke invrijheidsstelling geschonden. Dat blijkt vanavond in Brandpunt-Reporter, dat opnames heeft gemaakt met een verborgen camera.
            |Van der G. spande een kort geding aan tegen KRO-NCRV waarin hij eiste dat de uitzending werd verboden. De rechtbank van Amsterdam heeft die eis aan het begin van de avond afgewezen, zo meldt de omroep in een persbericht.
            |Van der G. riskeert nu dat hij alsnog de resterende zes jaar gevangenisstraf geheel of gedeeltelijk moet uitzitten, aldus KRO-NCRV.
            |In een heimelijk opgenomen gesprek zegt de moordenaar dat hij foto’s die vorig jaar in De Telegraaf verschenen zelf in scène heeft gezet. Hij vertelt dat zijn toenmalige advocaat, Stijn Franken, contact legde met de fotograaf: ,,Die maakte bij wijze van spreken 200 foto’s en dan konden wij zeggen:  die wel en die niet”, aldus Van der G. Stijn Franken wil volgens KRO-NCRV hier geen commentaar op geven.
            |Mediaverbod
            |De omroep legt uit: ,,Van der G. heeft een mediaverbod waardoor hij niet actief contact mag leggen met de pers. Het mediaverbod maakt onderdeel uit van de bijzondere voorwaarden die hem door de rechter werden opgelegd, die mede tot doel hebben de kans op recidive te verminderen en daarmee de samenleving te beschermen. Uitzonderingen kunnen alleen worden gemaakt na voorafgaande toestemming van zowel Reclassering als Openbaar Ministerie. De Reclassering laat in een reactie aan Brandpunt Reporter weten geen toestemming te hebben verleend en dit te beschouwen als een overtreding van zijn mediaverbod. Het Openbaar Ministerie geeft geen commentaar."
            |Uit de uitzending blijkt volgens KRO-NCRV verder dat Volkert van der G. zijn resocialisatie tegenwerkt. Hij vertelt ,,misbruik te kunnen maken van de soepele opstelling van uitkeringsinstantie UWV in zijn woonplaats Apeldoorn, die geen haast maakt met een re-integratietraject. Hij zegt niet te willen werken ,,omdat je dan niet meer gratis kan procederen”. Tegen de Reclassering zegt hij een klacht te hebben ingediend en hij weigerde een handtekening te zetten onder afspraken over zijn re-integratie. ,,Ik zie die hele reclassering als treiterij."
            |Reactie Telegraaf-hoofdredacteur Paul Jansen:
            |“Ik heb geen kennis van de exacte omstandigheden waaronder de foto in 2014 aan ons door een freelancer is aangeboden. Het ging De Telegraaf destijds om de nieuwswaarde van de foto, en die is evident”.
            |Brandpunt-Reporter is zondagavond te zien om 22.35 uur op NPO2."""
  println(s)
  def countWords(text: String): Map[String, Int] = {
    val mMap = new mutable.HashMap[String, Int]
    for {
      w <- text.split("[., ?!\"']+")
      if (!w.isEmpty)
    } {
      val word = w.toLowerCase()
      if(mMap.contains(word)) mMap.update(word, mMap(word) + 1)
      else mMap.update(word, 1)
    }
    mMap.toMap
  }
  // count words and sort in descending order, have a look at the most frequently used Dutch words in the piece of news
  countWords(s).toSeq.sortWith((x, y) => x._2 > y._2)

  // another implementation
  def countWords1(text: String): Map[String, Int] = {
    val mMap = new mutable.HashMap[String, Int]
    for {
      w <- text.split("[., ?!\"']+")
      if (!w.isEmpty)
    } {
      val word = w.toLowerCase()
      val oldCount = mMap.getOrElse(word, 0)
      mMap += (word -> (oldCount + 1))
    }
    mMap.toMap
  }
  countWords1(s).toSeq.sortWith((x, y) => x._2 > y._2)

  // common operations for maps
  // immutable
  val nums = Map("i" -> 1, "ii" -> 2)
  nums + ("vi" -> 6) // add a key
  nums - "ii" // remove a key
  nums ++ List("iii" -> 3, "v" -> 5) // add more keys
  nums -- List("ii", "i") // remove more keys
  nums.size
  nums.contains("ii")
  nums("ii")
  nums.keys  // returns an iterable over the keys
  nums.keySet // returns a set
  nums.values // iterable over the values
  nums.isEmpty
  // for mutables:
  // +=
  // ++=
  // -=
  // --=

  // Ordered Map/Set
  import collection.immutable.{TreeSet, TreeMap}
  val ts = TreeSet(9, 23, 1, 2)
  ts
  val tm = TreeMap(3 -> "c", 1 -> "a", 2 -> "b")
  tm
  // convert a map to a sorted map
  val m = Map("98" -> List(4, 12, 14), "001" -> List(22, 11))
  val t = TreeMap(m.toSeq: _*)
  t // sorted by key
  // sort an unsorted map
  m.toSeq.sortWith((x, y) => x._2(0) < y._2(0))

  // add a unsorted map into a sorted map
  val m1 = Map("07" -> List(3, 5, 1), "05" -> List(12, 5, 3))
  val t1: TreeMap[String, List[Int]] = t ++ m1
  t1


  // custom ordering with implicit Ordering[String]
  implicit val OrderingStringLength: Ordering[String] = Ordering.by((_: String).length)
  val t2 = TreeMap(t.toSeq: _*)

  // custom ordering provided directly to constructor
  val t3 = TreeMap(t.toSeq: _*)(Ordering.by((_: String).length))

  // syntactic sugar for immutable objects
  // += is not supported for immutable set
  //  val people = Set("Nancy", "Jane")
  //  people += "bob" // Error!
  // but `x += y` is interpreted as `x = x + y` for immutables if x is a var
  var people = Set("Nancy", "Jane")
  people += "Bob" // Error!
  people
  people ++= List("Luke", "Wendy")
  people

  // the same syntactic sugar for Double literal
  var roughlyPi = 3.0
  roughlyPi += 0.1
  roughlyPi += 0.04
  roughlyPi

  // initialization of collections
  List(1, 2, 3)
  Array(1.1, 2.2, 3.3)
  Set[Any](32, "a")
  val t4 = TreeSet[Int]() ++ List(1, 2, 3, 4)

  // converting to array or list
  t4.toList
  t4.toArray
}

More about custom ordering (unfinished)


object Example {

  object ComplexOrdering extends Ordering[Double] {
    def compare(a: Double, b: Double) = {
      if(math.sin(a) < math.sin(b)) -1 else 1
    }
  }
}

object Test {

  import Example._
  {
    import collection.immutable.TreeMap
    val t1 = TreeMap(1.0 -> "c", 1.1 -> "b", 1.2 -> "a", 7.08 -> "d", 7.07 -> "e")(ComplexOrdering)

  }
}

Conversion between mutable and unmutable

object Example {

  import collection.mutable
  import collection.immutable.TreeSet

  val x = TreeSet(1, 5, 8, 12)
  val y = mutable.Set.empty ++= x // mutable.Set[Int]
  val y1 = mutable.Set.empty[Int] ++= x // mutable.Set[Int]
  val z = TreeSet.empty ++ y // Set[Any]
  val z1 = TreeSet.empty[Int] ++ y // TreeSet[Int]

  val m = mutable.Map("i" -> 1, "ii" -> 2) // mutable.Map[String, Int]
  val im = Map.empty ++ m // Map[String, Int]
}

Tuples

object Example {
  val words = Array("a", "ab", "abc")
  def longestWord(words: Array[String]) = {
    var word = words(0)
    var idx = 0
    words.zipWithIndex.foreach(
      x => {
        if(x._1.length > word.length) {
          word = x._1
          idx = x._2
        }
      }
    )
    (word, idx)
  }
  val (word, idx) = longestWord(words)
}

Stateful object using a var

object Example {
  class BankAccount {
    private var bal: Int = 0
    def balance = bal
    def deposit(amount: Int) = {
      require(amount > 0)
      bal += amount
    }
    def withdraw(amount: Int): Boolean = {
      if(0 <= amount && amount <= bal) {
        bal -= amount
        true
      } else false
    }
  }

  val ba = new BankAccount()
  ba.deposit(15)
  ba.withdraw(7)
  ba.withdraw(7)
  ba.withdraw(7)
}

Purely functional while using var

Using a var doesn’t necessarily mean the function is not pure, here is an example:

object Example {
  def time[E](block: => E): E = {
    val t0 = System.nanoTime()
    val result = block
    println("Elapsed time: %d ns".format(System.nanoTime() - t0))
    result
  }

  class Keyed {
    def computeKey: Int = {
      Thread.sleep(5000)
      1530
    }
  }

  class MemoKeyed extends Keyed {
    private var keyCache: Option[Int] = None
    override def computeKey: Int = {
      // if the result is cached, use the cache, otherwise compute it
      keyCache.getOrElse({
        keyCache = Some(super.computeKey)
        keyCache.get
      })
    }
  }

  val m = new MemoKeyed()
  time(m.computeKey) // takes 5s
  time(m.computeKey) // takes very little time
}

Var as properties

object Example {
  class Time(var h: Int = 12, var m: Int = 0)
  val t = new Time()
  t.h
  t.m 
  t.h = 17 // Time.h_= is implemented for you
  t.m = 25 // Time.m_= is implemented for you
  t.h 
  t.m 
  t.m = 67 // Doesn't make sense for minute, not checked
}

Custom setters for validation

object Example {
  class Time {
    private [this] var h = 12
    private [this] var m = 0
    def hour: Int = h
    def hour_=(x: Int): Unit = {
      require(0 <= x && x < 24)
      h = x
    }
    def minute: Int = m
    def minute_=(x: Int): Unit = {
      require(0 <= x && x < 60)
      m = x
    }
  }
  val t = new Time()
  t.hour = 17
  t.minute = 25
  t.hour = 25 // error checked
}

Getter and setter without a corresponding var

object Example {
  class Thermometer {
    var c: Double = _
    def f = c * 9 / 5 + 32
    def f_=(x: Double): Unit = {
      c = (x - 32) * 5 / 9
    }
    override def toString = "%.2fF/%.2fC".format(f, c)
  }
  val t = new Thermometer()
  t.c = 32.0
  println(t.f) // 89.6
  t.f = 89.6
  println(t.c) // 32.0
}

Assign one value to multiple variables

object Example {
  val a, b, c = List(1, 2, 3)
}

Digital circuit simulation

Negator, half adder and full adder.

object Example {

  abstract class Simulation {
    type Action = () => Unit

    case class WorkItem(time: Int, action: Action)

    private var curtime = 0

    def currentTime: Int = curtime

    private var agenda: List[WorkItem] = List()

    private def insert(ag: List[WorkItem], item: WorkItem): List[WorkItem] = {
      if (ag.isEmpty || item.time < ag.head.time) item :: ag
      else ag.head :: insert(ag.tail, item)
    }

    def afterDelay(delay: Int)(block: => Unit): Unit = {
      val item = WorkItem(currentTime + delay, () => block)
      agenda = insert(agenda, item)
    }

    private def next(): Unit = {
      agenda match {
        case item :: rest =>
          agenda = rest
          curtime = item.time
          item.action()
      }
    }

    def run(): Unit = {
      afterDelay(0) {
        println("*** simulation started, time = " + currentTime + " ***")
      }
      while (!agenda.isEmpty) next()
    }
  }



  abstract class BasicCircuitSimulation extends Simulation {
    def InverterDelay: Int

    def AndGateDelay: Int

    def OrGateDelay: Int

    class Wire {
      private var sigVal = false
      private var actions: List[Action] = List()

      def getSignal = sigVal

      def setSignal(s: Boolean) = if (s != sigVal) {
        sigVal = s
        actions foreach (_())
      }

      def addAction(a: Action) = {
        actions = a :: actions
        a()
      }
    }

    def inverter(input: Wire, output: Wire) = {
      def invertAction(): Unit = {
        val inputSig = input.getSignal
        afterDelay(InverterDelay) {
          output setSignal (!inputSig)
        }
      }
      input addAction (invertAction)
    }

    def andGate(a1: Wire, a2: Wire, output: Wire): Unit = {
      def andAction() = {
        val a1Sig = a1.getSignal
        val a2Sig = a2.getSignal
        afterDelay(AndGateDelay) {
          output setSignal (a1Sig & a2Sig)
        }
      }
      a1.addAction(andAction)
      a2.addAction(andAction)
    }

    def orGate(o1: Wire, o2: Wire, output: Wire): Unit = {
      def orAction(): Unit = {
        val o1Sig = o1.getSignal
        val o2Sig = o2.getSignal
        afterDelay(OrGateDelay) {
          output.setSignal(o1Sig | o2Sig)
        }
      }
      o1.addAction(orAction)
      o2.addAction(orAction)
    }

    def probe(name: String, wire: Wire): Unit = {
      def probeAction(): Unit = {
        println(name + " " + currentTime + " new-value = " + wire.getSignal)
      }
      wire.addAction(probeAction)
    }
  }

  abstract class CircuitSimulation extends BasicCircuitSimulation {
    def halfAdder(a: Wire, b: Wire, s: Wire, c: Wire): Unit = {
      val d, e = new Wire
      orGate(a, b, d)
      andGate(a, b, c)
      inverter(c, e)
      andGate(d, e, s)
    }

    def fullAdder(a: Wire, b: Wire, cin: Wire, sum: Wire, cout: Wire): Unit = {
      val s, c1, c2 = new Wire
      halfAdder(a, cin, s, c1)
      halfAdder(b, s, sum, c2)
      orGate(c1, c2, cout)
    }
  }

  object MySimulation extends CircuitSimulation {
    def InverterDelay = 1
    def AndGateDelay = 3
    def OrGateDelay = 5
  }

  import MySimulation._
  val input1, input2, sum, carry = new Wire
  probe("sum", sum)
  probe("carry", carry)
  halfAdder(input1, input2, sum, carry)
  input1.setSignal(true)
  run()
}

Implement a Queue

One slow implementation and one fast:

object Example {
  class SlowAppendQueue[T](elems: List[T]) {
    // head and tail is O(1)
    def head = elems.head 
    def tail = new SlowAppendQueue(elems.tail)
    // enqueue is O(n)
    def enqueue(x: T) = new SlowAppendQueue(elems ::: List(x))
  }

  class SlowHeadQueue[T](smele: List[T]) {
    // head and tail is O(n)
    def head = smele.last 
    def tail = new SlowHeadQueue(smele.init)
    // enqueue is O(1)
    def enqueue(x: T) = new SlowHeadQueue(x :: smele)
  }

  // A faster implementation using 2 lists
  class Queue[T](private val leading: List[T],
                 private val trailing: List[T]
                  ) {
    // when the leading list is empty,
    // the trailing becomes the leading
    private def mirror =
      if (leading.isEmpty) {
        new Queue(trailing.reverse, Nil)
      } else {
        this
      }

    def head = mirror.leading.head
    def tail = {
      val q = mirror
      new Queue(q.leading.tail, q.trailing)
    }
    def enqueue(x: T) = new Queue(leading, x :: trailing)
  }

}

Hiding information

Hiding the fact that you are using two lists in the implementation is probably more elegant and allows more flexibility for future development.
This can be achieved using (public) overloaded/auxiliary constructors.

object Example {
  // the constructor is now private
  class Queue[T] private (private val leading: List[T],
                 private val trailing: List[T]
                  ) {
    // hiding the fact that you are using 2 lists in this implementation
    def this() = this(Nil, Nil)
    def this(elems: T*) = this(elems.toList, Nil)
    def this(elems: List[T]) = this(elems, Nil)
    // when the leading list is empty,
    // the trailing becomes the leading
    private def mirror =
      if (leading.isEmpty) {
        new Queue(trailing.reverse, Nil)
      } else {
        this
      }

    def head = mirror.leading.head
    def tail = {
      val q = mirror
      new Queue(q.leading.tail, q.trailing)
    }
    def enqueue(x: T) = new Queue(leading, x :: trailing)
  }

}

Alternatively, use a companion object:

object Example {

  // the constructor is now private
  class Queue[T] private(private val leading: List[T],
                         private val trailing: List[T]
                          ) {
    // when the leading list is empty,
    // the trailing becomes the leading
    private def mirror: Queue[T] =
      if (leading.isEmpty) {
        new Queue(trailing.reverse, Nil)
      } else {
        this
      }

    def size = leading.size + trailing.size

    def head: T = mirror.leading.head

    def tail: Queue[T] = {
      val q = mirror
      new Queue(q.leading.tail, q.trailing)
    }

    def enqueue(x: T) = new Queue(leading, x :: trailing)
  }

  object Queue {
    def apply[T] = new Queue[T](Nil, Nil)

    def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)

    def apply[T](xs: List[T]) = new Queue[T](xs, Nil)
  }
}

object Test {
  import Example._
  val q = Queue(1, 2, 3)
  q.size
  val q1 = q.enqueue(1)
  q1.size
  q1.head
  q1.tail.head
  val q2 = Queue[Int]()
  val q3 = q2.enqueue(3)
  q3.size 
  q3.head
  q3.tail.head // error, tail is empty
}

Or, more radically, use a trait and a private class:

import _root_.Example.Queue.QueueImpl

object Example {

  trait Queue[T] {
    def head: T

    def tail: Queue[T]

    def enqueue(x: T): Queue[T]

    def size: Int
  }

  object Queue {
    def apply[T](xs: T*): Queue[T] = new QueueImpl[T](xs.toList, Nil)

    private class QueueImpl[T](
                                private val leading: List[T],
                                private val trailing: List[T]
                                ) extends Queue[T] {
      def mirror = {
        if (leading.isEmpty)
          new QueueImpl[T](trailing.reverse, Nil)
        else
          this
      }

      def head: T = mirror.leading.head

      def tail = {
        val q = mirror
        new QueueImpl[T](q.leading.tail, q.trailing)
      }

      def enqueue(x: T) = new QueueImpl[T](leading, x :: trailing)

      def size = leading.size + trailing.size
    }

  }

}

object Test {

  import Example._

  val q: Queue[Int] = Queue(1, 2, 3)
  q.size
  val q1 = q.enqueue(1)
  q1.size
  q1.head
  q1.tail.head
  val q2 = Queue[Int]()
  val q3 = q2.enqueue(3)
  q3.size
  q3.head
  q3.tail.head // error, tail is empty
}

Contravariant and covariant


object Example {

  class Publication(val title: String)

  class Book(title: String) extends Publication(title)

  object Library {
    val books: Set[Book] = {
      Set(
      new Book("Programming in Scala"),
      new Book("Walden")
      )
    }

    def printBookList(info: Book => AnyRef): Unit = {
      for(book <- books) println(info(book))
    }
  }

}

object Test {

  import Example._
  {
    // See the type of printBookList
    // Publication is superclass of Book: contravariant
    // String is subclass of AnyRef: covariant
    def getTitle(p: Publication): String = p.title
    Library.printBookList(getTitle)
  }
}

Demonstrate type lower bounds with a Queue implementation:


object Example {
  class Queue[+T] private (
                          private[this] var leading: List[T],
                          private [this] var trailing: List[T]
                            ) {
    private def mirror: Unit = {
      if(leading.isEmpty) {
        while(!trailing.isEmpty) {
          leading = trailing.head :: leading
          trailing = trailing.tail
        }
      }
    }

    def this() = this(Nil, Nil)

    def head: T = {
      mirror
      leading.head
    }

    def tail: Queue[T] = {
      mirror
      new Queue(leading.tail, trailing)
    }

    def enqueue[U >: T](x: U) = new Queue[U](leading, x :: trailing)

    def enqueue[U >: T](xs: U*) = new Queue[U](leading, xs.reverse.toList ::: trailing)

    def size = leading.size + trailing.size
  }

  object Queue {
    def apply[T, U <: T](xs: U*): Queue[T] = new Queue(xs.toList, Nil)
    def apply[T]: Queue[T] = new Queue[T](Nil, Nil)
  }

  class Publication
  class Book extends Publication
  class Novel extends Book
  class Journal extends Publication
  class Magazine extends Publication

}

object Test {

  import Example._
  {
    val q = new Queue[Int]()
    val q1 = q.enqueue[Int](1, 2, 3)
    q1.head // 1
    val p = Queue.apply[Book, Book](new Book, new Book, new Book) // Queue[Nothing] ?
    val p1 = p.enqueue(new Journal, new Magazine) // p1 is Queue[Publication]
  }
}

0 comments: