ML (Meta Language)
SELECTED TOPICS FROM TEXT CHAPS 8 and 9
Standard ML handout.
8.2 TYPES
ML strongly typed with compile-time testing (rare for functional language)
Built-in basic types: int, real, bool, string
Create enumerated types with datatype keyword, e.g.
datatype direction = ne | se | sw | nw ;
datatype texture = arc | band;
Structured types:
a. list e.g. int list or direction list
b. product (tuple, compound type) e.g. type square = texture * direction;
c. function e.g. int -> bool
(function type describes mapping from input parms to return value)
e.g. square list -> int
ML will try to infer types from context
8.2 BASIC LIST OPERATIONS
'[' and ']' are list boundary characters
',' is list element separator
[] is the empty list
null(<list>) test for empty list
hd(<list>) returns head element
tl (<list>) returns tail list (list w/o head element)
:: infix operator to construct list (left operand is element, right is list)
examples:
1::[2,3] is [1,2,3]
1:: [ ] is [1]
hd ([1,2,3]) is 1
tl ([1,2,3]) is [2,3]
Exercise: given [1,2,3] what expression yields list containing second value?
8.3 and 8.4 FUNCTIONS AND CONTROL FLOW
function definition syntax already seen
conditional: if then else e.g. fun abs(n) = if n >=0 then n else 0-n
iteration by recursion e.g. fun len(x) = if null(x) then 0 else 1+len(tl(x))
Exercise: define recursive factorial?
defining functions by case e.g. fun len([ ]) = 0 | len(a::y) = 1 + len(y)
(equivalent to above)
9.3 FUNCTIONS AS FIRST CLASS VALUES
- assign to data structs, pass as args, return from functions
ML "map" function as example:
fun map f [ ] = [ ]
| map f (a::y) = (f a):: (map f y)
(note some parentheses not specified -- this is OK.)
mechanism to apply arbitrary function f to each item in arbitrary list.
9.7 Little Quilt in ML. Annotated version of Figure 9.7:
(* enumerated types *) datatype texture = arcs | bands; datatype direction = ne | se | sw | nw; (* type definitions *) type square = texture * direction; type row = square list; type quilt = row list;
We've already seen some of these examples. A square has a texture and a direction. A row is a list of squares. A quilt is a list of rows. Thus is quilt is a list of lists. [ [...] [...] [...] ]
(* utility functions *) fun clockwise ne = se | clockwise se = sw | clockwise sw = nw | clockwise nw = ne;
Note that parentheses are omitted, and "parameters" are constants. The interpretation of these is obvious (pattern matching used to match argument to parameter).
fun turnsq(tex,dir) =(tex,clockwise dir):square;
Building block for "turn". Returns a new object of type "square" with the direction rotated.
fun emptyquilt ([]) = true | emptyquilt([]::x) = emptyquilt(x:quilt) | emptyquilt(_) = false;
First case is empty list.
Second case is list whose first element is an empty list (e.g. first row is emtpy list).
Third case is "otherwise"
exception fail;
Declares an exception. Use with "raise" (equiv. to "throw") and with "handle" (equiv. to "catch")
(* the 4 basic elements of Little Quilt: a,b,turn,sew *) val a = [[ (arcs, ne) ]]; val b = [[ (bands, ne) ]];
Both a and b are single-square quilts, with the given texture and direction. Each quilt consists of a single row (list) and each row consists of a single square.
fun sew([],[]) = []:quilt | sew(r::x, s::y) = (r@s) :: sew(x,y) | sew(_) = raise fail;
(note Fig 9.7 has error in first line. ":quilt" should be at the end, not before the "=")
First case: sewing together two empty quilts yields empty quilt
Second case:
Third case: otherwise, raise exception (unequal heights).
If both quilts have same height, recursion leads eventually to first case. Otherwise, recursion leads eventually to third case (one of x or y is empty).
fun turn x = if emptyquilt x then nil else rev(map(turnsq o hd) x) :: turn(map tl x);
"nil" is equivalent to empty list.
Strategy for nonempty quilt is this:
- extract a "column" (first square in every row) into a list
- turn each individual square in that list
- reverse the order of list elements (result is a row in solution)
- repeat first three steps for each "column" (the recursive part)
Break last line of code down into two majors parts:
1. rev(map(turnsq o hd) x)
'rev' is builtin function to reverse a list; 'o' is function composition operator.
map(turnsq o hd) x
is equivalent to map turnsq (map hd x)Translation: Apply 'hd' to every row in x to extract square and apply 'turnsq' on that square. This processes one "column" into a new list. Finally rev reverses it.
2. turn(map tl x)
Recursively apply turn to next "column".
With each recursive step, a "column" is turned into a row, which becomes the next row of the resulting quilt.
The two steps combine to process the 2D grid like a nested loop where step 1 is the inner loop based on column number and step 2 is the outer loop based on row number.
Related Home Pages:
notes | CSC 326 | Peter Sanderson | Computer Science | SMSULast reviewed: 18 November 1999
Peter Sanderson ( PeteSanderson@mail.smsu.edu )