[   ^ to index...   |   <-- previous   |   next -->   ]

Patterns and functions

To determine which case of a function applies to a value, merely apply the matching algorithm to each argument pattern until you get a match. (For multi-argument functions, treat the argument tuple as a single argument.)

- fun silly(0, y) = y
    | silly(x, y) = 1 + silly(x-1, y);
val silly = fn : int * int -> int

- val my_tuple = (3,4);
val my_tuple = (3,4) : int * int

- silly(my_tuple);
val it = 7 : int

In practice, pattern-matching function arguments is usually so intuitive that you don't have to think consciously about the algorithm. Consider the following function to reverse the elements of a list, and its translation to pattern-matching style:

fun reverse(x) =
    if null(x) 
        then nil
        else reverse(tl(x)) @ [hd(x)];

fun reverse(nil) = nil
  | reverse(x::xs) = reverse(xs) @ [x];

Tail-recursion

You can transform many functions into tail-recursive equivalents by using a simple trick: add one or more extra parameters that represent "the work that you have done so far". Here's tail-recursive reverse:

fun reverse(x) = 
    let fun rev(nil, rev_so_far) = done
          | rev(x::xs, rev_so_far) = rev(xs, x::rev_so_far);
    in
        rev(x, nil)
    end;

Exercises

  1. Write a recursive function list_equals, in pattern-matching style, that takes two lists and determines whether they contain the same elements in the same order. Note that the two lists must be the same length. What is the type of this function?
  2. Rewrite list_equals to be fully tail-recursive, if it is not already. There are at least two ways; if you've found one, try to find the other. Hint: how would you represent the "work done so far"?
  3. Write tail-recursive and non-tail-recursive functions to sum the elements of an integer list.


Keunwoo Lee
Last modified: Wed Apr 11 21:24:58 PDT 2001