http://t3x.org/mlite/examples.html (light|dark)
Not impressed yet? Here's more...
The perm
function generates all permutions of a list:
fun perm [x] = [[x]] (* 1 *) | x = let val x = rot x (* 2 *) in let val hs = map head x (* 3 *) and ts = map (perm o tail) x (* 4 *) in append_map dist ` zip hs ts (* 5 *) end end ; perm [1,2,3] --> [[1, 2, 3], [1, 3, 2], [2, 3, 1], [2, 1, 3], [3, 1, 2], [3, 2, 1]]
How does it work?
(1) There is only one permutation of a single-element list.
(2) The rot
function turns x
into a list
of rotations of x
. It is defined as follows:
fun rot (_, 0) = [] | (x :: xs, k) = (x :: xs) :: rot (xs @ [x], k - 1) | x = rot (x, len x) ; rot [1,2,3] --> [[1, 2, 3], [2, 3, 1], [3, 1, 2]]
(3) map head
extracts the heads from a list of
rotations:
map head [[1, 2, 3], [2, 3, 1], [3, 1, 2]] --> [1, 2, 3]
(4) map (perm o tail)
extracts the tails
of the rotations and generates the permutations of the tails by recursing.
perm o tail
is the function composition of
perm
and tail
.
map (perm o tail) [[1, 2, 3], [2, 3, 1], [3, 1, 2]] --> [[[2, 3], [3, 2]], [[3, 1], [1, 3]], [[1, 2], [2, 1]]]
(5) The zip
function creates tuples from the heads and
permutated tails:
zip [1,2,3] [[[2, 3], [3, 2]], [[3, 1], [1, 3]], [[1, 2], [2, 1]]] --> [(1, [[2, 3], [3, 2]]), (2, [[3, 1], [1, 3]]), (3, [[1, 2], [2, 1]])]
and append_map
maps dist
over the above result.
Unlike map
, append_map
appends its results.
The dist
function distributes its first element over the
lists in its second argument:
fun dist (x, a :: as) = (x :: a) :: dist (x, as) | (x, []) = [] ; dist (1, [[2, 3], [3, 2]]) --> [[1, 2, 3], [1, 3, 2]]
So the final result of the function is this: (@
denotes
the concatenation done by append_map
)
dist (1, [[2, 3], [3, 2]]) @ dist (2, [[3, 1], [1, 3]]) @ dist (3, [[1, 2], [2, 1]]) --> [[1, 2, 3], [1, 3, 2], [2, 3, 1], [2, 1, 3], [3, 1, 2], [3, 2, 1]]
Does it make sense now? Have another look! Or check out other examples.