SlideShare a Scribd company logo
Lecture 5: Functional Programming




                      TI1220 2012-2013
              Concepts of Programming Languages

                      Eelco Visser / TU Delft
Lecture 5: Functional Programming
John McCarthy (September 4, 1927 –
October 24, 2011) was an American
computer scientist and cognitive scientist.
He coined the term "artificial
intelligence" (AI), developed the Lisp
programming language family, significantly
influenced the design of the ALGOL
programming language, popularized
timesharing, and was very influential in the
early development of AI.



Around 1959, he invented so-called "garbage collection" methods to
solve problems in Lisp. Based on the lambda calculus, Lisp soon
became the programming language of choice for AI applications after
its publication in 1960.


http://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)
Outline
  From the lab:
     Unit testing
     Graded assignment 1
  Functional objects in Scala
  Pattern matching
  Recursion and induction
  Algebraic data types
  Binary trees
  Algebraic data types in C
Messages from the Lab
Tests

• check that your code is correct
• regression testing: don’t make the same mistake twice
Coverage

• a test for each representative case
Test-driven development

• (1) define tests for representative cases
• (2) write code
• (3) test
Unit Testing in Scala


            import org.scalatest.Suite

            class <NameOfTestClass> extends Suite {
              import <ClassUnderTest>._

                def <testNameOfTest> {
                  expect(<expected result>) {
                    <computation>
                  }
                }
            }
/* import test framework .h */
#include "solution.c"
#include "CuTest.h"
#include "CuJoin.h"
                                                 Unit Testing in C
/* your imported libraries */
#include <string.h>

/* signatures of all functions being tested */
char* wc(char* data);

/* defined tests */
void test_1(CuTest *tc) {
  char* wcout = wc("hellon world");
  char* expected = "2 2 10 12";
  CuAssertTrue(tc, !strcmp(wcout,expected));
}
/* hook all your tests into the harness */
void testHooker(CuSuite* intoSuite){
    SUITE_ADD_TEST(intoSuite, test_1);
}
test("Changing properties", function() {
  var obj = {x : 3};
  expect(5);
  ok(changeProp, "function exists");
  equal(obj.x, 3);
  equal(obj.y, undefined);
  changeProp(obj);
  equal(obj.x, 42);
  equal(obj.y, 9);
});




                           Unit Testing in JavaScript
Graded Assignment 1
  Algebraic datatypes in C
  Dynamic dispatch in C

Important dates
   Deadline: April 2, 2013 23:59
   Extension: April 5, 2013 23:59

   Submitting after extension date is not possible
   Maximum penalty for submitting after deadline: 6 points
   Minimum grade needed: 4
   Grade: 70% unit tests, 30% check lists
   Grade for GAs: average of four assignments
Algebraic Datatypes in C
abstract class XML
case class Text(t: String) extends XML
case class Elem(tag: String, elems: List[XML]) extends XML

object Solution {

    def text(elems1: List[XML]): List[XML] =
      elems1.flatMap(_ match {
         case t@Text(_) => List[XML](t)
         case Elem(_, elems2) => text(elems2)
      })

}




       translate this Scala program to an equivalent C program
// values

abstract class Value {
  def value: Int                                    Dynamic Dispatch in C
  def isFailure: Boolean
  def +(that: Value): Value
  def *(that: Value): Value
}

object LookupFailure extends Value {
  def value: Int = 0
                                                    translate this Scala program
  def isFailure: Boolean = true                     to an equivalent C program
  def +(that: Value) = LookupFailure
  def *(that: Value) = LookupFailure
}

class IntValue(v : Int) extends Value {
  val value = v
  def isFailure: Boolean = false
  def +(that: Value) = that match {
    case v: IntValue => new IntValue(value + v.value)
    case _ => LookupFailure
  }
  def *(that: Value) = that match {
    case v: IntValue => new IntValue(value * v.value)
    case _ => LookupFailure
  }
}
// environments

abstract class Env {                               Dynamic Dispatch in C
  def lookup(x: String): Value
}
class MtEnv extends Env {
  def lookup(x: String): Value = LookupFailure
}
class Bind(key: String, value: Int, env: Env) extends Env {
  def lookup(x: String): Value =
    if(x == key) new IntValue(value) else env.lookup(x);
}
// expressions

abstract class Exp {
  def eval(env: Env): Value;
                                                    Dynamic Dispatch in C
  override def toString: String
}

class IntExp(value: Int) extends Exp {
  def eval(env: Env) = new IntValue(value)
  override def toString = value.toString
}

class VarExp(name: String) extends Exp {
  def eval(env: Env) = env.lookup(name)
  override def toString = name
}

class PlusExp(left: Exp, right: Exp) extends Exp {
  def eval(env: Env) = left.eval(env) + right.eval(env)
  override def toString = "(" + left.toString + " + " + right.toString + ")"
}

class MulExp(left: Exp, right: Exp) extends Exp {
  def eval(env: Env) = left.eval(env) * right.eval(env)
  override def toString = "(" + left.toString + " * " + right.toString + ")"
}
Functional Programming
       (in Scala)
Ingredients of functional programming

   Immutable objects

   Pattern matching

   Inductive (algebraic) data types

   Recursive functions

   First-class functions (next week)
Functional Objects in Scala
functional object: the fields
 of an object are immutable
Example: Rational Numbers

•   Rational = Int x Int

•   Notation: numerator/denominator

•   Addition

    • example: 1/2 + 2/3 = 3/6 + 4/6 = (3 + 4)/6 = 7/6
    • general: n1/d1 + n2/d2 = (n1*d2 + n2*d1) /
      (d1*d2)

•   Multiplication

    • n1/d1 + n2/d2 = (n1 * n2) / (d1 * d2)
•   Division

    • n1/d1 / n2/d2 = n1/d2 * d2/n2
Constructing a Rational


                                            class parameters


            class Rational(n: Int, d: Int) {
              println("Created " + n + "/" + d)
            }




             scala> new Rational(1, 2)
             Created 1/2
             res0: Rational = Rational@2d83e895
Immutable object trade-offs

Advantages

• easier reasoning
• pass around freely (no risk of undesired mutations)
• cannot be changed concurrently in two threads
• immutable object make safe hash table keys
Disadvantages

• copying large object graphs vs in-place update
Overriding Methods




class Rational(n: Int, d: Int) {
  override def toString = n + "/" + d
}




   scala> val half = new
   Rational(1, 2)
   half: Rational = 1/2
Checking Pre-conditions




       class Rational(n: Int, d: Int) {
         require(d != 0)
         override def toString = n + "/" + d
       }




scala> val half = new Rational(1, 0)
java.lang.IllegalArgumentException: requirement failed
class Rational(n: Int, d: Int) {
  require(d != 0)
  override def toString = n + "/" + d
  def add(that: Rational): Rational =
    new Rational(n * that.d + that.n * d, d * that.d)
}




                        Adding Rationals Functionally
