500 likes | 685 Views
CSE-321 Programming Languages Introduction to Functional Programming (Part II). 박성우. POSTECH March 12, 2007. Outline. Expressions and values V Variables V Functions V Types Polymorphism Recursion Datatypes Pattern matching Higher-order functions Exceptions Modules. f.
E N D
CSE-321 Programming LanguagesIntroduction to Functional Programming(Part II) 박성우 POSTECH March 12, 2007
Outline • Expressions and values V • Variables V • Functions V • Types • Polymorphism • Recursion • Datatypes • Pattern matching • Higher-order functions • Exceptions • Modules
f fnf => (f true, f false) ( true, false) f f What is the Type of ? All we know about f is that it takes booleans as arguments.
f All we know about f is that it takes booleans as arguments. bool ?
f : bool -> ? fnf => (f true, f false) : (bool -> ?) -> ? * ?
f : bool -> 'a • 'a • type variable • usually read as alpha • means 'for any type alpha' fnf => (f true, f false) : (bool -> 'a) -> 'a * 'a
Polymorphic Types • Types involving type variables 'a, 'b, 'c, ... • E.g. • fnx => x : 'a -> 'a • fnx => fny => (x, y) : 'a -> 'b -> ('a * 'b) • fn (x : 'a) => fn (y : 'a) => x = y : 'a -> 'a -> bool (* actually does not typecheck! *)
Equality Types • Motivation • Equality (=) is not defined on every type. • E.g. • comparing two functions for equality? • Type variables with equality • ''a, ''b, ''c, ... • ''a means 'for any type alpha for which equality is defined' • fn (x : ''a) => fn (y : ''a) => x = y : ''a -> ''a -> bool
Outline • Expressions and values V • Variables V • Functions V • Types V • Recursion • Datatypes • Pattern matching • Higher-order functions • Exceptions • Modules
Recursion in SML fun sum n =if n = 0 then 0else sum (n - 1) + n Iteration in C int i, sum; for (i = 0, sum = 0; i <= n; i++)sum += n; Recursion vs. Iteration • Recursion is not an awkward tool if you are used to functional programming. • Recursion seems elegant but inefficient!
0 + 1 1 + 2 36 + 9 f 1 f 8 f 9 f 0 45 + 10 55 Recursion in Action fun sum n =if n = 0then0else sum (n - 1) + n call stack 0 further computation ... ... f 10 evaluation
0 0 0 ... ... 0 f 0 f 8 f 1 f 9 0 f 10 0 Funny Recursion fun zero n =if n = 0then0else zero (n - 1) call stack no further computation evaluation
0 0 f 1 f 9 f 8 f 0 0 0 Funny Recursion Optimized fun zero n =if n = 0then0else zero (n - 1) call stack 0 0 ... ... f 10 evaluation
f 9 f 8 f 1 f 0 Funny Recursion Further Optimized fun zero n =if n = 0then0else zero (n - 1) call stack f 10 ... 0 evaluation
Tail Recursive Function • A tail recursive function f: • A recursive call to f is the last step in evaluating the function body. • That is, no more computation remains after calling f itself. • A tail recursive call needs no stack! • A tail recursive call is as efficient as iteration!
Tail recursive sum fun sum' accum k =if k = 0 thenaccumelse sum' (accum + k) (k - 1) fun sum n = sum' 0 n Example • Non-tail recursive sum fun sum n =if n = 0 then 0else sum (n - 1) + n • Think about the invariant of sum: • given: sum' accum k • invariant: accum = n + (n - 1) + ... (k + 1)
Outline • Expressions and values V • Variables V • Functions V • Types V • Recursion V • Datatypes • Pattern matching • Higher-order functions • Exceptions • Modules
Enumeration Types in C enum shape { Circle, Rectangle, Triangle}; • Great flexibility • e.g. Circle + 1 == Rectangle Triangle - 1 == Rectangle (Circle + Triangle) / 2 == Rectangle • But is this good or bad?
Datatypes in SML datatype shape = Circle | Rectangle | Triangle • No flexibility • e.g. Circle + 1 (x) Triangle - 1 (x) Circle + Triangle (x) • But high safety.
Set datatype set = Empty | Many - Empty; val it = Empty : set - Many; val it = Many : set
Set with Arguments datatype set = Empty | Many ofint - Many 5; val it = Many 5 : set
Set with Type Parameters datatype'a set = Empty | Singleton of'a | Pair of'a * 'a - Singleton 0; val it = Singleton 0 : int set - Pair (0, 1); val it = Pair (0, 1) : int set
Set with Type Parameters datatype'a set = Empty | Singleton of'a | Pair of'a * 'a - Pair (Singleton 0, Pair (0, 1)) val it = Pair (Singleton 0, Pair (0, 1)) : int set set
Set with Type Parameters datatype'a set = Empty | Singleton of'a | Pair of'a * 'a - Pair (0, true); stdIn:27.1-27.15 Error: operator and operand don't agree [literal] operator domain: int * int operand: int * bool in expression: Pair (0,true)
Recursive Set with Type Parameters datatype'aset = Empty | NonEmpty of'a * 'aset • This is essentially the definition of datatype list.
Datatype list datatype'alist = nil | :: of'a * 'alist - nil; val it = [] : 'a list - 2 :: nil; (* :: infix *) val it = [2] : int list - 1 :: (2 :: nil); val it = [1,2] : int list - 1 :: 2 :: nil; (* :: right associative *) val it = [1,2] : int list
Datatype list datatype 'a list = nil | :: of 'a * 'a list - 1 :: [2] :: nil; - [1] :: 2 :: nil; - [1] :: [2] :: nil; val it = [[1],[2]] : int list list - [1] :: [[2]]; val it = [[1],[2]] : int list list (X) (X)
Using Datatypes • We know how to create values of various datatypes: • shape • set • int set • int list • ... • But how do we use them in programming? • What is the point of creating datatype values that are never used?
Outline • Expressions and values V • Variables V • Functions V • Types V • Recursion V • Datatypes V • Pattern matching • Higher-order functions • Exceptions • Modules
Simple Pattern datatype shape = Circle | Rectangle | Triangle (* convertToEnum : shape -> int *) fun convertToEnum (x : shape) : int = case x of Circle=> 0 |Rectangle=> 1 |Triangle=> 2
Pattern with Arguments datatype set = Empty | Many ofint fun size (x : set) : int = case x of Empty => 0 | Many n => n
Wildcard Pattern _ : "don't care" datatype 'a set = Empty | Singleton of 'a | Pair of 'a * 'a fun isEmpty (x : 'a set) : bool = case x of Empty => true | _ => false
Pattern with Type Annotation datatype 'a list = nil | :: of 'a * 'a list fun length (x : 'a list) : int = case x of (nil : 'a list) => 0 | (_ : 'a) :: (tail : 'a list) => 1 + length tail
Outline • Expressions and values V • Variables V • Functions V • Types V • Recursion V • Datatypes V • Pattern matching V • Higher-order functions • Exceptions • Modules
Higher-order Functions • Take functions as arguments. • Return functions as the result.
Why "Higher-order"? • T0 ::= int | bool | real | unit | ... • T1 ::= T0 -> T0 | T0 (* 1st order *) • T2 ::= T1 -> T1 | T1 (* 2nd order *) • T3 ::= T2 -> T2 | T2 (* higher order *) • ...
Higher-order Functions in List • val exists : ('a -> bool) -> 'a list -> bool • val all : ('a -> bool) -> 'a list -> bool • val map : ('a -> 'b) -> 'a list -> 'b list • val filter : ('a -> bool) -> 'a list -> 'a list • val app : ('a -> unit) -> 'a list -> unit (* printInt : int -> unit *) fun printInt i = TextIO.print ((Int.toString i) ^ "\n" ); List.appprintInt[1, 2, 3];
b2 b1 b3 bn bn-1 f f f f f List Fold Function • foldl : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b foldr : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b • foldl f b0 [a0, a1, a2, ..., an-1] a2 a0 a1 an-2 an-1 ... b0 ... bn-2
+ ¢ + + + ¢ + ¢ ¢ Summation fun sum (l : int list) = List.foldl (fn (a, accum) => a + accum) 0 l • foldl op+ 0 [a0, a1, a2, ..., an-1] op+ a2 a0 a1 an-2 an-1 ... 0 ... ¢
List Reversal fun reverse (l : 'a list) = List.foldl (fn (a, rev) => a :: rev) nil l fun reverse (l : 'a list) = List.foldlop:: nil l • Whenever you need iterations over lists, first consider foldl and foldr.
More Examples List.exists f l = List.foldl (fn (a, b) => b orelse f a) false l List.all f l = List.foldl (fn (a, b) => b andalso f a) true l List.app f l = List.foldl (fn (a, _) => f a) () l List.map f l = List.foldr (fn (a, b) => f a :: b) nil l List.filter f l = List.foldr (fn (a, b) => if f a then a :: b else b) nil l
Outline • Expressions and values V • Variables V • Functions V • Types V • Recursion V • Datatypes V • Pattern matching V • Higher-order functions V • Exceptions • exception: please see the course notes. • Modules
Outline • Expressions and values V • Variables V • Functions V • Types V • Recursion V • Datatypes V • Pattern matching V • Higher-order functions V • Exceptions V • Modules
Structure collection of type declarations, exceptions, values, and so on. structure Set = struct type 'a set = 'a list val emptySet = nil fun singleton x = [x] fun union s1 s2 = s1 @ s2 end Signature conceptually type of structures. signature SET = sig type 'a set val emptySet : 'a set val singleton : 'a -> 'a set val union : 'a set -> 'a set -> 'a set end Structures and Signatures
Transparent constraint Type definition is exported to the outside. structure Set : SET = struct type 'a set = 'a list val emptySet = nil fun singleton x = [x] fun union s1 s2 = s1 @ s2 end - Set.singleton 1 = [1]; val it = true : bool Opaque constraint No type definition is exported. structure Set :> SET = struct type 'a set = 'a list val emptySet = nil fun singleton x = [x] fun union s1 s2 = s1 @ s2 end - Set.singleton 1 = [1]; (* Error! *) Structures + Signatures
We need to test your 46 structures! structure ahwbestOne :> HW_ONE = ... structure bopyOne :> HW_ONE = ... structure diozzOne :> HW_ONE = structure freeOne :> HW_ONE = ... ... structure zistinOne :> HW_ONE = ... • How can we test 46 structures? ) They all conform to the same signature. ) We use a functor!
Functors • Functions on structures • takes a structure as an argument • returns a structure as the result structure structure
HW1 Test Functor signature HW_TEST = sig val score : int end structure S = HwSolOne functor Hw1TestFn (P : HW_ONE val name : string) : HW_TEST = struct open P ... val sumScore = test "sum" 5 sumS.sum [1, 5, 10] val facScore = test "fac" 5 facS.fac [1, 5, 10 ] val gcdScore = test "gcd" 4 gcdS.gcd [(15,20), (24,12), (10000,0)] ... val cartprodScore = test "cartprod" 8 (cartprod [1,2,3])(S.cartprod [1,2,3]) [["a","b","c","d"]] val score = sumScore + facScore + ... + cardprodScore val _ = print ("\n " ^ name ^ ", score : " ^ (Int.toString score) ^ "\n" ) ... end
HW1 Test Functor in Action structure ahwbestOneTest = Hw1TestFn (structure P = ahwbestOne val name = "ahwbest") structure bopyOneTest = Hw1TestFn (structure P = bopyOne val name = "bopy") structure diozzOneTest = Hw1TestFn (structure P = diozzOne val name = "diozz") structure freeOneTest = Hw1TestFn (structure P = freeOne val name = "free") ... structure zistinOneTest = Hw1TestFn (structure P = zistinOne val name = "zistin")
So it is YOU who will grade your homework! Learn functors to find your score!