Functions are algorithm
There are many implementations of sequence. The one we use is implemented using slices.
Constructors:
singleton
append (fuse)
empty
tabulate
Destructors:
map
reduce
nth
length
Convenience:
subseq
flatten
inject
Below algorithm is parallel, but time is still 2n + 1
map f nil = nil
map f (CONS (x, xs)) =
let
(y, ys) = (f x || map f xs)
in
CONS (y, ys)
end
Monoid: a sequence that is associative, and has an identity
Constructors: build datatype
Discriminator: separate constructed datatype, it tells us which case of datatype it is (singleton, empty, or many elements)
Iterator: calculate
Specification:
signature SEQ =
sig
(* Constructors *)
type 'a seq
one: 'a -> 'a seq
fuse: 'a seq * 'a seq -> 'a seq
empty: 'a seq
(* Destructors: discriminator *)
(* Destructors: iterator *)
end
In addition, you can use fold: ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
to transform a list into a sequence:
fold CONS EMPTY L
Remember how
fold
is defined. ```sml ( foldl : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b ) fun foldl g z [] = z | foldl g z (x::xs) = foldl g (g(x, z)) xs( foldr : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b ) fun foldr g z [] = z | foldr g z (x::xs) = g(x, foldr g z xs) ```
You can do calculation on a tree with mapReduce: ('b * 'b -> 'b) -> ('a -> 'b) -> 'b -> 'a seq -> 'b
: where f has to be total, pure, associative with unit e
mapReduce f s e
mapReduce
takes leaf function, combine function, and zero.
reduce
andscan
requires associativity. A good example of commutative but not associative is Rock-Paper-Scissors.
You can build list with tabulate: (int -> 'a) -> int -> 'a seq
(you input function that generates element, length), and its corresponding destructors are nth: 'a seq -> (int -> 'a)
and length: 'a seq -> int
.
Although you can calculate length A = mapReduce op+ (fn x => 1) 0
, it is not efficient because tabluate
may already stored in it data structure.
Table of Content