Visibility of Class Parameters

 class Rational(n: Int, d: Int) {
   require(d != 0)
   override def toString = n + "/" + d
   def add(that: Rational): Rational =
     new Rational(n * that.d + that.n * d, d * that.d)
 }



$ fsc Rational.scala
Rational.scala:5: error: value d is not a   member of Rational
    new Rational(n * that.d + that.n * d,   d * that.d)
                          ^
Rational.scala:5: error: value d is not a   member of Rational
    new Rational(n * that.d + that.n * d,   d * that.d)
                                                     ^
two errors found
Functional Fields


class Rational(n: Int, d: Int) {
  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)
}




scala> new Rational(1,2) add new Rational(2,3)
res0: Rational = 7/6
Non-functional Objects

      class ImperativeRational(n: Int, d: Int) {
        require(d != 0)
        var numer: Int = n
        var denom: Int = d
        override def toString = numer + "/" + denom
        def add(that: ImperativeRational) {
          numer = numer * that.denom + that.numer * denom;
          denom = denom * that.denom;
        }
      }


                            scala> val half = new ImperativeRational(1, 2)
Destructive Update          half: ImperativeRational = 1/2

                            scala> val twothirds = new ImperativeRational(2,3)
                            twothirds: ImperativeRational = 2/3

                            scala> half.add(twothirds)

                            scala> half
                            res1: ImperativeRational = 7/6
Self References



     def lessThan(that: Rational) =
       this.numer * that.denom < that.numer * this.denom

     def max(that: Rational) =
       if (this.lessThan(that)) that else this




                     this: optional when referring to fields
Auxiliary Constructors



class Rational(n: Int, d: Int) {
  require(d != 0)
  val numer = n
  val denom = d
  def this(n: Int) = this(n, 1)
      // auxiliary constructor
  ...
}




      scala> new Rational(6)
      res1: Rational = 6/1
Private Fields and Methods



class Rational(n: Int, d: Int) {
  require(d != 0)
  private val g = gcd(n.abs, d.abs)
  val numer = n / g
  val denom = d / g
  ...
  private def gcd(a: Int, b: Int): Int =
    if (b == 0) a else gcd(b, a % b)
}




        scala> new Rational(6,42)
        res1: Rational = 1/7
Using Functions as Infix Operators




def add(that: Rational): Rational =
  new Rational(numer * that.denom + that.numer * denom,
               denom * that.denom)




     scala> new Rational(1,2).add(new Rational(2,3))
     res0: Rational = 7/6
     scala> new Rational(1,2) add new Rational(2,3)
     res0: Rational = 7/6
def +(that: Rational): Rational =
   new Rational(numer * that.denom + that.numer * denom,
                denom * that.denom)

 def *(that: Rational): Rational =
   new Rational(numer * that.numer, denom * that.denom)



scala> val d = a + b * c
d: Rational = 11/14
                                           Operator Identifiers
scala> val d = a.+(b.*(c))
d: Rational = 11/14
scala> val d = a * b + c
d: Rational = 16/21
scala> val d = (a.*(b)).+(c)
d: Rational = 16/21




Invoking Operators
How many Rational objects are created while executing:

class Rational(n: Int, d: Int) {
  require(d != 0)
  val numer: Int = n
  val denom: Int = d
  override def toString = numer + "/" + denom
  def +(that: Rational): Rational =
    new Rational(
      numer * that.denom + that.numer * denom,
      denom * that.denom)
}
var half = new Rational(1,2)
half = half + half + half

(a)   1
(b)   2
(c)   3
(d)   4                                              Quiz
Alphanumeric identifier

•   identifier: [$A-Za-z_][$A-Za-z_0-9]* ($ reserved for Scala compiler)

•   camel-case convention: toString, HashSet

Operator identifier

•   Unicode set of mathematical symbols(Sm) or other symbols(So), or
    to the 7-bit ASCII characters that are not letters, digits,
    parentheses, square brackets, curly braces, single or double quote,
    or an underscore, period,semi-colon, comma, or back tick
    character.

Literal Identifier
                                                           Identifier Syntax
•   arbitrary string enclosed in back ticks (` . . . `).
Method Overloading


       def *(that: Rational): Rational =
         new Rational(numer * that.numer, denom * that.denom)

       def *(i: Int): Rational =
         new Rational(numer * i, denom)




scala> val c = new Rational(3,7)
c: Rational = 3/7
                                              In a method call, the
scala> c * 2
                                          compiler picks the version
res1: Rational = 6/7
                                           of an overloaded method
                                          that correctly matches the
                                            types of the arguments.
Method Overloading
                                         does not apply to this


 def *(that: Rational): Rational =
   new Rational(numer * that.numer, denom * that.denom)

 def *(i: Int): Rational =
   new Rational(numer * i, denom)




scala> 2 * c
<console>:7: error: overloaded method value * with alternatives:
(Double)Double <and> (Float)Float <and> (Long)Long <and> (Int)Int <and>
(Char)Int <and> (Short)Int <and> (Byte)Int cannot be applied to (Rational)
       2 * c
       ^
Implicit Conversions


def *(that: Rational): Rational =
  new Rational(numer * that.numer, denom * that.denom)

def *(i: Int): Rational =
  new Rational(numer * i, denom)




implicit def intToRational(x: Int) = new Rational(x)



                 scala> 2 * c
                 res4: Rational = 6/7
Functional Objects - Summary

Immutable objects

• class parameters
• immutable fields (val)
• methods don’t change object, but return value
Natural, concise notation

• methods as infix operators, operator identifiers
• method overloading
• implicit conversion
Pattern Matching
e0 match   {
  case 1   => e1;
  case 2   => e2;                  Scala’s Match
  case _   => e3
}




                    Match is similar to Java switch. Differences:

                    • Match is expression: returns a value
                    • Alternatives never fall through
                    • MatchError when no pattern matches
val firstArg = if (args.length > 0) args(0) else ""
firstArg match {
  case "salt" => println("pepper")
  case "chips" => println("salsa")
  case "eggs" => println("bacon")
                                       Choosing between Actions
  case _ => println("huh?")
}




val firstArg = if (!args.isEmpty) args(0) else ""
val friend =
  firstArg match {
    case "salt" => "pepper"
    case "chips" => "salsa"
    case "eggs" => "bacon"
    case _ => "huh?"                    Choosing between Values
  }
println(friend)
def describe(x: Any) = x match {
  case 5 => "five"
  case true => "truth"
  case "hello" => "hi!"                   Constant Patterns
  case Nil => "the empty list"
  case _ => "something else"
}




expr match {
  case 0 => "zero"
  case somethingElse => "not zero: " + somethingElse
}




                                           Pattern Variables
Recursion and Induction
Inductive Definitions




         Natural number

         • 0 is a number
         • if n is a number then n + 1 is a number
         • nothing else is a natural number
def property(n: Int): Boolean =
  n match {
    case 0 => // base case
    case m => ... property (m - 1) ...
              // recursive case for n + 1
  }

def f(n: Int): Int =
  n match {
    case 0 => // base case
    case m => ... f(m - 1) ...
              // recursive case for n + 1
  }

           Induction Principle
