Posted: Sat Nov 05, 2005 2:10 am Post subject: (No subject)
Pattern matching and "when"
Last post had this example:
code:
# let rec find value lst index =
match lst with
[] -> None
| x::xs ->
if x = value then Some index
else find value xs (index + 1);;
val find : 'a -> 'a list -> int -> int option = <fun>
That's a mess. Let's clean it up by adding some conditions to our pattern match.
code:
# let rec find value lst index =
match lst with
[] -> None
| x::_ when x = value -> Some index
| _::xs -> find value xs (index + 1);;
val find : 'a -> 'a list -> int -> int option = <fun>
Much better.
code:
x::_ when x = value
We know that:
code:
x::_
Will match a list where x is the first element, and where we don't care what the rest of the list is. And because of the "when" part of this, this pattern will only match if "x" is the value we're looking for.
Since we now have the value we're looking for, we don't need to look at the rest of the list, so we can use the underscore.
code:
_::xs
If we get past that pattern, then we know that the first element isn't what we're looking for, so we don't have to give a darn about it.
Sponsor Sponsor
wtd
Posted: Sun Nov 06, 2005 3:35 pm Post subject: (No subject)
Hashtables
Someone coming from Ruby, Python or Perl has seen hashes (or "dictionaries" in Pythonspeak), and should be well acquainted with their power.
You'll be happy to know that O'Caml has hashtables as well, as defined by the Hashtbl module.
Unfortunately, there isn't a literal syntactic form for a hash, but they're easy enough to manipulate anyway.
First off, though, let's create a hashtable.
code:
# open Hashtbl;;
# let h = Hashtbl.create 10;;
val h : ('_a, '_b) Hashtbl.t = <abstr>
The 10 simply indicates that we're initially reserving space for ten entries.
Now, let's add an entry.
code:
# Hashtbl.add h "hello" "world";;
- : unit = ()
And then we'll look that up again.
code:
# Hashtbl.find h "hello";;
- : string = "world"
What if I try to look up something that's not in the hash?
code:
# Hashtbl.find h "foo";;
Exception: Not_found.
So I might perhaps handle this like so:
code:
# try Hashtbl.find h "foo" with Not_found -> "";;
- : string = ""
Now here's something fun. Add "Hello" => "world" to the hashtable. Now add "hello" => "ninja" to the table.
The "ninja" entry should overwrite "world", correct?
Well, not quite. The table holds both. If you do a find for "hello" you'll get "ninja", but "world" is still in there, and using the find_all function instead of find will make that perfectly clear.
If we want to achieve the overwriting effect, we can use the replace function.
To iterate over a hashtable, for the purpose of printing it out, for instance, we can use the iter function.
code:
# let h = Hashtbl.create 10;;
val h : ('_a, '_b) Hashtbl.t = <abstr>
# Hashtbl.add h "hello" "world";;
- : unit = ()
# Hashtbl.add h "foo" "bar";;
- : unit = ()
# Hashtbl.add h "wooble" "ninja";;
- : unit = ()
# Hashtbl.replace h "hello" "WORLD";;
- : unit = ()
# Hashtbl.iter (Printf.printf "%s: %s\n") h;;
hello: WORLD
wooble: ninja
foo: bar
- : unit = ()
One thing notably missing from the Hashtbl module is a function which retrieves all of the values or keys from a hashtable. Seems an odd omission, doesn't it?
Yet, the "fold" function makes all of this trivially possible.
code:
# let keys = Hashtbl.fold (fun k _ b -> k :: b) h [];;
val keys : string list = ["hello"; "foo"; "wooble"]
# let values = Hashtbl.fold (fun _ v b -> v :: b) h [];;
val values : string list = ["World"; "bar"; "ninja"]
Additional functions include:
mem - tests for the presence of a key in a hash
clear - empties the hashtable
remove - remove a key
length - returns the length of the hashtable
copy - creates a copy of the hashtable