Lecture 5: Functional Programming

  • 1,497 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
1,497
On Slideshare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
27
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Lecture 5: Functional Programming TI1220 2012-2013 Concepts of Programming Languages Eelco Visser / TU Delft
  • 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. 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. Messages from the Lab
  • 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. Unit Testing in Scala import org.scalatest.Suite class <NameOfTestClass> extends Suite { import <ClassUnderTest>._ def <testNameOfTest> { expect(<expected result>) { <computation> } } }
  • 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. 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. 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. 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. // 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. // 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. // 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. Functional Programming (in Scala)
  • 15. Ingredients of functional programming Immutable objects Pattern matching Inductive (algebraic) data types Recursive functions First-class functions (next week)
  • 16. Functional Objects in Scala
  • 17. functional object: the fields of an object are immutable
  • 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. 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. 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. Overriding Methodsclass Rational(n: Int, d: Int) { override def toString = n + "/" + d} scala> val half = new Rational(1, 2) half: Rational = 1/2
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Pattern Matching
  • 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. 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. 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. Recursion and Induction
  • 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. 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. def isEven(n: Int): Boolean = n match { case 0 => ? case m => ? }def isOdd(n: Int): Boolean = n match { case 0 => ? case m => ? }
  • 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. def power(n: Int, exp: Int): Int = exp match { case 0 => ? case m => ? }
  • 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. Algebraic Data Types
  • 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. 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. 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. length of list xsdef length(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? }
  • 54. length of list xsdef length(xs: IntList): Int = xs match { case Nil() => 0 case Cons(x, ys) => 1 + length(ys) }
  • 55. sum of the integers in xs def sum(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? }
  • 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. def product(xs: IntList): Int = xs match { case Nil() => ? case Cons(x, ys) => ? } product of the integers in xs
  • 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. def append(xs: IntList, ys: IntList): IntList = xs match { case Nil() => ? case Cons(x, zs) => ? } append elements of ys to xs
  • 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. elements of xs in reversedef reverse(xs: IntList): IntList = xs match { case Nil() => ? case Cons(y, ys) => ? }
  • 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. 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. 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. 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. 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. 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. 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. Binary Trees
  • 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. 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. 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. def toList(t: BinTree): IntList = t match { case Empty() => ? case Node(l, n, r) => ?} transform binary tree to list
  • 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. 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. Lookup in Binary Search Treedef lookup(x: Int, t: BinTree): Boolean = { t match { case Empty() => ? case Node(left, value, right) => ? } }
  • 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. Insert in Binary Search Treedef insert(x: Int, t: BinTree): BinTree = { t match { case Empty() => ? case Node(left, value, right) => ? } }
  • 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. 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. Algebraic Datatypes in C Based on: Simple algebraic data types for C by Pieter Hartel and Henk Muller. Version 8, 2nd September, 2010
  • 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. 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. 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. 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. 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. 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. 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. 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