def isEven(n: Int): Boolean =
  n match {
    case 0 => ?
    case m => ?
  }

def isOdd(n: Int): Boolean =
  n match {
    case 0 => ?
    case m => ?
  }
def isEven(n: Int): Boolean =
  n match {
    case 0 => true
    case m => isOdd(m - 1)
  }

def isOdd(n: Int): Boolean =
  n match {
    case 0 => false
    case m => isEven(m - 1)
  }
def power(n: Int, exp: Int): Int =
  exp match {
    case 0 => ?
    case m =>
      ?




  }
def power(n: Int, exp: Int): Int =
  exp match {
    case 0 => 1
    case m =>
      if(exp % 2 == 1)
        n * power(n, m - 1)
      else
        power(n * n, m / 2)
  }
Algebraic Data Types
Inductive Data Structures



Natural number

• 0 is a number
• if n is a number then n + 1 is a number
List

• Empty list is a list
• If L is a list then adding an element in
   front of L produces a list
abstract class IntList

  case class Nil() extends IntList

  // Nil() is a list (the empty list)

  case class Cons(hd: Int, tail: IntList) extends IntList

  // if hd is an Int and tail is an IntList
  // then Cons(hd, tl) is an IntList




Nil()
Cons(1, Nil())                          Scala: Case Classes
Cons(2, Cons(1, Nil()))
Cons(1, Cons(2, Cons(3, Nil())))
...
abstract class IntList
case class Nil() extends IntList
case class Cons(hd: Int, tail: IntList) extends IntList

def f(xs: IntList): T =
  xs match {
    case Nil() => // base case
    case Cons(x, ys) => ... f(ys) ...
                        // recursive case
  }




                          Induction Principle for IntList
length of list xs




def length(xs: IntList): Int =
  xs match {
    case Nil() => ?
    case Cons(x, ys) => ?
  }
length of list xs




def length(xs: IntList): Int =
  xs match {
    case Nil() => 0
    case Cons(x, ys) => 1 + length(ys)
  }
sum of the integers in xs



             def sum(xs: IntList): Int =
               xs match {
                 case Nil() => ?
                 case Cons(x, ys) => ?
               }
sum of the integers in xs



             def sum(xs: IntList): Int =
               xs match {
                 case Nil() => 0
                 case Cons(x, ys) => x + sum(ys)
               }
def product(xs: IntList): Int =
  xs match {
    case Nil() => ?
    case Cons(x, ys) => ?
  }




           product of the integers in xs
def product(xs: IntList): Int =
  xs match {
    case Nil() => 1
    case Cons(x, ys) => x * product(ys)
  }




           product of the integers in xs
def append(xs: IntList, ys: IntList): IntList =
  xs match {
    case Nil() => ?
    case Cons(x, zs) => ?
  }




         append elements of ys to xs
def append(xs: IntList, ys: IntList): IntList =
  xs match {
    case Nil() => ys
    case Cons(x, zs) => Cons(x, append(zs, ys))
  }




         append elements of ys to xs
elements of xs in reverse




def reverse(xs: IntList): IntList =
  xs match {
    case Nil() => ?
    case Cons(y, ys)
         => ?
  }
elements of xs in reverse




def reverse(xs: IntList): IntList =
  xs match {
    case Nil() => Nil()
    case Cons(y, ys)
         => append(reverse(ys), Cons(y, Nil()))
  }
def reverse(xs: IntList): IntList =
                       xs match {
                         case Nil() => Nil()
                         case Cons(y, ys)
                              => append(reverse(ys), Cons(y, Nil()))
                       }




def reverse(xs: IntList): IntList =
  reverseAcc(xs, Nil())

def reverseAcc(xs: IntList, rest: IntList): IntList =
  xs match {
    case Nil() => rest
    case Cons(y, ys)
         => reverseAcc(ys, Cons(y, rest))
  }




                     reverse in linear time using accumulator
the list of elements of xs that are even



def filterEven(xs: IntList): IntList =
  xs match {
    case Nil() => Nil()
    case Cons(y, ys)
         => if(y % 2 == 0) Cons(y, filterEven(ys))
             else filterEven(ys)
    }
def insert(x: Int, xs: IntList): IntList =
  xs match {
    case Nil() => Cons(x, Nil())
    case Cons(y, ys) => if(x < y) Cons(x, Cons(y, ys))
                        else Cons(y, insert(x, ys))
  }

def isort(xs: IntList): IntList =
  xs match {
    case Nil() => Nil()
    case Cons(y, ys) => insert(y, isort(ys))
  }




          sort elements in ascending order
def msort(xs: IntList): IntList = {                Merge Sort
  val n = lenght(xs) / 2
  n match {
    case 0 => xs
    case _ => val (as, bs) = splitAt(xs, n)
              merge(msort(as), msort(bs))
  }
}



        def splitAt(xs: IntList, n: Int): (IntList, IntList) =
          xs match {
            case Nil() => (Nil(), Nil())
            case Cons(y, ys) => n match {
              case 0 => (Nil(), xs)
              case _ =>
                val (as, bs) = splitAt(ys, n - 1)
                (Cons(y, as), bs)
            }
          }
Merge Sort


define merge(xs: IntList, ys: IntList): IntList =
  xs match {
    case Nil() => ys
    case Cons(a, as) =>
      ys match {
        case Nil() => xs
        case Cons(b, bs) =>
          if(a < b) Cons(a, merge(as, ys))
          else Cons(b, merge(xs, bs))
      }
  }
abstract class IntList
case class Nil() extends IntList
case class Cons(hd: Int, tail: IntList) extends IntList

def f(xs: IntList): T =
  xs match {
    case Nil() => // base case
    case Cons(x, ys) => ... f(ys) ...
                        // recursive case
  }




                          Induction Principle for IntList
Binary Trees
abstract class BinTree

case class Empty() extends BinTree

case class Node(left: BinTree,
                value: Int,
                right: BinTree) extends BinTree



                                     Empty()

                                     Node(Empty(), 42, Empty())

                                     Node(Empty(), 5,
                                          Node(Empty(), 42, Empty()))
def f(t: BinTree): A =
  t match {                          Node(Node(Empty(), 2, Empty()),
                                          5, Node(Empty(), 42, Empty()))
    case Empty() => ...
    case Node(t1, i, t2) =>
      ... f(t1) ... f(t2) ...
  }
def replace(x: Int, y: Int, t: BinTree): BinTree =
  t match {
    case Empty() => ?
    case Node(l, n, r) =>
      ?




  }




                            replace occurrence of x by y
def replace(x: Int, y: Int, t: BinTree): BinTree =
  t match {
    case Empty() => Empty()
    case Node(l, n, r) =>
      Node(
        replace(x, y, l),
        if(n == x) y else n,
        replace(x, y, r)
      )
  }




                            replace occurrence of x by y
def toList(t: BinTree): IntList = t match {
  case Empty() => ?
  case Node(l, n, r) =>
    ?
}




                         transform binary tree to list
