Documente Academic
Documente Profesional
Documente Cultură
Features of ML
A pure functional language
serious programs can be written without using variables
Widely accepted
reasonable performance (claimed) can be compiled syntax not as arcane (or as simple) as LISP
In these slides,
We use Standard ML of New Jersey Runs on PCs, Linux, and other flavors of UNIX Much of this material is based on Ullmans book, Elements of ML Programming
See the SML documentation at
http://www.smlnj.org
or equivalently
~nicholas/../pub/331/smlnj.linux/bin
Add this directory to your path, and do a rehash Then invoke sml from a shell prompt with the command sml Use control d to exit interpreter
Arithmetic in ML
Copy and paste the following text into a Standard ML window
2+2; 3*4; 4/3; 6 div 2; 7 div 3; (* note semicolon at end*) (* an error! *) (* integer division *)
Declaring Constants
Constants are not exactly the same as variables
they can be redefined, but existing uses of that constant (e.g. in function definitions) arent affected by such redefinition
val freezingFahr = 32;
- Int.abs ~3; val it = 3 : int - Int.sign ~3; val it = ~1 : int - Int.max (4, 7); val it = 7 : int - Int.min (~2, 2); val it = ~2 : int - Math.sqrt real(2); stdIn:57.1-57.18 Error: operator and operand don't agree [tycon mismatch] operator domain: real operand: int -> real in expression: Math.sqrt real - Math.sqrt(real(2)); val it = 1.41421356237 : real - Math.sqrt(real 3); val it = 1.73205080757 : real
Strings
Delimited by double quotes the caret mark ^ is used for string concatenation, e.g. house^cat \n is used for newline, as in C and C++
Comparison Operators
The usual <, >, <=, >= and <> are available For reals, = and <> are not available
For reals a and b, a <= b andalso a>= b is an equality test
The connectors andalso and orelse are logical operators with short-circuit evaluation
If Then Else
If Then Else is an expression, not a control structure Example, if quotient, dividend and divisor are reals, we might have
val quotient = if divisor > 0 then dividend/divisor else 0
Tuples
Tuples are data items drawn from a Cartesian product type. Example:
type fraction = int * int; val foo: fraction = (44,100); #1(foo); (* returns 44 *) #2(foo); (* returns 100 *)
Lists in ML
Objects in a list must be of the same type
[1,2,3]; [dog, cat, moose];
Making Lists
The @ operator is used to concatenate two lists of the same type The :: operator makes a new list in which its first operand is the new first element of a list which is otherwise like the second operand. The functions hd and tl give the first element of the list, and the rest of the list, respectively
List Operations
- val list1 = [1,2,3]; val list1 = [1,2,3] : int list - val list2 = [3,4,5]; val list2 = [3,4,5] : int list - list1@list2; val it = [1,2,3,3,4,5] : int list - hd list1; val it = 1 : int - tl list2; val it = [4,5] : int list
Declaring Functions
A function takes an input value and returns an output value ML will figure out the types
Notes
ML is picky about not mixing types, such as int and real, in expressions The value of it is always the last value computed Function arguments dont always need parentheses, but it doesnt hurt to use them
Generalizing Min
An example of ML pattern matching
the cons notation x::xs is both a binary constructor and a pattern cases arent supposed to overlap
MinList([1,2]);
MinList([315, 41, 59, 265, 35, 897]);
Building trees
Its easy to build recursive data types in ML Some examples follow
Expressions may be grouped with parentheses, e.g (print(a);print(b)) But the grouped expressions may not change the environment, so this is not the same as a block in a procedural language
The rec keyword stands for recursive, which is necessary because the binding of reverse as a function name is not yet established
Anonymous Functions
Functions dont have to have names, e.g. (fn x => x+1) (3) yields 4 Such functions can be passed as parameters, e.g. for use in the map or reduce functions, to be discussed later in this chapter.
is equivalent to
case E1 of true => E2 | false => E3
Exceptions
exception Foo and Bar; raise Foo; exception Foo of string; The handle clause matches exceptions with (hopefully) suitable actions Exceptions can be defined in let clauses
Polymorphic Functions
If you dont know the type in advance, or if it doesnt matter, a list matches a list of any type Example:
fun listLen(x: a list) = if x = nil then 0
else 1+listLen(tl(x));
More on reduce
Harper gives a more general form of reduce
fun reduce (unit, opn, nil) = unit | reduce (unit, opn, h::t) = opn(h, reduce (unit, opn, t))
or
fun add_up alist = reduce (0, op +, alist)
More on reduce
To avoid passing unit and opn as parameters that dont change, again from Harpers book,
fun better_reduce (unit, opn, alist) = nil = unit | red (h::t) = opn(h, red t)) in red alist end let fun red
More on reduce
Staging helps even more! Again from Harper
fun staged_reduce (unit, opn) = = unit | red (h::t) = opn(h, red t)) in red end let fun red nil
Datatypes
Enumerated types, e.g.
datatype berryType = raspberry | blueberry | blackberry;
Recursive Datatypes
Example: binary trees, where the values may be of some type label:
datatype label btree = Empty | Node of label * label btree * label btree
ASTs Revisited
(* Sample ML program - Abstract Syntax Trees *) (* Assume that terminalType and nonterminalType already known *) (* Declare the ast datatype *) datatype ast = empty | leaf of terminalType | node of nonterminalType*(ast list); fun traverse(empty) = print "empty tree" | traverse(leaf(t)) = (printTerminal t; print " ") | traverse(node(nt, []) = printNonterminal(nt) | traverse(node(nt, x::xs)) = (printNonterminal(nt); traverse(x); traverseList(xs)) and fun traverseList([]) = print | traverseList(x::xs) = (traverse(x); traverseList(xs));
Record Structures
Records are wrapped in curly braces, and fields are separated by commas Field names may be used to refer to specific elements of the record
Record Example
- type addressType = {street:string, city:string, zip:int}; type addressType = {city:string, street:string, zip:int} (note that SML sorted the fields alphabetically) - val umbc:addressType = {street="1000 Hilltop Circle", city="Baltimore",zip=21250}; val umbc = {city="Baltimore",street="1000 Hilltop Circle", zip=21250} : addressType - #city(umbc); val it = "Baltimore" : string
If we dont care about all the fields, use an ellipsis, e.g. x as {street=xstr,}::xs Or even x as {city,}
Arrays
open Array; val zeroVector = array(100,0); sub(zeroVector,0) is zero, as is sub(zeroVector,99) update(zeroVector,2,3.14) changes the third element of the (now misnamed) zeroVector
Case Studies
Hash tables
Make an array of hash buckets, each bucket containing a simple list of values
Triangularization of a matrix
If the array has m rows and n columns, make an array of m elements, each element being an array of n elements.