p
is to write #2 p
.
This style is not idiomatic or readable. The proper way to handle
this is by pattern matching, so
fun first (x, _) = x fun second (_, y) = yis preferred, and not
fun bogus_first p = #1 p fun bogus_second p = #2 p(For reasons I don't want to discuss, but will answer in class if asked, these versions don't even type-check.) If your pair or tuple is not an argument to a function, use
val
to do the pattern matching:
val (x, y) = lookup_pair mumbleBut usually you can include matching in ordinary
fun
matching.
Points will be deducted on homework for using #1
,
#2
, and their friends.
option
, which lets you
handle it.
The definition of option
is
datatype 'a option = NONE | SOME of 'aand it is already defined when you start the interactive system. You need not and should not define it yourself.
Some examples
- datatype chesspiece = K | Q | R | N | B | P - type square = chesspiece option - val empty : square = NONE - val lower_left : square = SOME R - fun play piece = SOME piece : square; > val play = fn : chesspiece -> chesspiece option - SOME true; > val it = SOME true : bool option - SOME 37; > val it = SOME 37 : int option - SOME "fish" = SOME "fowl"; > val it = false : bool - SOME "fish" = NONE; > val it = false : bool - "fish" = NONE; ! Toplevel input: ! "fish" = NONE; ! ^^^^ ! Type clash: expression of type ! 'a option ! cannot be made to have type ! string
The option
type is covered in Ullman on pages 111-113,
208, etc.
Array
structure
in Chapter 7, he
doesn't cover the immutable Vector
structure except for a
couple of pages deep in Chapter 9.
Like an array, a vector
offers constant-time access to an array of elements, but a vector is not
mutable.
Because of its immutability, Vector
is often preferred.
It is especially flexible when initialized with
Vector.tabulate
.
Here's the signature:
signature VECTOR = sig eqtype 'a vector val maxLen : int val fromList : 'a list -> 'a vector val tabulate : int * (int -> 'a) -> 'a vector val length : 'a vector -> int val sub : 'a vector * int -> 'a val extract : 'a vector * int * int option -> 'a vector val concat : 'a vector list -> 'a vector val app : ('a -> unit) -> 'a vector -> unit val foldl : ('a * 'b -> 'b) -> 'b -> 'a vector -> 'b val foldr : ('a * 'b -> 'b) -> 'b -> 'a vector -> 'b val appi : (int * 'a -> unit) -> 'a vector * int * int option -> unit val foldli : (int * 'a * 'b -> 'b) -> 'b -> 'a vector * int * int option -> 'b val foldri : (int * 'a * 'b -> 'b) -> 'b -> 'a vector * int * int option -> 'b endIt makes me deeply unhappy to have to warn you that the signature for
Vector
was changed in 2004, and that
although the MLton compiler has tracked this change, and Standard ML
of New Jersey has tracked parts of the change, Moscow ML has not
tracked the change at all. For simplicity, you are best off sticking
with Moscow ML and using MLton with the -basis 1997
option, but you need to know that these are not consistent with the
current documentation on the web.