def toList(t: BinTree): IntList = t match {
  case Empty() => List()
  case Node(l, n, r) =>
    append(toList(l), Cons(n, toList(r)))
}




                         transform binary tree to list
Binary Search Trees



Invariant:

Node(t1, n, t2)
- all numbers in t1 are smaller than n
- all numbers in t2 are larger than n

Functions:
def insert(x: Int, t: BinTree): BinTree
def lookup(x: Int, t: BinTree): Boolean


Correctness
lookup(x, insert(x, t)) == true
Lookup in Binary
                            Search Tree




def lookup(x: Int, t: BinTree): Boolean = {
  t match {
    case Empty() => ?
    case Node(left, value, right) =>
      ?




      }
  }
Lookup in Binary
                            Search Tree




def lookup(x: Int, t: BinTree): Boolean = {
  t match {
    case Empty() => false
    case Node(left, value, right) =>
      if(x < value)
        lookup(x, left)
      else if (x > value)
        lookup(x, right)
      else true
    }
  }
Insert in Binary
    Search Tree




def insert(x: Int, t: BinTree): BinTree = {
  t match {
    case Empty() => ?
    case Node(left, value, right) =>
     ?




      }
  }
Insert in Binary
    Search Tree




def insert(x: Int, t: BinTree): BinTree = {
  t match {
    case Empty() => Node(Empty(), x, Empty())
    case Node(left, value, right) =>
      if(x < value)
        Node(insert(x, left), value, right)
      else if(x > value)
        Node(left, value, insert(x, right))
      else t
    }
  }
Insert in Binary
                                         Search Tree



def toBinTree(xs: IntList): BinTree = xs match {
  case Nil() => Empty()
  case Cons(y, ys) => insert(y, toBinTree(ys))
}

def listSort(xs: IntList): IntList =
  toList(toBinTree(xs))
Algebraic Datatypes in C



    Based on: Simple algebraic data types for C by Pieter
    Hartel and Henk Muller. Version 8, 2nd September, 2010
Algebraic Data Types in Scala




abstract class Tree
case class Leaf(v: Int)
case class Branch(v: Int, left: Tree, right: Tree)

def sum(t: Tree): Int = t match {
  case Leaf(v) => v
  case Branch(v, left, right) => v + sum(left) + sum(right)
}
ADT K&R Style



typedef struct tree_struct {
	 int val;
	 struct tree_struct *left;
	 struct tree_struct *right;
} tree;

tree *mkBRANCH(int val, tree *left, tree *right) {
	 tree *result = calloc(1, sizeof(struct tree_struct));
	 if (result == NULL) {
	 	 printf("panicn");
	 }
	 result->val = val;
	 result->left = left;
	 result->right = right;
	 return result;
}
int krsum1(tree *cur) {                         ADT K&R Style
	 if (cur == NULL) {
	 	 return 0;
	 } else {
	 	 return cur->val + krsum1(cur->left) + krsum1(cur->right);
	 }
}
int krsum2(tree_cc *cur) {
	 /*assert cur->left==NULL <==> cur->right==NULL*/
	 if (cur->left == NULL) {
	 	 return cur->val;
	 } else {
	 	 return cur->val + krsum1(cur->left) + krsum2(cur->right);
	 }
}
void test() {
	 tree *r = mkBRANCH(30, NULL, NULL);
	 tree *l = mkBRANCH(20, NULL, NULL);
	 tree *t = mkBRANCH(10, l, r);
	 printf("%dn", krsum1(t));
}
ADT K&R Style



No explicit cases for Leaf and Branch

•   distinction by use of NULL values for branches

•   Does not cater for more alternatives

Confusion about NULL

•   uninitialized value

•   end-of-list, leaf-of-tree

•   => errors

Explicit allocation of tree nodes
Algebraic Data Type with Union


typedef enum {
	 LEAF = 0, BRANCH = 1
} tree_tag;

typedef struct tree_struct {
	 tree_tag tag;
	 char *filename;
	 union {
	 	 struct {
	 	 	 int _val;
	 	 } _LEAF;
	 	 struct {
	 	 	 int _val;
	 	 	 struct tree_struct *_left;
	 	 	 struct tree_struct *_right;
	 	 } _BRANCH;
	 } data;
} tree;
Algebraic Data Type with Union

tree *mkLEAF(int _val) {
	 tree *result = calloc(1, sizeof(struct tree_struct));
	 if (result == NULL) {
	 	 printf("panicn");
	 }
	 result->tag = LEAF;
	 result->data._LEAF._val = val;
	 return result;
}

tree *mkBRANCH(int val, tree *left, tree *right) {
	 tree *result = calloc(1, sizeof(struct tree_struct));
	 if (result == NULL) {
	 	 printf("panicn");
	 }
	 result->tag = BRANCH;
	 result->data._BRANCH._val = val;
	 result->data._BRANCH._left = left;
	 result->data._BRANCH._right = right;
	 return result;
}
Recursive Functions on ADT




            int sum(tree *t) {
            	 if (t == NULL) {
            	 	 return 0;
            	 } else if (t->tag == LEAF) {
            	 	 return t->data._LEAF._val;
            	 } else { // t->tag == BRANCH
            	 	 return t->data._BRANCH._val
            	 	 	     + sum(t->data._BRANCH._left)
            	 	 	     + sum(t->data._BRANCH._right);
            	 }
            }
Reading & Programming in Week 5

Reading

  Sebesta Chapter 6:

  Scala Chapter 6: Functional Objects

  Scala Chapter 15: Case Classes and Pattern Matching

WebLab:
  C, JavaScript, Scala tutorials
   Graded Assignment 1: Dynamic Dispatch in C
   (deadline 2 April 2013, 23:59)


                                   Week 6: First-Class Functions

More Related Content

PDF
TI1220 Lecture 6: First-class Functions
PPT
JBUG 11 - Scala For Java Programmers
PDF
Java programming lab manual
PPT
Extractors & Implicit conversions
PPTX
Templates presentation
ODP
Functions In Scala
ODP
Functional Programming With Scala
PDF
Implicit conversion and parameters
TI1220 Lecture 6: First-class Functions
JBUG 11 - Scala For Java Programmers
Java programming lab manual
Extractors & Implicit conversions
Templates presentation
Functions In Scala
Functional Programming With Scala
Implicit conversion and parameters

What's hot (20)

PDF
Demystifying functional programming with Scala
PDF
Python Programming - IX. On Randomness
ODP
Clojure basics
PDF
Scala categorytheory
PDF
Programming in Scala - Lecture Four
PPT
Scala collection
PPT
Scala functions
PDF
Ti1220 Lecture 2
DOCX
Data Structure Project File
PDF
Meet scala
PDF
Suit case class
PDF
Data structure lab manual
PPTX
Templates in C++
ODP
Knolx session
ODP
Object Equality in Scala
PDF
Java8: Language Enhancements
PDF
Programming in Scala - Lecture Two
ODP
Effective way to code in Scala
PDF
FP in Java - Project Lambda and beyond
Demystifying functional programming with Scala
Python Programming - IX. On Randomness
Clojure basics
Scala categorytheory
Programming in Scala - Lecture Four
Scala collection
Scala functions
Ti1220 Lecture 2
Data Structure Project File
Meet scala
Suit case class
Data structure lab manual
Templates in C++
Knolx session
Object Equality in Scala
Java8: Language Enhancements
Programming in Scala - Lecture Two
Effective way to code in Scala
FP in Java - Project Lambda and beyond
Ad

