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); |