|
|
SML |
Back to the Win32 Shootout Back to dada's perl lab |
| All Source For smlnj |
|---|
| Ackermann's Function |
(* -*- mode: sml -*-
* $Id: ackermann.smlnj,v 1.5 2001/07/09 00:25:27 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun ack(0,n) = n+1
| ack(m,0) = ack(m-1,1)
| ack(m,n) = ack(m-1,ack(m,(n-1)));
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t );
fun main(name, args) =
let
val arg = hd(args @ ["1"]);
val num = atoi arg;
val ack = ack(3, num);
val result = Int.toString ack;
in
printl ["Ack(3,", arg, "): ", result];
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("ackermann", Test.main);
|
| Array Access |
(* -*- mode: sml -*-
* $Id: ary3.smlnj,v 1.2 2001/07/09 00:25:27 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun index i = i;
fun ary n =
let
val x = Array.array(n, 0)
val y = Array.array(n, 0)
fun xinit i =
if i = n then ()
else (Array.update(x, i, i + 1) ; xinit (i + 1))
fun xtoy i =
if i < 0 then ()
else (Array.update(y, i, Array.sub(x, i) + Array.sub(y, i)) ; xtoy (i - 1))
fun aryloop i =
if i < 0 then ()
else (xtoy(n-1); aryloop (i-1))
in
xinit 0;
aryloop 999;
print (Int.toString (Array.sub(y, 0)));
print " ";
print (Int.toString (Array.sub(y, (n-1))));
print "\n"
end;
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun main(name, args) =
let
val arg = hd(args @ ["1"])
val num = atoi arg
in
ary num;
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("ary3", Test.main);
|
| Count Lines/Words/Chars |
(* -*- mode: sml -*-
* $Id: wc.smlnj,v 1.2 2001/07/09 00:25:29 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun incr r = r := !r + 1
val nl = ref 0
val nw = ref 0
val nc = ref 0
val max = 4096
val buf = Word8Array.array (max, 0w0)
val sub = Word8Array.sub
fun readblock scanfun =
let
val nread = Posix.IO.readArr (Posix.FileSys.stdin,
{buf = buf, i = 0, sz = NONE})
in
if nread = 0
then ()
else (nc := !nc + nread;
scanfun (0, nread))
end
val c2b = Byte.charToByte
val newline = c2b #"\n"
val space = c2b #" "
val tab = c2b #"\t"
fun scan_out_of_word (i, n) =
if i < n
then
let
val c = sub (buf, i)
in
if c = newline
then (incr nl; scan_out_of_word (i + 1, n))
else if c = space orelse c = tab
then scan_out_of_word (i + 1, n)
else (incr nw; scan_in_word (i + 1, n))
end
else readblock scan_out_of_word
and scan_in_word (i, n) =
if i < n then
let
val c = sub (buf, i)
in
if c = newline
then (incr nl; scan_out_of_word (i + 1, n))
else if c = space orelse c = tab
then scan_out_of_word (i + 1, n)
else scan_in_word (i + 1, n)
end
else readblock scan_in_word
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t )
fun main (name, args) =
let
val _ =
(scan_out_of_word (0, 0);
printl [Int.toString (!nl), " ", Int.toString (!nw), " ", Int.toString (!nc)])
in
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("wc", Test.main);
|
| Echo Client/Server |
(* -*- mode: sml -*-
* $Id: echo.smlnj,v 1.1 2001/07/10 00:47:14 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Daniel Wang
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end =
struct
exception Error of string
val data = "Hello there sailor\n"
fun mkSocks () = let
val server = INetSock.TCP.socket()
val client = INetSock.TCP.socket()
val _ = Socket.bind(server,INetSock.any 0)
val saddr = INetSock.fromAddr(Socket.Ctl.getSockName server)
val _ = Socket.listen(server,2)
val _ = Socket.connect(client,INetSock.toAddr saddr)
val _ = INetSock.TCP.setNODELAY(server,true)
val _ = INetSock.TCP.setNODELAY(client,true)
in {client=client,server=server}
end
fun readString (s,n) = let
fun loop(0) = []
| loop(n) = let
val data = Byte.bytesToString(Socket.recvVec(s,n))
val len = String.size data
in if len = 0 then []
else (data::(loop(n - len)))
end
in String.concat (loop n)
end
fun writeString (out,str) =
Socket.sendVec(out,{buf=Byte.stringToBytes str,i=0,sz=NONE})
fun closeSock s =
(Socket.shutdown(s,Socket.NO_RECVS_OR_SENDS);
Socket.close s)
fun main (_,args) = let
val num =
case args of
nil => 1
| n::_ => valOf (Int.fromString n)
val {client=client_sock,server=server_sock} = mkSocks()
fun server () = let
val (sock,_) = Socket.accept(server_sock)
fun s b =
case readString(sock,19) of
"" => (Posix.Process.wait ();
TextIO.output(TextIO.stdOut,
concat ["server processed ",
Int.toString b,
" bytes\n"]);
TextIO.flushOut(TextIO.stdOut))
| i =>(writeString(sock,i);
s (b + 19))
in s 0
end
fun client () = let
fun c 0 = closeSock(client_sock)
| c n = let
val _ = writeString(client_sock,data);
val reply = readString(client_sock,19)
in if reply = data then c(n - 1)
else raise Error "Didn't receive the same data"
end
in c num
end
in
case Posix.Process.fork () of
SOME pid => server ()
| NONE => client ();
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("echo",Test.main);
|
| Exception Mechanisms |
(* -*- mode: sml -*-
* $Id: except.smlnj,v 1.2 2001/07/09 00:25:27 doug Exp $
* http://www.bagley.org/~doug/shootout/
* Translated from except.ocaml by Stephen Weeks
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun incr r = r := !r + 1
fun print_endline s = (print s; print "\n")
fun for (start, stop, f) =
let
fun loop i =
if i > stop
then ()
else (f i; loop (i + 1))
in
loop start
end
exception HiException of int
exception LoException of int
val hi = ref 0
val lo = ref 0
fun blowup n =
if n mod 2 = 0 then raise (LoException n)
else raise (HiException n)
fun lo_fun n =
blowup n
handle LoException ex => incr lo
fun hi_fun n =
lo_fun n
handle HiException ex => incr hi
fun some_fun n =
hi_fun n
handle x =>
(print_endline "Should not get here.";
raise x)
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t );
fun main (name, args) =
let
val n = atoi (hd (args @ ["1"]))
val _ = for (1, n, some_fun)
in
printl ["Exceptions: HI=",
Int.toString (!hi),
" / LO=",
Int.toString (!lo)];
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("except", Test.main);
|
| Fibonacci Numbers |
(* -*- mode: sml -*-
* $Id: fibo.smlnj,v 1.5 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun fib 0 = 1
| fib 1 = 1
| fib n = fib (n-2) + fib (n-1);
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun main(name, args) =
let
val arg = hd(args @ ["1"]);
val num = atoi arg;
val fib = fib num;
val result = Int.toString fib;
in
print result; print "\n";
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("fibo", Test.main);
|
| Hash (Associative Array) Access |
(* -*- mode: sml -*-
* $Id: hash.smlnj,v 1.3 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
open HashTable;
fun hashtest n =
let
val hx = mkTable (HashString.hashString, op =) (n, (Fail "not found"))
fun doinserts i n =
if i < n then (
insert hx ((Int.fmt StringCvt.HEX i), i);
doinserts (i+1) n
) else ()
fun dolookups i c =
if i > 0 then
case find hx (Int.toString i) of
SOME key => dolookups (i-1) (c+1)
| _ => dolookups (i-1) c
else c
in (
doinserts 0 n;
dolookups n 0
) end;
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun main(name, args) =
let
val arg = hd(args @ ["1"])
val num = atoi arg
val result = hashtest num
in
print (Int.toString result) ; print "\n" ;
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("hash", Test.main);
|
| Hashes, Part II |
(* -*- mode: sml -*-
* $Id: hash2.smlnj,v 1.3 2001/07/10 13:01:54 doug Exp $
* http://www.bagley.org/~doug/shootout/
* Modified by Daniel Wang
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
open HashTable;
fun hashtest2 n =
let
exception NotFound
val h1 = mkTable (HashString.hashString, op =) (10000, NotFound)
val h2 = mkTable (HashString.hashString, op =) (10000, NotFound)
fun doinserts1 i =
if i < 10000 then (
insert h1 ("foo_" ^ (Int.toString i), i);
doinserts1 (i+1)
) else ()
fun addinto h k v1 =
case find h k of
SOME valref => valref := (!valref) + v1
| NONE => insert h (k, ref v1)
fun doinserts2 i =
if i < n then (
appi (fn (k,v) => (addinto h2 k v)) h1;
doinserts2 (i+1)
) else ()
in (
doinserts1 0;
doinserts2 0;
print (Int.toString (lookup h1 "foo_1")); print " ";
print (Int.toString (lookup h1 "foo_9999")); print " ";
print (Int.toString (!(lookup h2 "foo_1"))); print " ";
print (Int.toString (!(lookup h2 "foo_9999")));
print "\n"
) end;
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun main(name, args) =
let
val arg = hd(args @ ["1"])
val num = atoi arg
in
hashtest2 num;
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("hash2", Test.main);
|
| Heapsort |
(* -*- mode: sml -*-
* $Id: heapsort.smlnj,v 1.3 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
* Based on cdoe from Stephen Weeks, improved by Henry Cejtin.
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
val sub = Array.sub
val update = Array.update
local
val im = 139968
val ia = 3877
val ic = 29573
val last = ref 42
val scale = 1.0 / Real.fromInt im
in
fun gen_random max =
let val last' = (! last * ia + ic) mod im
in last := last';
max * scale * Real.fromInt last'
end
end
fun heapSort (n, ra: real array) =
let fun inner (l, ir, rra) =
let fun loop (i, j) =
if j <= ir
then let val j =
if j < ir
andalso sub (ra, j) < sub (ra, j + 1)
then j + 1
else j
val (i, j) =
if rra < sub (ra, j)
then (update (ra, i, sub (ra, j));
(j, j + j))
else (i, ir + 1)
in loop (i, j)
end
else update (ra, i, rra)
in loop (l, l + l)
end
fun outer1 l =
let val l' = l - 1
in if l' > 0
then (inner (l', n, sub (ra, l'));
outer1 l')
else ()
end
fun outer2 ir =
let val rra = sub (ra, ir)
val _ = update (ra, ir, sub (ra, 1))
val ir = ir - 1
in if ir = 1
then update (ra, 1, rra)
else (inner (1, ir, rra);
outer2 ir)
end
in outer1 (n div 2 + 1);
outer2 n
end
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t );
fun main (name, args) =
let val n = atoi (hd (args @ ["1"]))
val ary = Array.tabulate (n + 1, fn _ => gen_random 1.0)
in
heapSort (n, ary);
print (concat [Real.fmt (StringCvt.FIX (SOME 10)) (sub (ary, n)),
"\n"]);
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("heapsort", Test.main);
|
| Hello World |
(* -*- mode: sml -*-
* $Id: hello.smlnj,v 1.2 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun main(name, args) =
let in print "hello world\n"; OS.Process.success end;
end
val _ = SMLofNJ.exportFn("hello", Test.main);
|
| List Operations |
(* -*- mode: sml -*-
* $Id: lists.smlnj,v 1.2 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
val sub = Array.sub
val update = Array.update
fun for (start, stop, f) =
let
fun loop i =
if i > stop
then ()
else (f i; loop (i + 1))
in
loop start
end
fun failwith s = raise Fail s
structure Deque:
sig
type 'a t
exception Empty
val make: int * 'a -> 'a t
val iota: int -> int t
val is_empty: 'a t -> bool
val equal: ''a t * ''a t -> bool
val length: 'a t -> int
val nth: 'a t * int -> 'a
val push_front: 'a * 'a t -> unit
val push_back: 'a t * 'a -> unit
val take_front: 'a t -> 'a
val take_back: 'a t -> 'a
val copy: 'a t -> 'a t
val reverse: 'a t -> 'a t
end =
struct
type 'a t = {size: int ref,
first: int ref,
last: int ref,
field: 'a array ref,
fill: 'a}
local
fun make sel (d: 'a t) = sel d
in
fun fill z = make #fill z
end
local
fun make sel (d: 'a t) = !(sel d)
in
fun field z = make #field z
fun first z = make #first z
fun last z = make #last z
fun size z = make #size z
end
exception Empty
fun make (n, dummy) =
let
val n = Int.max (n, 0)
val nplus = Int.max (1, n)
in
{size = ref nplus,
first = ref (Int.quot (nplus, 2)),
last = ref (Int.quot (nplus, 2) - 1),
field = ref (Array.array (nplus, dummy)),
fill = dummy}
end
fun iota i =
let
val i = Int.max (0, i)
val iplus = Int.max (1, i)
in
{size = ref iplus,
first = ref 0,
last = ref (i - 1),
field = ref (Array.tabulate (iplus, fn n => n + 1)),
fill = i}
end
fun length buf = last buf - first buf + 1
fun is_empty buf = last buf < first buf
fun array_eq (arr1, off1, arr2, off2, i) =
let
fun loop (off1, off2, i) =
case i of
0 => true
| n =>
sub (arr1, off1) = sub (arr2, off2)
andalso loop (off1 + 1, off2 + 1, n - 1)
in loop (off1, off2, i)
end
fun equal (buf1, buf2) =
let
val len = length buf1
in
len = length buf2
andalso array_eq (field buf1, first buf1,
field buf2, first buf2,
len)
end
fun nth (buf, n) =
if n < 0 orelse n >= length buf
then failwith "nth"
else sub (field buf, first buf + n)
fun double_shift buf =
let
val new_size = size buf * 2
val len = length buf
val new_first = Int.quot (new_size - len, 2)
val new_field = Array.array (new_size, fill buf)
val _ = Array.copy {src = field buf,
si = first buf,
dst = new_field,
di = new_first,
len = SOME len}
in
#size buf := new_size;
#field buf := new_field;
#first buf := new_first;
#last buf := new_first + len - 1
end
fun push_front (elem, buf) =
let
val _ = if first buf = 0 then double_shift buf else ()
val new_first = first buf - 1
in
update (field buf, new_first, elem);
#first buf := new_first
end
fun push_back (buf, elem) =
let
val _ = if last buf = size buf - 1 then double_shift buf else ()
val new_last = last buf + 1
in
update (field buf, new_last, elem);
#last buf := new_last
end
fun take_front buf =
if is_empty buf
then raise Empty
else
let
val old_first = first buf
in
#first buf := old_first + 1;
sub (field buf, old_first)
end
fun take_back buf =
if is_empty buf
then raise Empty
else
let
val old_last = last buf
in
#last buf := old_last - 1;
sub (field buf, old_last)
end
fun copy buf =
let
val len = length buf
val new_buf = make (len, fill buf)
val _ = Array.copy {src = field buf,
si = first buf,
dst = field new_buf,
di = 0,
len = SOME len}
in
#first new_buf := 0;
#last new_buf := len - 1;
new_buf
end
fun reverse buf =
let
val len = length buf
val fst = first buf
val fld = field buf
val new_buf = make (len, fill buf)
val new_fld = field new_buf
val _ =
for (0, len - 1, fn i =>
update (new_fld, len - i - 1, sub (fld, fst + i)))
in
#first new_buf := 0;
#last new_buf := len - 1;
new_buf
end
end
open Deque
fun empty () = iota 0
val size = 10000
fun test_lists () =
let
val d1 = iota size
val d2 = copy d1
val d3 = empty ()
val _ = for (1, length d2, fn _ => push_back (d3, take_front d2))
val _ = for (1, length d3, fn _ => push_back (d2, take_back d3))
val d1 = reverse d1
val _ = if size <> nth (d1, 0) then failwith "First test failed" else ()
val _ = if length d1 <> length d2 then failwith "Second test failed" else ()
val _ = if not (equal (d1, d2)) then failwith "Third test failed" else ()
in
length d1
end
fun main (name, args) =
let
val n =
case Int.fromString (hd (args @ ["1"])) of
NONE => 1
| SOME n => n
val result = ref 0
val _ = for (1, n, fn _ => result := test_lists ())
in
print (concat [Int.toString (!result), "\n"]);
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("lists", Test.main);
|
| Matrix Multiplication |
(* -*- mode: sml -*-
* $Id: matrix.smlnj,v 1.3 2001/07/11 01:40:04 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun incr r = r := !r + 1
fun for (start, stop, f) =
let
fun loop i =
if i > stop
then ()
else (f i; loop (i + 1))
in
loop start
end
structure Array2 =
struct
datatype 'a t = T of 'a array array
fun sub (T a, r, c) = Array.sub (Array.sub (a, r), c)
fun subr (T a, r) =
let val a = Array.sub (a, r)
in fn c => Array.sub (a, c)
end
fun update (T a, r, c, x) = Array.update (Array.sub (a, r), c, x)
fun array (r, c, x) =
T (Array.tabulate (r, fn _ => Array.array (c, x)))
end
val sub = Array2.sub
val update = Array2.update
val size = 30
fun mkmatrix (rows, cols) =
let
val count = ref 1
val last_col = cols - 1
val m = Array2.array (rows, cols, 0)
in
for (0, rows - 1, fn i =>
for (0, last_col, fn j =>
(update (m, i, j, !count)
; incr count)));
m
end
fun mmult (rows, cols, m1, m2, m3) =
let
val last_col = cols - 1
val last_row = rows - 1
in
for (0, last_row, fn i =>
for (0, last_col, fn j =>
update (m3, i, j,
let
val m1i = Array2.subr (m1, i)
fun loop (k, sum) =
if k < 0
then sum
else loop (k - 1,
sum + m1i k * sub (m2, k, j))
in loop (last_row, 0)
end)))
end
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t );
fun main (name, args) =
let
val n = atoi (hd (args @ ["1"]))
val m1 = mkmatrix (size, size)
val m2 = mkmatrix (size, size)
val m3 = Array2.array (size, size, 0)
val _ = for (1, n - 1, fn _ => mmult (size, size, m1, m2, m3))
val _ = mmult (size, size, m1, m2, m3)
in
printl [Int.toString (sub (m3, 0, 0)),
" ",
Int.toString (sub (m3, 2, 3)),
" ",
Int.toString (sub (m3, 3, 2)),
" ",
Int.toString (sub (m3, 4, 4))];
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("matrix", Test.main)
|
| Method Calls |
(* -*- mode: sml -*-
* $Id: methcall.smlnj,v 1.2 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun for (start, stop, f) =
let
fun loop i =
if i > stop
then ()
else (f i; loop (i + 1))
in
loop start
end
structure Toggle =
struct
datatype 'a t = T of {
state: 'a ref,
value: 'a t -> 'a,
activate: 'a t -> 'a t
}
fun new state =
T {state = ref state,
value = fn T {state, ...} => !state,
activate = fn this as T {state, ...} => (state := not(!state); this)}
fun activate (this as T {activate, ...}) = activate this
fun value (this as T {value, ...}) = value this
end
structure Ntoggle =
struct
datatype 'a t = T of {
state: 'a ref,
value: 'a t -> 'a,
activate: 'a t -> 'a t,
countMax: int,
counter: int ref
}
fun new (state, countMax) =
T {
state = ref state,
value = fn T {state, ...} => !state,
activate = (fn this as T {state, counter, countMax, ...} =>
let
val newCounter = 1 + !counter
val _ = counter := newCounter
val _ =
if !counter >= countMax
then (state := not(!state);
counter := 0)
else ()
in
this
end),
countMax = countMax,
counter = ref 0
}
fun activate (this as T {activate, ...}) = activate this
fun value (this as T {value, ...}) = value this
end
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t )
fun main (name, args) =
let
val n = atoi (hd (args @ ["1"]))
val v = ref true
val tog = Toggle.new true
val _ = for (0, n - 1, fn _ => v := Toggle.value (Toggle.activate tog))
val _ = print (if !v then "true\n" else "false\n")
val _ = v := true
val ntog = Ntoggle.new (!v, 3)
val _ = for (0, n - 1, fn _ => v := Ntoggle.value (Ntoggle.activate ntog))
val _ = print (if !v then "true\n" else "false\n")
in
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("methcall", Test.main);
|
| Nested Loops |
(* -*- mode: sml -*-
* $Id: nestedloop.smlnj,v 1.3 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun loopF 0 x = x
| loopF n x = loopF (n-1) (x+1)
fun loopE 0 m x = x
| loopE n m x = loopE (n-1) m (loopF m x);
fun loopD 0 m x = x
| loopD n m x = loopD (n-1) m (loopE m m x);
fun loopC 0 m x = x
| loopC n m x = loopC (n-1) m (loopD m m x);
fun loopB 0 m x = x
| loopB n m x = loopB (n-1) m (loopC m m x);
fun loopA 0 m x = x
| loopA n m x = loopA (n-1) m (loopB m m x);
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun main(name, args) =
let
val arg = hd(args @ ["1"]);
val num = atoi arg;
val result = loopA num num 0
in
print (Int.toString result); print "\n";
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("nestedloop", Test.main);
|
| Object Instantiation |
(* -*- mode: sml -*-
* $Id: objinst.smlnj,v 1.3 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun for (start, stop, f) =
let
fun loop i =
if i > stop
then ()
else (f i; loop (i + 1))
in
loop start
end
structure Toggle =
struct
datatype 'a t = T of {
state: 'a ref,
value: 'a t -> 'a,
activate: 'a t -> 'a t
}
fun new state =
T {state = ref state,
value = fn T {state, ...} => !state,
activate = fn this as T {state, ...} => (state := not(!state); this)}
fun activate (this as T {activate, ...}) = activate this
fun value (this as T {value, ...}) = value this
end
structure Ntoggle =
struct
datatype 'a t = T of {
state: 'a ref,
value: 'a t -> 'a,
activate: 'a t -> 'a t,
countMax: int,
counter: int ref
}
fun new (state, countMax) =
T {
state = ref state,
value = fn T {state, ...} => !state,
activate = (fn this as T {state, counter, countMax, ...} =>
let
val newCounter = 1 + !counter
val _ = counter := newCounter
val _ =
if !counter >= countMax
then (state := not(!state);
counter := 0)
else ()
in
this
end),
countMax = countMax,
counter = ref 0
}
fun activate (this as T {activate, ...}) = activate this
fun value (this as T {value, ...}) = value this
end
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t )
fun main (name, args) =
let
val n = atoi (hd (args @ ["1"]))
val v = ref true
val tog = Toggle.new true
val _ = for (0, 4, fn _ =>
print (if Toggle.value (Toggle.activate tog)
then "true\n"
else "false\n"))
val r = ref (Toggle.new false)
val _ = for (0, n - 1, fn _ => r := Toggle.new true)
val _ = Toggle.activate (!r)
val _ = print "\n"
val ntog = Ntoggle.new (true, 3)
val _ = for (0, 7, fn _ =>
print (if Ntoggle.value (Ntoggle.activate ntog)
then "true\n"
else "false\n"))
val r2 = ref (Ntoggle.new (true, 3))
val _ = for (0, n - 1, fn _ => r2 := Ntoggle.new (true, 3))
val _ = Ntoggle.activate (!r2)
in
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("objinst", Test.main);
|
| Producer/Consumer Threads |
(* -*- mode: sml -*-
* $Id: prodcons.smlnj,v 1.1 2001/09/01 01:40:21 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Matthias Blume
*)
(* producer-consumer threads in SML/NJ
* (concurrency primitives re-implemented "by hand" on top of call/cc
* using the code in John Reppy's book "Concurrent Programming in ML")
*
* (C) 2001 Lucent Technologies, Bell Labs
* written by Matthias Blume
*)
structure Queue :> sig
exception Empty
type tt = unit SMLofNJ.Cont.cont
type q
val new : unit -> q
val enqueue : q * tt -> unit
val dequeue : q -> tt
val empty : q -> bool
end = struct
exception Empty
type tt = unit SMLofNJ.Cont.cont
type q = tt list ref * tt list ref
fun new () : q = (ref [], ref [])
fun enqueue ((f as ref [], ref []) : q, x) = f := [x]
| enqueue ((_, b as ref xs), x) = b := x :: xs
fun dequeue ((f, b) : q) =
case !f of
[] => (case rev (!b) of
x :: xs => (f := xs; b := []; x)
| [] => raise Empty)
| x :: xs => (f := xs; x)
fun empty ((ref [], ref []) : q) = true
| empty _ = false
end
structure Mutex :> sig
val yield : unit -> unit
val fork : (unit -> unit) -> unit
val exit : unit -> 'a
type mutex
type condition
val mutex : unit -> mutex
val lock : mutex -> unit
val unlock : mutex -> unit
val condition : mutex -> condition
val wait : condition -> unit
val signal : condition -> unit
val run : (unit -> unit) * Time.time -> unit
end = struct
local
structure C = SMLofNJ.Cont
structure Q = Queue
type tt = unit C.cont
(* We take the easy way out: Simply drop signals that
* arrive during an atomic section on the floor. This is
* enough for our purpose and simplifies the coding... *)
val atomicState = ref false
fun atomicBegin () = atomicState := true
fun atomicEnd () = atomicState := false
val readyQ : Q.q = Q.new ()
fun dispatch () = C.throw (Q.dequeue readyQ) ()
fun sigH (_: Signals.signal, _: int, k: tt) =
if !atomicState then k
else (Q.enqueue (readyQ, k); Q.dequeue readyQ)
in
fun yield () =
(atomicBegin ();
C.callcc (fn k => (Q.enqueue (readyQ, k); dispatch ()));
atomicEnd ())
fun exit () = (atomicBegin (); dispatch ())
fun fork f = let
val k = C.isolate (fn () => (atomicEnd ();
f () handle _ => ();
exit ()))
in
atomicBegin ();
Q.enqueue (readyQ, k);
atomicEnd ()
end
datatype mutex = Mutex of { locked : bool ref, blocked : Q.q }
fun mutex () = Mutex { locked = ref false, blocked = Q.new () }
fun lock (Mutex { locked, blocked }) =
(atomicBegin ();
if !locked then
C.callcc (fn k => (Q.enqueue (blocked, k);
dispatch ()))
else locked := true;
atomicEnd ())
fun unlock (Mutex { locked, blocked }) =
(atomicBegin ();
if Q.empty blocked then locked := false
else C.callcc (fn k => (Q.enqueue (readyQ, k);
C.throw (Q.dequeue blocked) ()));
atomicEnd ())
datatype condition = Cond of { mutex : mutex, waiting : Q.q }
fun condition m = Cond { mutex = m, waiting = Q.new () }
fun wait (Cond { mutex = m as Mutex { locked, blocked }, waiting }) =
(atomicBegin ();
C.callcc (fn k =>
(Q.enqueue (waiting, k);
if Q.empty blocked then (locked := false;
dispatch ())
else C.throw (Q.dequeue blocked) ()));
if !locked then
C.callcc (fn k => (Q.enqueue (blocked, k);
dispatch ()))
else locked := true;
atomicEnd ())
fun signal (Cond { waiting, ... }) =
(atomicBegin ();
if Q.empty waiting then ()
else Q.enqueue (readyQ, Q.dequeue waiting);
atomicEnd ())
fun run (f, t) = let
val oh = Signals.setHandler (Signals.sigALRM,
Signals.HANDLER sigH)
val _ = SMLofNJ.IntervalTimer.setIntTimer (SOME t)
fun reset () =
(ignore (Signals.setHandler (Signals.sigALRM, oh));
SMLofNJ.IntervalTimer.setIntTimer NONE)
in
(f () handle e => (reset (); raise e))
before reset ()
end
end
end
structure ProdCons : sig
val main : string * string list -> OS.Process.status
end = struct
fun doit n = let
val c_running = Mutex.mutex ()
val p_running = Mutex.mutex ()
val consumer's_turn = ref false
val data = ref 0
val produced = ref 0
val consumed = ref 0
val m = Mutex.mutex ()
val c = Mutex.condition m
fun producer () = let
fun wait () = if !consumer's_turn then wait (Mutex.wait c) else ()
fun loop i =
if i <= n then
let val _ = Mutex.lock m
val _ = wait ()
in
data := i;
consumer's_turn := true;
produced := !produced + 1;
Mutex.signal c;
Mutex.unlock m;
loop (i + 1)
end
else ()
in
loop 1 before Mutex.unlock p_running
end
fun consumer () = let
fun wait () = if !consumer's_turn then () else wait (Mutex.wait c)
fun loop () = let
val _ = Mutex.lock m
val _ = wait ()
val i = !data
in
consumer's_turn := false;
consumed := !consumed + 1;
Mutex.signal c;
Mutex.unlock m;
if i <> n then loop () else ()
end
in
loop () before Mutex.unlock c_running
end
val _ = Mutex.lock p_running
val _ = Mutex.lock c_running
val p = Mutex.fork producer
val c = Mutex.fork consumer
in
Mutex.lock p_running;
Mutex.lock c_running;
TextIO.output (TextIO.stdOut,
concat [Int.toString (!produced), " ",
Int.toString (!consumed), "\n"])
end
fun main (_, args) = let
val n = case args of [] => 1
| (x :: _) => getOpt (Int.fromString x, 1)
in
Mutex.run (fn () => doit n, Time.fromMilliseconds 1);
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("prodcons", ProdCons.main);
|
| Random Number Generator |
(* -*- mode: sml -*-
* $Id: random.smlnj,v 1.5 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
val im : int = 139968;
val ia : int = 3877;
val ic : int = 29573;
fun randloop 0 seed rand max = rand
| randloop n seed rand max =
let
val newseed : int = (seed * ia + ic) mod im;
val newrand : real = max * (Real.fromInt newseed) / (Real.fromInt im);
in
randloop (n-1) newseed newrand max
end;
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun main(name, args) =
let
val arg = hd(args @ ["1"]);
val num = atoi arg;
val result = randloop num 42 0.0 100.0;
in
print (Real.fmt (StringCvt.FIX (SOME 9)) result); print "\n";
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("random", Test.main);
|
| Regular Expression Matching |
(* -*- mode: sml -*-
* $Id: regexmatch.smlnj,v 1.1 2001/07/10 13:04:21 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
* "ported" to SML/NJ
* with help from Daniel Wang
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun ++ (r: int ref): int =
let
val n = 1 + !r
val _ = r := n
in n
end
structure Int =
struct
open Int
type t = int
fun exists (start: t, stop: t, f: t -> bool): bool =
let
fun loop i = i < stop andalso (f i orelse loop (i + 1))
in
loop start
end
fun forall (start, stop, f) = not (exists (start, stop, not o f))
fun fold (start: t, stop: t, a: 'a, f: t * 'a -> 'a): 'a =
let
fun loop (i: t, a: 'a) =
if i >= stop
then a
else loop (i + 1, f (i, a))
in loop (start, a)
end
fun for (start: t, stop: t, f: t -> unit): unit =
let
fun loop i =
if i >= stop
then ()
else (f i; loop (i + 1))
in
loop start
end
end
structure Array2 =
struct
open Array2
type 'a t = 'a array
val new = array
fun tabulate (r, c, f) = Array2.tabulate RowMajor (r, c, f)
fun foreachi (a, f) =
appi RowMajor f {base = a, row = 0, col = 0,
nrows = NONE, ncols = NONE}
end
structure Vector =
struct
open Vector
fun exists (v, f) =
Int.exists (0, length v, fn i => f (sub (v, i)))
fun foreach (v, f) = app f v
fun foreachi (v, f) = appi f (v, 0, NONE)
fun new (n, x) = tabulate (n, fn _ => x)
fun new1 x = new (1, x)
end
structure List =
struct
open List
fun foreach (l, f) = app f l
fun fold (l, b, f) = foldl f b l
fun appendRev (l1, l2) = fold (l1, l2, op ::)
fun push (r, x) = r := x :: !r
fun keepAll (l, f) = filter f l
fun peek (l, f) = find f l
fun insert (l, x, op <=) =
let
fun loop (l, ac) =
case l of
[] => appendRev (ac, [x])
| x' :: l' =>
if x <= x'
then appendRev (ac, x :: l)
else loop (l', x' :: ac)
in loop (l, [])
end
end
structure Array =
struct
open Array
val new = array
fun modify (a, f) = Array.modify f a
fun foreachi (a, f) = appi f (a, 0, NONE)
fun indices (a: bool array): int vector =
let
val n = Array.length a
val numTrue =
let
fun loop (i, count) =
if i = n
then count
else loop (i + 1,
if Array.sub (a, i)
then count + 1
else count)
in loop (0, 0)
end
val next = ref 0
fun loop i =
if Array.sub (a, i)
then (next := i + 1; i)
else loop (i + 1)
in Vector.tabulate (numTrue, fn _ => loop (!next))
end
end
structure Char =
struct
open Char
val fromInt = chr
val toInt = ord
end
structure String =
struct
open String
type t = string
fun contains (s: t, c: char): bool =
Int.exists (0, size s, fn i => c = sub (s, i))
end
val numChars: int = 128
structure Regexp =
struct
datatype t =
AnchorStart
| CharSet of char -> bool
| Or of t list
| Seq of t list
| Star of t
end
structure Stack:
sig
type 'a t
val clear: 'a t -> unit
val exists: 'a t * ('a -> bool) -> bool
val foreach: 'a t * ('a -> unit) -> unit
val new: int * 'a -> 'a t
val push: 'a t * 'a -> unit
end =
struct
datatype 'a t = T of {elts: 'a array,
size: int ref}
fun new (size: int, dummy: 'a): 'a t =
T {elts = Array.new (size, dummy),
size = ref 0}
fun push (T {elts, size}, x) =
let
val n = !size
val _ = Array.update (elts, n, x)
val _ = size := n + 1
in ()
end
fun exists (T {elts, size, ...}, f) =
Int.exists (0, !size, fn i => f (Array.sub (elts, i)))
fun foreach (T {elts, size}, f) =
Int.for (0, !size, fn i => f (Array.sub (elts, i)))
fun clear (T {size, ...}) = size := 0
end
structure NFA:
sig
(* The states in an NFA are indexed from 0 to n-1, where n is the number
* of states.
*)
type state = int
(* State i is final iff Array.sub (final, i).
* The outgoing states from state i on input char c are given by
* Array2.sub (next, i, Char.ord c).
* anchorStarts is sorted in increasing order of state index.
*)
datatype t = T of {anchorStarts: state list,
final: bool array,
seen: bool array,
stack1: int Stack.t,
stack2: int Stack.t,
start: state,
next: state vector Array2.t}
val fromRegexp: Regexp.t -> t
val match: {nfa: t,
string: string,
startPos: int,
anchorStart: bool} -> int option
val numStates: t -> int
end =
struct
type state = int
datatype t = T of {anchorStarts: state list,
final: bool array,
seen: bool array,
stack1: int Stack.t,
stack2: int Stack.t,
start: state,
next: state vector Array2.t}
fun numStates (T {next, ...}) = Array2.nRows next
(* Simulating an NFA with two stacks and a bit vector, as in Algorithm 3.4
* (page 126) of Compilers: Principles, Techniques, and Tools by Aho,
* Sethi, and Ullman.
*)
fun match {anchorStart: bool,
nfa as T {anchorStarts, final, seen, stack1, stack2, start,
next},
startPos,
string = s}: int option =
let
val numStates = numStates nfa
val n = String.size s
val _ = Array.modify (seen, fn _ => false)
fun loop (current: state Stack.t,
nextStates: state Stack.t,
i: int,
last: int option): int option =
let
val last =
if Stack.exists (current, fn s => Array.sub (final, s))
then SOME i
else last
in
if numStates = 0 orelse i = n
then (Stack.clear stack1
; Stack.clear stack2
; last)
else
let
val _ = Array.modify (seen, fn _ => false)
val c = Char.toInt (String.sub (s, i))
val _ =
Stack.foreach (current, fn s =>
Vector.foreach
(Array2.sub (next, s, c),
fn s' =>
if Array.sub (seen, s')
then ()
else (Array.update (seen, s', true)
; Stack.push (nextStates, s'))))
val _ = Stack.clear current
in loop (nextStates, current, i + 1, last)
end
end
val _ = Stack.push (stack1, start)
val _ =
if anchorStart
then List.foreach (anchorStarts, fn s =>
Stack.push (stack1, s))
else ()
in
loop (stack1, stack2, startPos, NONE)
end
(* This conversion from a regular expression to an NFA is based on
* Section 3.9 (pages 134 -- 140) of Compilers: Principles, Techniques,
* and Tools by Aho, Sethi, and Ullman.
*
* It creates one NFA state for each CharSet (called a "position") that is
* in the regexp. There is also one extra state for the start state.
* It adds edges as in rules 1 and 2 (page 138) for the followpos function.
*)
fun fromRegexp (r: Regexp.t): t =
let
fun loop (r, ac) =
let open Regexp
in case r of
AnchorStart => ac + 1
| CharSet _ => ac + 1
| Or rs => List.fold (rs, ac, loop)
| Seq rs => List.fold (rs, ac, loop)
| Star r => loop (r, ac)
end
val numPos = loop (r, 0)
val numStates = numPos + 1
val start = numPos
val posCounter = ref ~1
val follow = Array2.new (numStates, numStates, false)
val posChars = Array2.tabulate (numPos, numChars, fn _ => false)
local
datatype t = T of bool vector
in
fun contains (T v, s) = Vector.sub (v, s)
val empty: t = T (Vector.new (numPos, false))
fun union (T v, T v'): t =
T (Vector.tabulate (numPos, fn i =>
Vector.sub (v, i)
orelse Vector.sub (v', i)))
fun singleton (i: int): t =
T (Vector.tabulate (numPos, fn j => i = j))
fun foreach (T v, f) =
Vector.foreachi (v, fn (i, b) => if b then f i else ())
end
val anchorStarts = ref []
fun loop r =
case r of
Regexp.AnchorStart =>
let
val i = ++ posCounter
val _ = List.push (anchorStarts, i)
val first = singleton i
in
{first = first,
last = first,
nullable = false}
end
| Regexp.CharSet f =>
let
val i = ++ posCounter
val _ =
Int.for (0, numChars, fn c =>
if f (Char.chr c)
then Array2.update (posChars, i, c, true)
else ())
val first = singleton i
in {first = first,
last = first,
nullable = false}
end
| Regexp.Or rs =>
List.fold
(rs, {first = empty,
last = empty,
nullable = false},
fn (r, {first = f, last = l, nullable = n}) =>
let
val {first = f', last = l', nullable = n'} =
loop r
in
{first = union (f, f'),
last = union (l, l'),
nullable = n orelse n'}
end)
| Regexp.Seq rs =>
List.fold
(rs, {first = empty,
last = empty,
nullable = true},
fn (r, {first = f, last = l, nullable = n}) =>
let
val {first = f', last = l', nullable = n'} =
loop r
val _ =
foreach
(l, fn s =>
foreach
(f', fn s' => Array2.update (follow, s, s', true)))
in
{first = if n then union (f, f') else f,
last = if n' then union (l, l') else l',
nullable = n andalso n'}
end)
| Regexp.Star r =>
let
val {first = f, last = l, nullable = n} = loop r
val _ =
foreach
(l, fn s =>
foreach
(f, fn s' => Array2.update (follow, s, s', true)))
in
{first = f, last = l, nullable = true}
end
val {first, last, nullable} = loop r
(* Any anchor starts in first should be anchor starts.
* This also reverses anchorStarts so they are in order.
*)
val anchorStarts =
List.fold (!anchorStarts, [], fn (s, ac) =>
if contains (first, s) then s :: ac else ac)
val _ = foreach (first, fn i =>
Array2.update (follow, start, i, true))
val final = Array.array (numStates, false)
val _ = foreach (last, fn i => Array.update (final, i, true))
val _ = if nullable then Array.update (final, start, true) else ()
val a = Array.new (numStates, false)
val next =
Array2.tabulate
(numStates, numChars, fn (i, c) =>
let
val _ =
Int.for (0, numStates, fn j => Array.update (a, j, false))
val _ =
Int.for
(0, numPos, fn j =>
if Array2.sub (follow, i, j)
andalso Array2.sub (posChars, j, c)
then Array.update (a, j, true)
else ())
in Array.indices a
end)
in
T {anchorStarts = anchorStarts,
final = final,
next = next,
seen = Array.new (numStates, false),
stack1 = Stack.new (numStates, ~1),
stack2 = Stack.new (numStates, ~1),
start = start}
end
end
structure DFA:
sig
type t
val fromNFA: NFA.t -> t
val match: {dfa: t,
string: string,
startPos: int,
anchorStart: bool} -> int option
val minimize: t -> t
end =
struct
(* The states in a DFA are indexed from 0 to n-1, where n is the number
* of states.
*)
type state = int
(* State i is final iff Array.sub (final, i).
* The outgoing state from state i on input char c is
* Array2.sub (next, i, Char.ord c).
*)
datatype t = T of {anchorStart: state,
dead: bool array,
final: bool array,
next: state Array2.t,
start: state}
fun numStates (T {next, ...}): int = Array2.nRows next
fun match {dfa as T {anchorStart = ancSt, dead, final, start, next},
string as s,
startPos: int,
anchorStart: bool}: int option =
let
val n = String.size s
fun loop (i: int, state: int, last: int option): int option =
let
val last =
if Array.sub (final, state)
then SOME i
else last
in
if Array.sub (dead, state) orelse i = n
then last
else loop (i + 1,
Array2.sub (next, state,
Char.toInt (String.sub (s, i))),
last)
end
in loop (startPos,
if anchorStart then ancSt else start,
NONE)
end
fun dead (numStates, final, next) =
Array.tabulate
(numStates, fn i =>
not (Array.sub (final, i))
andalso Int.forall (0, numChars, fn c =>
i = Array2.sub (next, i, c)))
(* This DFA minimization algorithm is based on algorithm 3.6 (page 142)
* of Compilers: Principles, Techniques, and Tools by Aho, Sethi, and
* Ullman.
*
* It maintains an array, r, that stores for each state s the
* representative of the class to which s belongs.
* It repeatedly refines an equivalence relation, represented by a list
* of classes, where each class is a list of states (i.e. ints).
*)
fun minimize (dfa as T {anchorStart, final, start, next, ...}): t =
let
val numStates = numStates dfa
type class = int list
type classes = class list
val repCounter = ref ~1
val change = ref false
fun newRep () = (change := true; ++ repCounter)
val finRep = newRep ()
val nonfinRep = newRep ()
val r = Array.tabulate (numStates, fn i =>
if Array.sub (final, i)
then finRep
else nonfinRep)
fun rep s = Array.sub (r, s)
fun trans (s, c) = rep (Array2.sub (next, s, c))
fun refine (class: class, ac: classes): classes =
let
val r =
List.fold
(class, [], fn (state, classes) =>
let
fun loop (classes, ac) =
case classes of
[] =>
(case ac of
[] => [{class = [state],
old = state}]
| _ =>
let
val s = newRep ()
val _ = Array.update (r, state, s)
in {class = [state],
old = state} :: ac
end)
| (z as {class, old}) :: classes =>
if Int.forall
(0, numChars, fn c =>
trans (old, c) = trans (state, c))
then
(Array.update (r, state, rep old)
; {class = state :: class,
old = old} :: (List.appendRev
(classes, ac)))
else loop (classes, z :: ac)
in loop (classes, [])
end)
in List.fold (r, ac, fn ({class, ...}, ac) =>
case class of
[_] => ac
| _ => class :: ac)
end
fun refineAll (classes: classes): unit =
case classes of
[] => ()
| _ =>
let
val _ = change := false
val classes =
List.fold (classes, [], fn (class, ac) =>
case class of
[_] => ac
| _ => refine (class, ac))
in if !change
then refineAll classes
else ()
end
val (fin, nonfin) =
Int.fold (0, numStates, ([], []), fn (i, (f, n)) =>
if Array.sub (final, i)
then (i :: f, n)
else (f, i :: n))
val _ = refineAll [fin, nonfin]
val numStates' = 1 + !repCounter
val reached = Array.new (numStates', false)
fun visit (s: int ): unit =
let
val s' = rep s
in
if Array.sub (reached, s')
then ()
else (Array.update (reached, s', true)
; Int.for (0, numChars, fn c =>
visit (Array2.sub (next, s, c))))
end
val _ = visit start
val _ = visit anchorStart
val c = ref ~1
val newR = Array.tabulate (numStates', fn s =>
if Array.sub (reached, s)
then ++ c
else ~1)
val numStates' = 1 + !c
val _ = Array.modify (r, fn s => Array.sub (newR, s))
val next' = Array2.new (numStates', numChars, ~1)
val _ =
Array2.foreachi
(next, fn (s, c, s') =>
Array2.update (next', rep s, c, rep s'))
val final' = Array.array (numStates', false)
val _ =
Array.foreachi
(final, fn (i, b) =>
if b then Array.update (final', rep i, true) else ())
in T {anchorStart = rep anchorStart,
dead = dead (numStates', final', next'),
final = final',
start = rep start,
next = next'}
end
(* This is the usual "subset construction", as in algorithm 3.2 (page 118)
* of Compilers: Principles, Techniques, and Tools by Aho, Sethi, and
* Ullman.
*
* It associates each (reachable) set of states in the nfa with a single
* state in the DFA.
*)
fun fromNFA (nfa as NFA.T {anchorStarts, final, start, next, ...}) =
let
type states = state vector
val counter = ref ~1
type work = {states: states,
state: int,
out: int vector option ref}
val cache: work list ref = ref []
val todo: work list ref = ref []
fun statesToState (ss: states): int =
case List.peek (!cache, fn {states, ...} => ss = states) of
NONE =>
let
val state = ++ counter
val work = {out = ref NONE,
state = state,
states = ss}
val _ = List.push (cache, work)
val _ = List.push (todo, work)
in
state
end
| SOME {state, ...} => state
local
val seen = Array.array (NFA.numStates nfa, false)
in
fun loop () =
case !todo of
[] => ()
| {states, out, ...} :: rest =>
(todo := rest
; out := (SOME
(Vector.tabulate
(numChars, fn c =>
let
val _ =
Array.modify (seen, fn _ => false)
val _ =
Vector.foreach
(states, fn s =>
Vector.foreach
(Array2.sub (next, s, c), fn s' =>
Array.update (seen, s', true)))
in statesToState (Array.indices seen)
end)))
; loop ())
end
val start' = statesToState (Vector.new1 start)
val anchorStart' =
statesToState
(Vector.fromList (List.insert (anchorStarts, start, op <=)))
val _ = loop ()
val numStates = 1 + !counter
val next' = Array2.new (numStates, numChars, ~1)
val final' = Array.new (numStates, false)
val _ =
List.foreach
(!cache, fn {states, state = i, out, ...}: work =>
let
val _ =
Vector.foreachi
(valOf (! out), fn (c, j) =>
Array2.update (next', i, c, j))
val _ =
if Vector.exists (states, fn s => Array.sub (final, s))
then Array.update (final', i, true)
else ()
in ()
end)
val dead' = dead (numStates, final', next')
in T {anchorStart = anchorStart',
dead = dead',
final = final',
start = start',
next = next'}
end
end
structure Regexp:
sig
structure Compiled:
sig
type t
val find: t * string -> {start: int, length: int} option
end
type t
val anchorStart: t
val any: t
val char: char -> t
val compileDFA: t -> Compiled.t
val compileNFA: t -> Compiled.t
val digit: t
val nonDigit: t
val notOneOf: string -> t
val oneOf: string -> t
val or: t list -> t
val seq: t list -> t
val star: t -> t
end =
struct
open Regexp
val anchorStart = AnchorStart
val isChar = CharSet
fun isNotChar f = isChar (not o f)
fun char c = isChar (fn c' => c = c')
val or = Or
val seq = Seq
val star = Star
val any = isChar (fn _ => true)
fun oneOf s = isChar (fn c => String.contains (s, c))
fun notOneOf s = isNotChar (fn c => String.contains (s, c))
val digs = "0123456789"
val digit = oneOf digs
val nonDigit = notOneOf digs
val empty = Or []
val emptyString = Seq []
structure Compiled =
struct
datatype t =
DFA of DFA.t
| NFA of NFA.t
fun find (c: t, s: string) =
let
val n = String.size s
fun loop (i: int, anchorStart: bool) =
if i = n
then NONE
else
let
val res =
case c of
DFA dfa =>
DFA.match {dfa = dfa,
string = s,
startPos = i,
anchorStart = anchorStart}
| NFA nfa =>
NFA.match {nfa = nfa,
string = s,
startPos = i,
anchorStart = anchorStart}
in
case res of
NONE => loop (i + 1, false)
| SOME finish => SOME {start = i,
length = finish - i}
end
in loop (0, true)
end
end
fun compileDFA r =
Compiled.DFA (DFA.minimize (DFA.fromNFA (NFA.fromRegexp r)))
fun compileNFA r =
Compiled.NFA (NFA.fromRegexp r)
end
local
open Regexp
in
val d = digit
val eol = char #"#"
val space = oneOf " \t"
val r =
seq [or [anchorStart, notOneOf "0123456789("],
or [seq [char #"(", d, d, d, char #")"],
seq [d, d, d]],
char #" ",
d, d, d,
oneOf " -",
d, d, d, d,
or [eol, nonDigit]]
val comp = Regexp.compileDFA r
end
fun incr (r: int ref) = r := !r + 1
val ins = TextIO.stdIn
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t )
local
val form = "(...) ...-...."
val a = CharArray.tabulate (String.size form, fn i =>
String.sub (form, i))
in
fun checkPhone (mustPrint: bool, cnt: int ref, line: string) =
case Regexp.Compiled.find (comp, line) of
NONE => ()
| SOME {start = pos, ...} =>
let
fun blit (src, dst, length) =
let
val stop = src + length
fun loop (src, dst) =
if src = stop
then ()
else (CharArray.update (a, dst,
String.sub (line, src))
; loop (src + 1, dst + 1))
in
loop (src, dst)
end
val (o1, o2, o3) =
if #"(" = String.sub (line, pos)
then (1, 6, 10)
else if #"(" = String.sub (line, pos + 1)
then (2, 7, 11)
else if Char.isDigit (String.sub (line, pos))
then (0, 4, 8)
else (1, 5, 9)
val _ = blit (pos + o1, 1, 3)
val _ = blit (pos + o2, 6, 3)
val _ = blit (pos + o3, 10, 4)
val _ =
if mustPrint
then printl [Int.toString (!cnt), ": ",
CharArray.extract (a, 0, NONE)]
else ()
val _ = incr cnt
in
()
end
end
fun doit (phones,mustPrint: bool): unit =
let val cnt = ref 1
in List.foreach (phones, fn line => checkPhone (mustPrint, cnt, line))
end
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0
fun main (name, args) =
let
val n = atoi (hd (args @ ["1"]))
val phones =
let
fun loop lines =
case TextIO.inputLine ins of
"" => rev lines
| line => loop (line :: lines)
in loop []
end
val _ = Int.for (1, n, fn _ => doit (phones,false))
val _ = doit (phones,true)
in OS.Process.success
end
end
val _ = SMLofNJ.exportFn("regexmatch", Test.main);
|
| Reverse a File |
(* -*- mode: sml -*-
* $Id: reversefile.smlnj,v 1.4 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Tom 7
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
val bufsize = 4096
val rdbufsize = 4096
val stdout = Posix.FileSys.wordToFD 0w1
val stdin = Posix.FileSys.wordToFD 0w0
datatype block = END
| MORE of int ref * Word8Array.array * block
val buff = Unsafe.Word8Array.create rdbufsize
fun out END = ()
| out (MORE (ir as ref i, a, next)) =
let in
Posix.IO.writeArr (stdout, {buf=a, i=i, sz=NONE});
out next
end
fun rd (start, len, count, b) =
if (start + len) >= count then
(* done with this block.
Copy from start to the end of the array into
buff, then return the starting index into buff. *)
let in
Word8Array.copy {di=0,
dst=buff,
src=buff,
len=SOME len,
si=start};
(b, len)
end
else
if Unsafe.Word8Array.sub(buff, start + len) = 0w10 then
case b of MORE(ir as ref i, a, _) =>
if i > len then
let in
Word8Array.copy {di=i-len - 1,
dst=a,
len=SOME(len + 1),
si=start,
src=buff};
ir := i - (len + 1);
rd(start + len + 1, 0, count, b)
end
else
let
val na = Unsafe.Word8Array.create bufsize
val l = (len + 1) - i
in
Word8Array.copy {di=0,
dst=a,
len=SOME i,
si=(start + len + 1) - i,
src=buff};
Word8Array.copy {di=bufsize - l,
dst=na,
len=SOME l,
si=start,
src=buff};
ir := 0;
rd(start + len + 1, 0, count, MORE(ref (bufsize - l), na, b))
end
else rd (start, len + 1, count, b)
fun loop (b, s) =
let
val count = Posix.IO.readArr (stdin,
{buf=buff, i=s, sz=SOME (rdbufsize-s)})
val (bb, bs) = rd (0, s, count + s, b)
in
case count of
0 => out bb
| _ => loop (bb, bs)
end
fun main(name, args) =
( loop (MORE(ref bufsize, Unsafe.Word8Array.create bufsize, END), 0);
OS.Process.success);
end
val _ = SMLofNJ.exportFn("reversefile", Test.main);
|
| Sieve of Erathostenes |
(* -*- mode: sml -*-
* $Id: sieve.smlnj,v 1.8 2001/08/20 01:11:11 doug Exp $
* http://www.bagley.org/~doug/shootout/
* with help from Dan Wang
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
structure WA = Word8Array
val flags = WA.array (8193, 0w0)
fun init() = let
fun loop i =
if i < 8193 then (WA.update(flags,i,0w1);loop(i+1))
else ()
in loop 2
end
fun do_elts(i,count) =
if i < 8193 then
if WA.sub(flags,i) = 0w1 then let
fun loop k =
if k < 8193 then (WA.update(flags,k,0w0);loop(k+i))
else ()
in loop (i + i) ; do_elts(i + 1,count + 1)
end
else do_elts(i + 1, count)
else count
fun repeat 0 = (init (); do_elts(2,0))
| repeat n = (init (); do_elts(2,0);repeat(n-1))
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t )
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0
fun main(name, param_list) = let
val arg = hd(param_list @ ["1"]);
val num = atoi arg
val count = repeat num
in printl ["Count: ", Int.toString count];
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("sieve", Test.main);
|
| Spell Checker |
(* -*- mode: sml -*-
* $Id: spellcheck.smlnj,v 1.2 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
open TextIO;
fun chop str = String.substring (str, 0, (String.size str) - 1);
fun spellcheck () =
let
val dict = HashTable.mkTable (HashString.hashString, op =) (40000, (Fail "not found"))
in
let val din = openIn "Usr.Dict.Words"
fun init_dict din dict =
( HashTable.insert dict (chop (inputLine din), 1);
init_dict din dict )
in
init_dict din dict
end handle EndOfFile => ();
let fun check_words dict =
let val word = chop (inputLine stdIn) in
case HashTable.find dict word of
SOME _ => ()
| NONE => (print word ; print "\n");
check_words dict
end
in
check_words dict
end handle EndOfFile => ()
end
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0;
fun main(name, args) =
let
val arg = hd(args @ ["1"])
val num = atoi arg
in
spellcheck ();
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("spellcheck", Test.main);
|
| Statistical Moments |
(* -*- mode: sml -*-
* $Id: moments.smlnj,v 1.3 2001/07/10 12:55:23 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
* with help from Daniel Wang:
* Modified to be more functional and use SML/NJ library sorting function
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
val ins = TextIO.stdIn
fun loop (nums,sum) =
case TextIO.inputLine ins of
"" => (nums,sum)
| l => (case Real.fromString l of
NONE => raise Fail "invalid input"
| SOME num => loop(num::nums,sum+num))
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t )
fun r2s (x: real): string =
if Real.== (x, 0.0) then "0.000000"
else String.translate
(fn #"~" => "-" | c => str c)
(Real.fmt (StringCvt.FIX (SOME 6)) x)
fun main(name, args) = let
val (nums,sum) = loop ([],0.0)
val nums = Array.fromList nums
val n = Array.length nums
val n_float = real n
val mean = sum / n_float
fun moments (x,{average_deviation,variance,skew,kurtosis}) = let
val deviation = x - mean
val average_deviation =
average_deviation + abs(deviation)
val dev2 = deviation * deviation
val variance = variance + dev2
val dev3 = dev2 * deviation
val skew = skew + dev3
val dev4 = dev3 * deviation
val kurtosis = kurtosis + dev4
in {average_deviation=average_deviation,
variance=variance,
skew=skew,
kurtosis=kurtosis}
end
val init = {average_deviation=0.0,
variance=0.0,
skew=0.0,
kurtosis=0.0}
val {average_deviation,variance,skew,kurtosis} =
Array.foldl moments init nums
val average_deviation = average_deviation / n_float
val variance = variance / real (n - 1);
val standard_deviation = Real.Math.sqrt (variance)
val {skew,kurtosis} =
if variance > 0.0
then {skew=skew / n_float / variance / standard_deviation,
kurtosis=kurtosis / n_float / variance / variance - 3.0}
else {skew=skew,kurtosis=kurtosis}
val _ = ArrayQSort.sort Real.compare nums
val mid = Int.quot (n, 2)
val median =
if Int.rem (n, 2) = 1
then Array.sub (nums, mid)
else (Array.sub (nums, mid) +
Array.sub (nums, mid - 1)) / 2.0
in
printl ["n: ", Int.toString n, "\n",
"median: ", r2s median, "\n",
"mean: ", r2s mean, "\n",
"average_deviation: ", r2s average_deviation, "\n",
"standard_deviation: ", r2s standard_deviation, "\n",
"variance: ", r2s variance, "\n",
"skew: ", r2s skew, "\n",
"kurtosis: ", r2s kurtosis];
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("moments", Test.main);
|
| String Concatenation |
(* -*- mode: sml -*-
* $Id: strcat.smlnj,v 1.6 2001/07/10 04:01:48 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
* Modified by Daniel Wang
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun for (start, stop, f) =
let
fun loop i =
if i > stop
then ()
else (f i; loop (i + 1))
in
loop start
end
fun atoi s = case Int.fromString s of SOME num => num | NONE => 0
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t )
val stuff = "hello\n"
structure Buffer:
sig
type 'a t
val add: 'a t * 'a array -> unit
val length: 'a t -> int
val new: 'a -> 'a t
end =
struct
datatype 'a t = T of {dummy: 'a,
length: int ref,
elts: 'a array ref}
fun add (T {dummy, elts, length}, a) =
let
val l = !length
val e = !elts
val en = Array.length e
val an = Array.length a
val e =
if l + an >= en then
let val e' = Array.array(2 * en,dummy)
val _ = Array.copy {src = e, si = 0,len = SOME en,
dst = e',di = 0}
val _ = elts := e'
in e'
end
else e
val _ =
Array.copy {src = a, si = 0, len = NONE,
dst = e, di = l}
val _ = length := l + an
in ()
end
fun new (dummy: 'a) = T {dummy = dummy,
length = ref 0,
elts = ref (Array.array (32, dummy))}
fun length (T {length, ...}) = !length
end
fun main (name, args) =
let
val stuff =
Array.tabulate (String.size stuff, fn i => String.sub (stuff, i))
val n = atoi (hd (args @ ["1"]))
val b = Buffer.new #"\000"
val _ = for (1, n, fn _ => Buffer.add (b, stuff))
val _ = printl [Int.toString (Buffer.length b)]
in
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("strcat", Test.main);
|
| Sum a Column of Integers |
(* -*- mode: sml -*-
* $Id: sumcol.smlnj,v 1.3 2001/07/09 00:25:28 doug Exp $
* http://www.bagley.org/~doug/shootout/
*)
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
fun sumlines sum =
if TextIO.endOfStream TextIO.stdIn
then (print (Int.toString sum); print "\n")
else case (Int.fromString (TextIO.inputLine TextIO.stdIn)) of
NONE => sumlines sum
| SOME i => sumlines (sum + i);
fun main(name, args) = (sumlines 0; OS.Process.success);
end
val _ = SMLofNJ.exportFn("sumcol", Test.main);
|
| Word Frequency Count |
(* -*- mode: sml -*-
* $Id: wordfreq.smlnj,v 1.3 2001/07/09 00:25:29 doug Exp $
* http://www.bagley.org/~doug/shootout/
* from Stephen Weeks
*)
fun for (start, stop, f) =
let
fun loop i =
if i > stop
then ()
else (f i; loop (i + 1))
in
loop start
end
fun incr r = r := 1 + !r
val sub = Array.sub
val update = Array.update
signature HASH_SET =
sig
type 'a t
val foreach: 'a t * ('a -> unit) -> unit
(* lookupOrInsert (s, h, p, f) looks in the set s for an entry with hash h
* satisfying predicate p. If the entry is there, it is returned.
* Otherwise, the function f is called to create a new entry, which is
* inserted and returned.
*)
val lookupOrInsert: 'a t * word * ('a -> bool) * (unit -> 'a) -> 'a
val new: {hash: 'a -> word} -> 'a t
val size: 'a t -> int
end
structure HashSet: HASH_SET =
struct
datatype 'a t =
T of {buckets: 'a list array ref,
hash: 'a -> word,
mask: word ref,
numItems: int ref}
val initialSize: int = 65536
val initialMask: word = Word.fromInt initialSize - 0w1
fun 'a new {hash}: 'a t =
T {buckets = ref (Array.array (initialSize, [])),
hash = hash,
numItems = ref 0,
mask = ref initialMask}
fun size (T {numItems, ...}) = !numItems
fun numBuckets (T {buckets, ...}) = Array.length (!buckets)
fun index (w: word, mask: word): int =
Word.toInt (Word.andb (w, mask))
fun resize (T {buckets, hash, mask, ...}, size: int, newMask: word): unit =
let
val newBuckets = Array.array (size, [])
in Array.app (fn r =>
List.app (fn a =>
let val j = index (hash a, newMask)
in Array.update
(newBuckets, j,
a :: Array.sub (newBuckets, j))
end) r) (!buckets)
; buckets := newBuckets
; mask := newMask
end
fun maybeGrow (s as T {buckets, mask, numItems, ...}): unit =
let
val n = Array.length (!buckets)
in if !numItems * 4 > n
then resize (s,
n * 2,
Word.orb (0w1, Word.<< (!mask, 0w1)))
else ()
end
fun peekGen (T {buckets = ref buckets, mask, ...}, w, p, no, yes) =
let
val j = index (w, !mask)
val b = Array.sub (buckets, j)
in case List.find p b of
NONE => no (j, b)
| SOME a => yes a
end
fun lookupOrInsert (table as T {buckets, numItems, ...}, w, p, f) =
let
fun no (j, b) =
let val a = f ()
val _ = incr numItems
val _ = Array.update (!buckets, j, a :: b)
val _ = maybeGrow table
in a
end
in peekGen (table, w, p, no, fn x => x)
end
fun foreach (T {buckets, ...}, f) =
Array.app (fn r => List.app f r) (!buckets)
end
structure Buffer:
sig
type t
val add: t * Word8.word -> unit
val clear: t -> unit
val contents: t -> string
val new: int -> t
end =
struct
datatype t = T of {elts: Word8Array.array ref,
size: int ref}
fun contents (T {elts, size, ...}) =
Byte.bytesToString (Word8Array.extract (!elts, 0, SOME (!size)))
fun clear (T {size, ...}) = size := 0
fun new (bufSize) =
T {elts = ref (Word8Array.array (bufSize, 0w0)),
size = ref 0}
fun add (T {elts, size}, x) =
let
val s = !size
val _ = size := s + 1
val a = !elts
val n = Word8Array.length a
in
if s = n
then
let
val a' =
Word8Array.tabulate
(2 * n, fn i =>
if i < n then Word8Array.sub (a, i) else 0w0)
val _ = elts := a'
val _ = Word8Array.update (a', s, x)
in ()
end
else Word8Array.update (a, s, x)
end
end
structure Quicksort:
sig
val quicksort: 'a array * ('a * 'a -> bool) -> unit
end =
struct
fun assert (s, f: unit -> bool) =
if true orelse f ()
then ()
else raise Fail (concat ["assert: ", s])
fun forall (low, high, f) =
let
fun loop i = i > high orelse (f i andalso loop (i + 1))
in
loop low
end
fun fold (l, u, state, f) =
let
fun loop (i, state) =
if i > u
then state
else loop (i + 1, f (i, state))
in
loop (l, state)
end
fun 'a isSorted (a: 'a array,
lo: int,
hi: int,
op <= : 'a * 'a -> bool) =
let
fun loop (i, x) =
i > hi
orelse let
val y = sub (a, i)
in
x <= y andalso loop (i + 1, y)
end
in
lo >= hi orelse loop (lo + 1, sub (a, lo))
end
local
open Word
val seed = ref 0w13
in
fun rand () =
let
val res = 0w1664525 * !seed + 0w1013904223
val _ = seed := res
in
toIntX res
end
end
fun randInt (lo, hi) = lo + Int.mod (rand(), hi - lo + 1)
fun insertionSort (a: 'a array, op <= : 'a * 'a -> bool): unit =
let
fun x i = sub (a, i)
in
for (1, Array.length a - 1, fn i =>
let
val _ =
assert ("insertionSort1", fn () =>
isSorted (a, 0, i - 1, op <=))
val t = x i
fun sift (j: int) =
(assert ("insertionSort2", fn () =>
isSorted (a, 0, j - 1, op <=)
andalso isSorted (a, j + 1, i, op <=)
andalso forall (j + 1, i, fn k => t <= x k))
; if j > 0
then
let
val j' = j - 1
val z = x j'
in if t <= z
then (update (a, j, z);
sift j')
else j
end
else j)
val _ = update (a, sift i, t)
in ()
end)
end
fun 'a quicksort (a: 'a array, op <= : 'a * 'a -> bool): unit =
let
fun x i = Array.sub (a, i)
fun swap (i, j) =
let
val t = x i
val _ = update (a, i, x j)
val _ = update (a, j, t)
in ()
end
val cutoff = 20
fun qsort (l: int, u: int): unit =
if u - l > cutoff
then
let
val _ = swap (l, randInt (l, u))
val t = x l
val m =
fold
(l + 1, u, l, fn (i, m) =>
(assert
("qsort", fn () =>
forall (l + 1, m, fn k => x k <= t)
andalso forall (m + 1, i - 1, fn k => not (x k <= t)))
; if x i <= t
then (swap (m + 1, i)
; m + 1)
else m))
val _ = swap (l, m)
val _ = qsort (l, m - 1)
val _ = qsort (m + 1, u)
in ()
end
else ()
val max = Array.length a - 1
val _ = qsort (0, max)
val _ = insertionSort (a, op <=)
in
()
end
end
structure Test : sig
val main : (string * string list) -> OS.Process.status
end = struct
(* This hash function is taken from pages 56-57 of
* The Practice of Programming by Kernighan and Pike.
*)
fun hash (s: string): word =
let
val n = String.size s
fun loop (i, w) =
if i = n
then w
else Word.fromInt (Char.ord (String.sub (s, i))) + Word.* (w, 0w31)
in
loop (0, 0w0)
end
fun hash (s: string): word =
let
val n = String.size s
fun loop (i, w) =
if i = n
then w
else loop (i + 1,
Word.fromInt (Char.ord (String.sub (s, i)))
+ Word.* (w, 0w31))
in
loop (0, 0w0)
end
val max = 4096
val buf = Word8Array.array (max, 0w0)
val count: {hash: word,
word: string,
count: int ref} HashSet.t = HashSet.new {hash = #hash}
val wbuf = Buffer.new 64
val c2b = Byte.charToByte
fun scan_words (i, n, inword) =
if i < n
then
let
val c = Word8Array.sub (buf, i)
in
if c2b #"a" <= c andalso c <= c2b #"z"
then (Buffer.add (wbuf, c);
scan_words (i + 1, n, true))
else
if c2b #"A" <= c andalso c <= c2b #"Z"
then
(Buffer.add (wbuf, c + 0w32);
scan_words (i + 1, n, true))
else
if inword
then
let
val w = Buffer.contents wbuf
val h = hash w
in
incr (#count
(HashSet.lookupOrInsert
(count, h,
fn {hash, word, ...} =>
hash = h andalso word = w,
fn () => {hash = h, word = w, count = ref 0})));
Buffer.clear wbuf;
scan_words (i + 1, n, false)
end
else scan_words (i + 1, n, false)
end
else
let
val nread =
Posix.IO.readArr (Posix.FileSys.stdin,
{buf = buf, i = 0, sz = NONE})
in
if nread = 0
then ()
else scan_words (0, nread, inword)
end
fun printl [] = print "\n" | printl(h::t) = ( print h ; printl t )
fun rightJustify (s: string, width: int) =
let
val n = String.size s
in concat [CharVector.tabulate (width - n, fn _ => #" "), s]
end
fun main (name, args) =
let
val _ = scan_words (0, 0, false)
val a = Array.array (HashSet.size count, (0, ""))
val i = ref 0
val _ = HashSet.foreach (count, fn {word, count, ...} =>
(Array.update (a, !i, (!count, word)); incr i))
val _ = Quicksort.quicksort (a, fn ((c, w), (c', w')) =>
c > c' orelse c = c' andalso w >= w')
val _ = Array.app (fn (c, w) =>
printl [rightJustify (Int.toString c, 7), "\t", w]) a
in
OS.Process.success
end
end
val _ = SMLofNJ.exportFn("wordfreq", Test.main);
|