Similar to Lecture 5: Functional Programming (20)

PPT
Functional object
PPTX
Principles of functional progrmming in scala
PPT
An introduction to scala
PDF
Functional programming ii
PDF
Spark workshop
PDF
Scala by Luc Duponcheel
PPT
Scala
PDF
Introduction à Scala - Michel Schinz - January 2010
ODP
Functional programming with Scala
PDF
TI1220 Lecture 8: Traits & Type Parameterization
ODP
Introducing scala
PPTX
Scala for curious
PDF
Scala Bootcamp 1
PDF
The Scala Programming Language
PPTX
Big Data Day LA 2016/ Hadoop/ Spark/ Kafka track - Iterative Spark Developmen...
PPT
Jeop game-final-review
PDF
(How) can we benefit from adopting scala?
PPTX
Scala Back to Basics: Type Classes
PDF
Lecture 3
Functional object
Principles of functional progrmming in scala
An introduction to scala
Functional programming ii
Spark workshop
Scala by Luc Duponcheel
Scala
Introduction à Scala - Michel Schinz - January 2010
Functional programming with Scala
TI1220 Lecture 8: Traits & Type Parameterization
Introducing scala
Scala for curious
Scala Bootcamp 1
The Scala Programming Language
Big Data Day LA 2016/ Hadoop/ Spark/ Kafka track - Iterative Spark Developmen...
Jeop game-final-review
(How) can we benefit from adopting scala?
Scala Back to Basics: Type Classes
Lecture 3
Ad

More from Eelco Visser (20)

PDF
CS4200 2019 | Lecture 5 | Transformation by Term Rewriting
PDF
CS4200 2019 | Lecture 4 | Syntactic Services
PDF
CS4200 2019 | Lecture 3 | Parsing
PDF
CS4200 2019 | Lecture 2 | syntax-definition
PDF
CS4200 2019 Lecture 1: Introduction
PDF
A Direct Semantics of Declarative Disambiguation Rules
PDF
Declarative Type System Specification with Statix
PDF
Compiler Construction | Lecture 17 | Beyond Compiler Construction
PDF
Domain Specific Languages for Parallel Graph AnalytiX (PGX)
PDF
Compiler Construction | Lecture 15 | Memory Management
PDF
Compiler Construction | Lecture 14 | Interpreters
PDF
Compiler Construction | Lecture 13 | Code Generation
PDF
Compiler Construction | Lecture 12 | Virtual Machines
PDF
Compiler Construction | Lecture 11 | Monotone Frameworks
PDF
Compiler Construction | Lecture 10 | Data-Flow Analysis
PDF
Compiler Construction | Lecture 9 | Constraint Resolution
PDF
Compiler Construction | Lecture 8 | Type Constraints
PDF
Compiler Construction | Lecture 7 | Type Checking
PDF
Compiler Construction | Lecture 6 | Introduction to Static Analysis
PDF
Compiler Construction | Lecture 5 | Transformation by Term Rewriting
CS4200 2019 | Lecture 5 | Transformation by Term Rewriting
CS4200 2019 | Lecture 4 | Syntactic Services
CS4200 2019 | Lecture 3 | Parsing
CS4200 2019 | Lecture 2 | syntax-definition
CS4200 2019 Lecture 1: Introduction
A Direct Semantics of Declarative Disambiguation Rules
Declarative Type System Specification with Statix
Compiler Construction | Lecture 17 | Beyond Compiler Construction
Domain Specific Languages for Parallel Graph AnalytiX (PGX)
Compiler Construction | Lecture 15 | Memory Management
Compiler Construction | Lecture 14 | Interpreters
Compiler Construction | Lecture 13 | Code Generation
Compiler Construction | Lecture 12 | Virtual Machines
Compiler Construction | Lecture 11 | Monotone Frameworks
Compiler Construction | Lecture 10 | Data-Flow Analysis
Compiler Construction | Lecture 9 | Constraint Resolution
Compiler Construction | Lecture 8 | Type Constraints
Compiler Construction | Lecture 7 | Type Checking
Compiler Construction | Lecture 6 | Introduction to Static Analysis
Compiler Construction | Lecture 5 | Transformation by Term Rewriting

