Patterns in ML functions. Formal vs. actual parameters Here's a function definition (in C): –int add (int x, int y) { return x + y; } –x and y are the.

Presentation on theme: "Patterns in ML functions. Formal vs. actual parameters Here's a function definition (in C): –int add (int x, int y) { return x + y; } –x and y are the."— Presentation transcript:

Patterns in ML functions

Formal vs. actual parameters Here's a function definition (in C): –int add (int x, int y) { return x + y; } –x and y are the formal parameters –Formal parameters must be variables (in C) Here's a function call: –total = add (total, 5); –total and 5 are the actual parameters –Actual parameters typically can be expressions

Parameters are patterns In most conventional languages, formal parameters must be variables –Example: x and y in f(x, y) are both variables In ML, all functions take a single parameter For example, in fun add (x, y) = x + y; we say that (x, y) is one parameter, a tuple But (x, y) is not a variable......it is a pattern

The unit as parameter Consider: fun five ( ) = 5; The single parameter is the unit, ( ) But the unit is a value, that is, a constant In ML, a formal parameter (not just an actual parameter) may be a constant Parameter transmission uses pattern matching

Patterns separate cases in Prolog Prolog passes parameters by unification Unification is a very general and powerful kind of pattern matching Parameters are used to separate a task into cases, for example, –member(X, [X | _]). member(X, [_ | Y]) :- member(X, Y).

Patterns separate cases in Java Java doesn't do pattern matching on parameters, but... Java does allow methods to be overloaded Overloaded methods have different signatures –The signature describes the number and type of parameters This is a primitive kind of pattern matching In a sense, it separates method calls into cases

ML allows multiple patterns Pattern matching is not guaranteed to succeed It's OK if a Prolog predicate fails, but......an ML function must return a value Therefore, ML must support multiple patterns in a function definition Also, ML must ensure that the patterns are exhaustive, i.e. some pattern must match

Factorial fun factorial 0 = 1 | factorial n = n * factorial (n - 1); The vertical bar separates cases The function name is repeated There's only one semicolon, at the end Patterns are tried in order, therefore......specific cases must precede general cases

Lists as patterns Use the cons operator :: to form patterns The pattern (x :: xs) matches a nonempty list fun listSize [ ] = 0 | listSize (x::xs) = 1 + listSize xs; –val listSize : 'a list -> int = fn listSize ["a", "b", "c"]; –val it : int = 3

Exhaustive patterns fun car (x::xs) = x; –warning: Match not exhaustive missing constructors of type 'a list : nil val car : 'a list -> 'a = fn car ["a", "b", "c"]; –val it : string = "a" ML warns you but lets you continue

Last element of a list fun last [x] = x | last (x::xs) = last xs; –warning: Match not exhaustive missing constructors of type 'a list : nil val last : 'a list -> 'a = fn last ["a", "b", "c"]; –val it : string = "c" Notice the use of [x] and (x::xs) as patterns [x] and (x::nil) are really the same thing

Doubling each element of a list fun doubleAll [ ] = [ ] | doubleAll (h::t) = 2 * h :: (doubleAll t); –val doubleAll : int list -> int list = fn doubleAll [1,2,3,4,5]; –val it : int list = [2, 4, 6, 8, 10]

as patterns as parameters A pattern as a formal parameter breaks apart the components of the actual parameter Using (x, y) gives us names for the two parts (x :: xs) gives us names for the head and tail We may also want a name for the whole thing identifier as pattern lets us do both Example: theList as (head :: tail)

Example: merge fun merge(nil, M) = M | merge(L, nil) = L | merge(L as x::xs, M as y::ys) = if x < y then x::merge(xs, M) else y::merge(L, ys); –val merge : (int list * int list) -> int list = fn Source: Elements of ML Programming, J. Ullman

Match expressions A match consists of rules separated by | A rule has the form pattern => result –pattern1 => result1 | pattern2 => result2 |... patternN => resultN The results must all be of the same type The patterns should be exhaustive

case expressions A match all by itself isn't a complete expression A case expression has the form: case expression of match Patterns are tried in the order they are given When a match is found, the corresponding result is evaluated and returned as the result If nothing matches, a Match exception is raised We haven't covered exceptions yet

if...then...else... expressions if expr1 then expr2 else expr3 is actually syntactic sugar for case expr1 of true => expr2 | false => expr3 If you make an error in an if expression, the compiler reports it as an error in a case expression

Functions using fn fun f(pattern1) = expr1 | f(pattern2) = expr2; is actually syntactic sugar for val rec f = fn pattern1 => expr1 | pattern2 => exprn2; Note the use of val, fn, and rec rec is only required for recursive functions

ML is purely functional The if and case expressions are special cases of a match; so is fun Arithmetic operators such as + are syntactic sugar for functions –Operators can be used as functions: op + (a, b) In ML, "expressions" are really functions, and basically everything is a function

The End

Download ppt "Patterns in ML functions. Formal vs. actual parameters Here's a function definition (in C): –int add (int x, int y) { return x + y; } –x and y are the."

Similar presentations