Lecture 5: Functional Programming

1,858 views
1,741 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,858
On SlideShare
0
From Embeds
0
Number of Embeds
1,257
Actions
Shares
0
Downloads
32
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Lecture 5: Functional Programming

  1. 1. Lecture 5: Functional Programming TI1220 2012-2013 Concepts of Programming Languages Eelco Visser / TU Delft
  2. 2. John McCarthy (September 4, 1927 –October 24, 2011) was an Americancomputer scientist and cognitive scientist.He coined the term "artificialintelligence" (AI), developed the Lispprogramming language family, significantlyinfluenced the design of the ALGOLprogramming language, popularizedtimesharing, and was very influential in theearly development of AI.Around 1959, he invented so-called "garbage collection" methods tosolve problems in Lisp. Based on the lambda calculus, Lisp soonbecame the programming language of choice for AI applications afterits publication in 1960.http://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)
  3. 3. 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
  4. 4. Messages from the Lab
  5. 5. Tests• check that your code is correct• regression testing: don’t make the same mistake twiceCoverage• a test for each representative caseTest-driven development• (1) define tests for representative cases• (2) write code• (3) test
  6. 6. Unit Testing in Scala import org.scalatest.Suite class <NameOfTestClass> extends Suite { import <ClassUnderTest>._ def <testNameOfTest> { expect(<expected result>) { <computation> } } }
  7. 7. /* 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);}
  8. 8. 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
  9. 9. Graded Assignment 1 Algebraic datatypes in C Dynamic dispatch in CImportant 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
  10. 10. Algebraic Datatypes in Cabstract class XMLcase class Text(t: String) extends XMLcase class Elem(tag: String, elems: List[XML]) extends XMLobject 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
  11. 11. // valuesabstract 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 }}
  12. 12. // environmentsabstract 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);}
  13. 13. // expressionsabstract 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 + ")"}
  14. 14. Functional Programming (in Scala)
  15. 15. Ingredients of functional programming Immutable objects Pattern matching Inductive (algebraic) data types Recursive functions First-class functions (next week)
  16. 16. Functional Objects in Scala
  17. 17. functional object: the fields of an object are immutable
  18. 18. 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
  19. 19. 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
  20. 20. Immutable object trade-offsAdvantages• easier reasoning• pass around freely (no risk of undesired mutations)• cannot be changed concurrently in two threads• immutable object make safe hash table keysDisadvantages• copying large object graphs vs in-place update
  21. 21. Overriding Methodsclass Rational(n: Int, d: Int) { override def toString = n + "/" + d} scala> val half = new Rational(1, 2) half: Rational = 1/2
  22. 22. 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
  23. 23. 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
  24. 24. 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.scalaRational.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
  25. 25. Functional Fieldsclass 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
  26. 26. 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
  27. 27. 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
  28. 28. Auxiliary Constructorsclass 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
  29. 29. Private Fields and Methodsclass 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
  30. 30. Using Functions as Infix Operatorsdef 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
  31. 31. 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 * cd: Rational = 11/14 Operator Identifiersscala> val d = a.+(b.*(c))d: Rational = 11/14scala> val d = a * b + cd: Rational = 16/21scala> val d = (a.*(b)).+(c)d: Rational = 16/21Invoking Operators
  32. 32. 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
  33. 33. Alphanumeric identifier• identifier: [$A-Za-z_][$A-Za-z_0-9]* ($ reserved for Scala compiler)• camel-case convention: toString, HashSetOperator 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 (` . . . `).
  34. 34. 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, thescala> c * 2 compiler picks the versionres1: Rational = 6/7 of an overloaded method that correctly matches the types of the arguments.
  35. 35. 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 ^
  36. 36. Implicit Conversionsdef *(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
  37. 37. Functional Objects - SummaryImmutable objects• class parameters• immutable fields (val)• methods don’t change object, but return valueNatural, concise notation• methods as infix operators, operator identifiers• method overloading• implicit conversion
  38. 38. Pattern Matching
  39. 39. 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
  40. 40. 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)
  41. 41. 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
  42. 42. Recursion and Induction
  43. 43. 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
  44. 44. 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
  45. 45. def isEven(n: Int): Boolean = n match { case 0 => ? case m => ? }def isOdd(n: Int): Boolean = n match { case 0 => ? case m => ? }
  46. 46. 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) }
  47. 47. def power(n: Int, exp: Int): Int = exp match { case 0 => ? case m => ? }
  48. 48. 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) }
  49. 49. Algebraic Data Types
  50. 50. Inductive Data StructuresNatural number• 0 is a number• if n is a number then n + 1 is a numberList• Empty list is a list• If L is a list then adding an element in front of L produces a list
  51. 51. 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 IntListNil()Cons(1, Nil()) Scala: Case ClassesCons(2, Cons(1, Nil()))Cons(1, Cons(2, Cons(3, Nil())))...
  52. 52. abstract class IntListcase class Nil() extends IntListcase class Cons(hd: Int, tail: IntList) extends IntListdef f(xs: IntList): T = xs match { case Nil() => // base case case Cons(x, ys) => ... f(ys) ... // recursive case } Induction Principle for IntList
  53. 53. length of list xsdef length(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? }
  54. 54. length of list xsdef length(xs: IntList): Int = xs match { case Nil() => 0 case Cons(x, ys) => 1 + length(ys) }
  55. 55. sum of the integers in xs def sum(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? }
  56. 56. sum of the integers in xs def sum(xs: IntList): Int = xs match { case Nil() => 0 case Cons(x, ys) => x + sum(ys) }
  57. 57. def product(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? } product of the integers in xs
  58. 58. def product(xs: IntList): Int = xs match { case Nil() => 1 case Cons(x, ys) => x * product(ys) } product of the integers in xs
  59. 59. def append(xs: IntList, ys: IntList): IntList = xs match { case Nil() => ? case Cons(x, zs) => ? } append elements of ys to xs
  60. 60. 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
  61. 61. elements of xs in reversedef reverse(xs: IntList): IntList = xs match { case Nil() => ? case Cons(y, ys) => ? }
  62. 62. elements of xs in reversedef reverse(xs: IntList): IntList = xs match { case Nil() => Nil() case Cons(y, ys) => append(reverse(ys), Cons(y, Nil())) }
  63. 63. 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
  64. 64. the list of elements of xs that are evendef filterEven(xs: IntList): IntList = xs match { case Nil() => Nil() case Cons(y, ys) => if(y % 2 == 0) Cons(y, filterEven(ys)) else filterEven(ys) }
  65. 65. 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
  66. 66. 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) } }
  67. 67. Merge Sortdefine 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)) } }
  68. 68. abstract class IntListcase class Nil() extends IntListcase class Cons(hd: Int, tail: IntList) extends IntListdef f(xs: IntList): T = xs match { case Nil() => // base case case Cons(x, ys) => ... f(ys) ... // recursive case } Induction Principle for IntList
  69. 69. Binary Trees
  70. 70. abstract class BinTreecase class Empty() extends BinTreecase 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) ... }
  71. 71. def replace(x: Int, y: Int, t: BinTree): BinTree = t match { case Empty() => ? case Node(l, n, r) => ? } replace occurrence of x by y
  72. 72. 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
  73. 73. def toList(t: BinTree): IntList = t match { case Empty() => ? case Node(l, n, r) => ?} transform binary tree to list
  74. 74. 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
  75. 75. Binary Search TreesInvariant:Node(t1, n, t2)- all numbers in t1 are smaller than n- all numbers in t2 are larger than nFunctions:def insert(x: Int, t: BinTree): BinTreedef lookup(x: Int, t: BinTree): BooleanCorrectnesslookup(x, insert(x, t)) == true
  76. 76. Lookup in Binary Search Treedef lookup(x: Int, t: BinTree): Boolean = { t match { case Empty() => ? case Node(left, value, right) => ? } }
  77. 77. Lookup in Binary Search Treedef 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 } }
  78. 78. Insert in Binary Search Treedef insert(x: Int, t: BinTree): BinTree = { t match { case Empty() => ? case Node(left, value, right) => ? } }
  79. 79. Insert in Binary Search Treedef 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 } }
  80. 80. Insert in Binary Search Treedef 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))
  81. 81. Algebraic Datatypes in C Based on: Simple algebraic data types for C by Pieter Hartel and Henk Muller. Version 8, 2nd September, 2010
  82. 82. Algebraic Data Types in Scalaabstract class Treecase 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)}
  83. 83. ADT K&R Styletypedef 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;}
  84. 84. 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));}
  85. 85. ADT K&R StyleNo explicit cases for Leaf and Branch• distinction by use of NULL values for branches• Does not cater for more alternativesConfusion about NULL• uninitialized value• end-of-list, leaf-of-tree• => errorsExplicit allocation of tree nodes
  86. 86. Algebraic Data Type with Uniontypedef 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;
  87. 87. Algebraic Data Type with Uniontree *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;}
  88. 88. 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); } }
  89. 89. Reading & Programming in Week 5Reading Sebesta Chapter 6: Scala Chapter 6: Functional Objects Scala Chapter 15: Case Classes and Pattern MatchingWebLab: C, JavaScript, Scala tutorials Graded Assignment 1: Dynamic Dispatch in C (deadline 2 April 2013, 23:59) Week 6: First-Class Functions

×