Lecture 5: Functional Programming

  • 1. Lecture 5: Functional Programming TI1220 2012-2013 Concepts of Programming Languages Eelco Visser / TU Delft
  • 3. John McCarthy (September 4, 1927 – October 24, 2011) was an American computer scientist and cognitive scientist. He coined the term "artificial intelligence" (AI), developed the Lisp programming language family, significantly influenced the design of the ALGOL programming language, popularized timesharing, and was very influential in the early development of AI. Around 1959, he invented so-called "garbage collection" methods to solve problems in Lisp. Based on the lambda calculus, Lisp soon became the programming language of choice for AI applications after its publication in 1960. http://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)
  • 4. Outline From the lab: Unit testing Graded assignment 1 Functional objects in Scala Pattern matching Recursion and induction Algebraic data types Binary trees Algebraic data types in C
  • 6. Tests • check that your code is correct • regression testing: don’t make the same mistake twice Coverage • a test for each representative case Test-driven development • (1) define tests for representative cases • (2) write code • (3) test
  • 7. Unit Testing in Scala import org.scalatest.Suite class <NameOfTestClass> extends Suite { import <ClassUnderTest>._ def <testNameOfTest> { expect(<expected result>) { <computation> } } }
  • 8. /* import test framework .h */ #include "solution.c" #include "CuTest.h" #include "CuJoin.h" Unit Testing in C /* your imported libraries */ #include <string.h> /* signatures of all functions being tested */ char* wc(char* data); /* defined tests */ void test_1(CuTest *tc) { char* wcout = wc("hellon world"); char* expected = "2 2 10 12"; CuAssertTrue(tc, !strcmp(wcout,expected)); } /* hook all your tests into the harness */ void testHooker(CuSuite* intoSuite){ SUITE_ADD_TEST(intoSuite, test_1); }
  • 9. test("Changing properties", function() { var obj = {x : 3}; expect(5); ok(changeProp, "function exists"); equal(obj.x, 3); equal(obj.y, undefined); changeProp(obj); equal(obj.x, 42); equal(obj.y, 9); }); Unit Testing in JavaScript
  • 10. Graded Assignment 1 Algebraic datatypes in C Dynamic dispatch in C Important dates Deadline: April 2, 2013 23:59 Extension: April 5, 2013 23:59 Submitting after extension date is not possible Maximum penalty for submitting after deadline: 6 points Minimum grade needed: 4 Grade: 70% unit tests, 30% check lists Grade for GAs: average of four assignments
  • 11. Algebraic Datatypes in C abstract class XML case class Text(t: String) extends XML case class Elem(tag: String, elems: List[XML]) extends XML object Solution { def text(elems1: List[XML]): List[XML] = elems1.flatMap(_ match { case t@Text(_) => List[XML](t) case Elem(_, elems2) => text(elems2) }) } translate this Scala program to an equivalent C program
  • 12. // values abstract class Value { def value: Int Dynamic Dispatch in C def isFailure: Boolean def +(that: Value): Value def *(that: Value): Value } object LookupFailure extends Value { def value: Int = 0 translate this Scala program def isFailure: Boolean = true to an equivalent C program def +(that: Value) = LookupFailure def *(that: Value) = LookupFailure } class IntValue(v : Int) extends Value { val value = v def isFailure: Boolean = false def +(that: Value) = that match { case v: IntValue => new IntValue(value + v.value) case _ => LookupFailure } def *(that: Value) = that match { case v: IntValue => new IntValue(value * v.value) case _ => LookupFailure } }
  • 13. // environments abstract class Env { Dynamic Dispatch in C def lookup(x: String): Value } class MtEnv extends Env { def lookup(x: String): Value = LookupFailure } class Bind(key: String, value: Int, env: Env) extends Env { def lookup(x: String): Value = if(x == key) new IntValue(value) else env.lookup(x); }
  • 14. // expressions abstract class Exp { def eval(env: Env): Value; Dynamic Dispatch in C override def toString: String } class IntExp(value: Int) extends Exp { def eval(env: Env) = new IntValue(value) override def toString = value.toString } class VarExp(name: String) extends Exp { def eval(env: Env) = env.lookup(name) override def toString = name } class PlusExp(left: Exp, right: Exp) extends Exp { def eval(env: Env) = left.eval(env) + right.eval(env) override def toString = "(" + left.toString + " + " + right.toString + ")" } class MulExp(left: Exp, right: Exp) extends Exp { def eval(env: Env) = left.eval(env) * right.eval(env) override def toString = "(" + left.toString + " * " + right.toString + ")" }
  • 16. Ingredients of functional programming Immutable objects Pattern matching Inductive (algebraic) data types Recursive functions First-class functions (next week)
  • 18. functional object: the fields of an object are immutable
  • 19. Example: Rational Numbers • Rational = Int x Int • Notation: numerator/denominator • Addition • example: 1/2 + 2/3 = 3/6 + 4/6 = (3 + 4)/6 = 7/6 • general: n1/d1 + n2/d2 = (n1*d2 + n2*d1) / (d1*d2) • Multiplication • n1/d1 + n2/d2 = (n1 * n2) / (d1 * d2) • Division • n1/d1 / n2/d2 = n1/d2 * d2/n2
  • 20. Constructing a Rational class parameters class Rational(n: Int, d: Int) { println("Created " + n + "/" + d) } scala> new Rational(1, 2) Created 1/2 res0: Rational = Rational@2d83e895
  • 21. Immutable object trade-offs Advantages • easier reasoning • pass around freely (no risk of undesired mutations) • cannot be changed concurrently in two threads • immutable object make safe hash table keys Disadvantages • copying large object graphs vs in-place update
  • 22. Overriding Methods class Rational(n: Int, d: Int) { override def toString = n + "/" + d } scala> val half = new Rational(1, 2) half: Rational = 1/2
  • 23. Checking Pre-conditions class Rational(n: Int, d: Int) { require(d != 0) override def toString = n + "/" + d } scala> val half = new Rational(1, 0) java.lang.IllegalArgumentException: requirement failed
  • 24. class Rational(n: Int, d: Int) { require(d != 0) override def toString = n + "/" + d def add(that: Rational): Rational = new Rational(n * that.d + that.n * d, d * that.d) } Adding Rationals Functionally
  • 25. Visibility of Class Parameters class Rational(n: Int, d: Int) { require(d != 0) override def toString = n + "/" + d def add(that: Rational): Rational = new Rational(n * that.d + that.n * d, d * that.d) } $ fsc Rational.scala Rational.scala:5: error: value d is not a member of Rational new Rational(n * that.d + that.n * d, d * that.d) ^ Rational.scala:5: error: value d is not a member of Rational new Rational(n * that.d + that.n * d, d * that.d) ^ two errors found
  • 26. Functional Fields class Rational(n: Int, d: Int) { 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) } scala> new Rational(1,2) add new Rational(2,3) res0: Rational = 7/6
  • 27. Non-functional Objects class ImperativeRational(n: Int, d: Int) { require(d != 0) var numer: Int = n var denom: Int = d override def toString = numer + "/" + denom def add(that: ImperativeRational) { numer = numer * that.denom + that.numer * denom; denom = denom * that.denom; } } scala> val half = new ImperativeRational(1, 2) Destructive Update half: ImperativeRational = 1/2 scala> val twothirds = new ImperativeRational(2,3) twothirds: ImperativeRational = 2/3 scala> half.add(twothirds) scala> half res1: ImperativeRational = 7/6
  • 28. Self References def lessThan(that: Rational) = this.numer * that.denom < that.numer * this.denom def max(that: Rational) = if (this.lessThan(that)) that else this this: optional when referring to fields
  • 29. Auxiliary Constructors class Rational(n: Int, d: Int) { require(d != 0) val numer = n val denom = d def this(n: Int) = this(n, 1) // auxiliary constructor ... } scala> new Rational(6) res1: Rational = 6/1
  • 30. Private Fields and Methods class Rational(n: Int, d: Int) { require(d != 0) private val g = gcd(n.abs, d.abs) val numer = n / g val denom = d / g ... private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) } scala> new Rational(6,42) res1: Rational = 1/7
  • 31. Using Functions as Infix Operators def add(that: Rational): Rational = new Rational(numer * that.denom + that.numer * denom, denom * that.denom) scala> new Rational(1,2).add(new Rational(2,3)) res0: Rational = 7/6 scala> new Rational(1,2) add new Rational(2,3) res0: Rational = 7/6
  • 32. def +(that: Rational): Rational = new Rational(numer * that.denom + that.numer * denom, denom * that.denom) def *(that: Rational): Rational = new Rational(numer * that.numer, denom * that.denom) scala> val d = a + b * c d: Rational = 11/14 Operator Identifiers scala> val d = a.+(b.*(c)) d: Rational = 11/14 scala> val d = a * b + c d: Rational = 16/21 scala> val d = (a.*(b)).+(c) d: Rational = 16/21 Invoking Operators
  • 33. How many Rational objects are created while executing: class Rational(n: Int, d: Int) { require(d != 0) val numer: Int = n val denom: Int = d override def toString = numer + "/" + denom def +(that: Rational): Rational = new Rational( numer * that.denom + that.numer * denom, denom * that.denom) } var half = new Rational(1,2) half = half + half + half (a) 1 (b) 2 (c) 3 (d) 4 Quiz
  • 34. Alphanumeric identifier • identifier: [$A-Za-z_][$A-Za-z_0-9]* ($ reserved for Scala compiler) • camel-case convention: toString, HashSet Operator identifier • Unicode set of mathematical symbols(Sm) or other symbols(So), or to the 7-bit ASCII characters that are not letters, digits, parentheses, square brackets, curly braces, single or double quote, or an underscore, period,semi-colon, comma, or back tick character. Literal Identifier Identifier Syntax • arbitrary string enclosed in back ticks (` . . . `).
  • 35. Method Overloading def *(that: Rational): Rational = new Rational(numer * that.numer, denom * that.denom) def *(i: Int): Rational = new Rational(numer * i, denom) scala> val c = new Rational(3,7) c: Rational = 3/7 In a method call, the scala> c * 2 compiler picks the version res1: Rational = 6/7 of an overloaded method that correctly matches the types of the arguments.
  • 36. Method Overloading does not apply to this def *(that: Rational): Rational = new Rational(numer * that.numer, denom * that.denom) def *(i: Int): Rational = new Rational(numer * i, denom) scala> 2 * c <console>:7: error: overloaded method value * with alternatives: (Double)Double <and> (Float)Float <and> (Long)Long <and> (Int)Int <and> (Char)Int <and> (Short)Int <and> (Byte)Int cannot be applied to (Rational) 2 * c ^
  • 37. Implicit Conversions def *(that: Rational): Rational = new Rational(numer * that.numer, denom * that.denom) def *(i: Int): Rational = new Rational(numer * i, denom) implicit def intToRational(x: Int) = new Rational(x) scala> 2 * c res4: Rational = 6/7
  • 38. Functional Objects - Summary Immutable objects • class parameters • immutable fields (val) • methods don’t change object, but return value Natural, concise notation • methods as infix operators, operator identifiers • method overloading • implicit conversion
  • 40. e0 match { case 1 => e1; case 2 => e2; Scala’s Match case _ => e3 } Match is similar to Java switch. Differences: • Match is expression: returns a value • Alternatives never fall through • MatchError when no pattern matches
  • 41. val firstArg = if (args.length > 0) args(0) else "" firstArg match { case "salt" => println("pepper") case "chips" => println("salsa") case "eggs" => println("bacon") Choosing between Actions case _ => println("huh?") } val firstArg = if (!args.isEmpty) args(0) else "" val friend = firstArg match { case "salt" => "pepper" case "chips" => "salsa" case "eggs" => "bacon" case _ => "huh?" Choosing between Values } println(friend)
  • 42. def describe(x: Any) = x match { case 5 => "five" case true => "truth" case "hello" => "hi!" Constant Patterns case Nil => "the empty list" case _ => "something else" } expr match { case 0 => "zero" case somethingElse => "not zero: " + somethingElse } Pattern Variables
  • 44. Inductive Definitions Natural number • 0 is a number • if n is a number then n + 1 is a number • nothing else is a natural number
  • 45. def property(n: Int): Boolean = n match { case 0 => // base case case m => ... property (m - 1) ... // recursive case for n + 1 } def f(n: Int): Int = n match { case 0 => // base case case m => ... f(m - 1) ... // recursive case for n + 1 } Induction Principle
  • 46. def isEven(n: Int): Boolean = n match { case 0 => ? case m => ? } def isOdd(n: Int): Boolean = n match { case 0 => ? case m => ? }
  • 47. def isEven(n: Int): Boolean = n match { case 0 => true case m => isOdd(m - 1) } def isOdd(n: Int): Boolean = n match { case 0 => false case m => isEven(m - 1) }
  • 48. def power(n: Int, exp: Int): Int = exp match { case 0 => ? case m => ? }
  • 49. def power(n: Int, exp: Int): Int = exp match { case 0 => 1 case m => if(exp % 2 == 1) n * power(n, m - 1) else power(n * n, m / 2) }
  • 51. Inductive Data Structures Natural number • 0 is a number • if n is a number then n + 1 is a number List • Empty list is a list • If L is a list then adding an element in front of L produces a list
  • 52. abstract class IntList case class Nil() extends IntList // Nil() is a list (the empty list) case class Cons(hd: Int, tail: IntList) extends IntList // if hd is an Int and tail is an IntList // then Cons(hd, tl) is an IntList Nil() Cons(1, Nil()) Scala: Case Classes Cons(2, Cons(1, Nil())) Cons(1, Cons(2, Cons(3, Nil()))) ...
  • 53. abstract class IntList case class Nil() extends IntList case class Cons(hd: Int, tail: IntList) extends IntList def f(xs: IntList): T = xs match { case Nil() => // base case case Cons(x, ys) => ... f(ys) ... // recursive case } Induction Principle for IntList
  • 54. length of list xs def length(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? }
  • 55. length of list xs def length(xs: IntList): Int = xs match { case Nil() => 0 case Cons(x, ys) => 1 + length(ys) }
  • 56. sum of the integers in xs def sum(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? }
  • 57. sum of the integers in xs def sum(xs: IntList): Int = xs match { case Nil() => 0 case Cons(x, ys) => x + sum(ys) }
  • 58. def product(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? } product of the integers in xs
  • 59. def product(xs: IntList): Int = xs match { case Nil() => 1 case Cons(x, ys) => x * product(ys) } product of the integers in xs
  • 60. def append(xs: IntList, ys: IntList): IntList = xs match { case Nil() => ? case Cons(x, zs) => ? } append elements of ys to xs
  • 61. def append(xs: IntList, ys: IntList): IntList = xs match { case Nil() => ys case Cons(x, zs) => Cons(x, append(zs, ys)) } append elements of ys to xs
  • 62. elements of xs in reverse def reverse(xs: IntList): IntList = xs match { case Nil() => ? case Cons(y, ys) => ? }
  • 63. elements of xs in reverse def reverse(xs: IntList): IntList = xs match { case Nil() => Nil() case Cons(y, ys) => append(reverse(ys), Cons(y, Nil())) }
  • 64. def reverse(xs: IntList): IntList = xs match { case Nil() => Nil() case Cons(y, ys) => append(reverse(ys), Cons(y, Nil())) } def reverse(xs: IntList): IntList = reverseAcc(xs, Nil()) def reverseAcc(xs: IntList, rest: IntList): IntList = xs match { case Nil() => rest case Cons(y, ys) => reverseAcc(ys, Cons(y, rest)) } reverse in linear time using accumulator
  • 65. the list of elements of xs that are even def filterEven(xs: IntList): IntList = xs match { case Nil() => Nil() case Cons(y, ys) => if(y % 2 == 0) Cons(y, filterEven(ys)) else filterEven(ys) }
  • 66. def insert(x: Int, xs: IntList): IntList = xs match { case Nil() => Cons(x, Nil()) case Cons(y, ys) => if(x < y) Cons(x, Cons(y, ys)) else Cons(y, insert(x, ys)) } def isort(xs: IntList): IntList = xs match { case Nil() => Nil() case Cons(y, ys) => insert(y, isort(ys)) } sort elements in ascending order
  • 67. def msort(xs: IntList): IntList = { Merge Sort val n = lenght(xs) / 2 n match { case 0 => xs case _ => val (as, bs) = splitAt(xs, n) merge(msort(as), msort(bs)) } } def splitAt(xs: IntList, n: Int): (IntList, IntList) = xs match { case Nil() => (Nil(), Nil()) case Cons(y, ys) => n match { case 0 => (Nil(), xs) case _ => val (as, bs) = splitAt(ys, n - 1) (Cons(y, as), bs) } }
  • 68. Merge Sort define merge(xs: IntList, ys: IntList): IntList = xs match { case Nil() => ys case Cons(a, as) => ys match { case Nil() => xs case Cons(b, bs) => if(a < b) Cons(a, merge(as, ys)) else Cons(b, merge(xs, bs)) } }
  • 69. abstract class IntList case class Nil() extends IntList case class Cons(hd: Int, tail: IntList) extends IntList def f(xs: IntList): T = xs match { case Nil() => // base case case Cons(x, ys) => ... f(ys) ... // recursive case } Induction Principle for IntList
  • 71. abstract class BinTree case class Empty() extends BinTree case class Node(left: BinTree, value: Int, right: BinTree) extends BinTree Empty() Node(Empty(), 42, Empty()) Node(Empty(), 5, Node(Empty(), 42, Empty())) def f(t: BinTree): A = t match { Node(Node(Empty(), 2, Empty()), 5, Node(Empty(), 42, Empty())) case Empty() => ... case Node(t1, i, t2) => ... f(t1) ... f(t2) ... }
  • 72. def replace(x: Int, y: Int, t: BinTree): BinTree = t match { case Empty() => ? case Node(l, n, r) => ? } replace occurrence of x by y
  • 73. def replace(x: Int, y: Int, t: BinTree): BinTree = t match { case Empty() => Empty() case Node(l, n, r) => Node( replace(x, y, l), if(n == x) y else n, replace(x, y, r) ) } replace occurrence of x by y
  • 74. def toList(t: BinTree): IntList = t match { case Empty() => ? case Node(l, n, r) => ? } transform binary tree to list
  • 75. def toList(t: BinTree): IntList = t match { case Empty() => List() case Node(l, n, r) => append(toList(l), Cons(n, toList(r))) } transform binary tree to list
  • 76. Binary Search Trees Invariant: Node(t1, n, t2) - all numbers in t1 are smaller than n - all numbers in t2 are larger than n Functions: def insert(x: Int, t: BinTree): BinTree def lookup(x: Int, t: BinTree): Boolean Correctness lookup(x, insert(x, t)) == true
  • 77. Lookup in Binary Search Tree def lookup(x: Int, t: BinTree): Boolean = { t match { case Empty() => ? case Node(left, value, right) => ? } }
  • 78. Lookup in Binary Search Tree def lookup(x: Int, t: BinTree): Boolean = { t match { case Empty() => false case Node(left, value, right) => if(x < value) lookup(x, left) else if (x > value) lookup(x, right) else true } }
  • 79. Insert in Binary Search Tree def insert(x: Int, t: BinTree): BinTree = { t match { case Empty() => ? case Node(left, value, right) => ? } }
  • 80. Insert in Binary Search Tree def insert(x: Int, t: BinTree): BinTree = { t match { case Empty() => Node(Empty(), x, Empty()) case Node(left, value, right) => if(x < value) Node(insert(x, left), value, right) else if(x > value) Node(left, value, insert(x, right)) else t } }
  • 81. Insert in Binary Search Tree def toBinTree(xs: IntList): BinTree = xs match { case Nil() => Empty() case Cons(y, ys) => insert(y, toBinTree(ys)) } def listSort(xs: IntList): IntList = toList(toBinTree(xs))
  • 82. Algebraic Datatypes in C Based on: Simple algebraic data types for C by Pieter Hartel and Henk Muller. Version 8, 2nd September, 2010
  • 83. Algebraic Data Types in Scala abstract class Tree case class Leaf(v: Int) case class Branch(v: Int, left: Tree, right: Tree) def sum(t: Tree): Int = t match { case Leaf(v) => v case Branch(v, left, right) => v + sum(left) + sum(right) }
  • 84. ADT K&R Style typedef struct tree_struct { int val; struct tree_struct *left; struct tree_struct *right; } tree; tree *mkBRANCH(int val, tree *left, tree *right) { tree *result = calloc(1, sizeof(struct tree_struct)); if (result == NULL) { printf("panicn"); } result->val = val; result->left = left; result->right = right; return result; }
  • 85. int krsum1(tree *cur) { ADT K&R Style if (cur == NULL) { return 0; } else { return cur->val + krsum1(cur->left) + krsum1(cur->right); } } int krsum2(tree_cc *cur) { /*assert cur->left==NULL <==> cur->right==NULL*/ if (cur->left == NULL) { return cur->val; } else { return cur->val + krsum1(cur->left) + krsum2(cur->right); } } void test() { tree *r = mkBRANCH(30, NULL, NULL); tree *l = mkBRANCH(20, NULL, NULL); tree *t = mkBRANCH(10, l, r); printf("%dn", krsum1(t)); }
  • 86. ADT K&R Style No explicit cases for Leaf and Branch • distinction by use of NULL values for branches • Does not cater for more alternatives Confusion about NULL • uninitialized value • end-of-list, leaf-of-tree • => errors Explicit allocation of tree nodes
  • 87. Algebraic Data Type with Union typedef enum { LEAF = 0, BRANCH = 1 } tree_tag; typedef struct tree_struct { tree_tag tag; char *filename; union { struct { int _val; } _LEAF; struct { int _val; struct tree_struct *_left; struct tree_struct *_right; } _BRANCH; } data; } tree;
  • 88. Algebraic Data Type with Union tree *mkLEAF(int _val) { tree *result = calloc(1, sizeof(struct tree_struct)); if (result == NULL) { printf("panicn"); } result->tag = LEAF; result->data._LEAF._val = val; return result; } tree *mkBRANCH(int val, tree *left, tree *right) { tree *result = calloc(1, sizeof(struct tree_struct)); if (result == NULL) { printf("panicn"); } result->tag = BRANCH; result->data._BRANCH._val = val; result->data._BRANCH._left = left; result->data._BRANCH._right = right; return result; }
  • 89. Recursive Functions on ADT int sum(tree *t) { if (t == NULL) { return 0; } else if (t->tag == LEAF) { return t->data._LEAF._val; } else { // t->tag == BRANCH return t->data._BRANCH._val + sum(t->data._BRANCH._left) + sum(t->data._BRANCH._right); } }
  • 90. Reading & Programming in Week 5 Reading Sebesta Chapter 6: Scala Chapter 6: Functional Objects Scala Chapter 15: Case Classes and Pattern Matching WebLab: C, JavaScript, Scala tutorials Graded Assignment 1: Dynamic Dispatch in C (deadline 2 April 2013, 23:59) Week 6: First-Class Functions