Discussion:
Help with list functions
(trop ancien pour répondre)
Joel
2009-05-13 17:14:46 UTC
Permalink
Hello,

I'm having some difficulty with list functions and was hoping you might
be able to help. Sorry for the newbie question...

What I'm wanting to do is create a ref list that contains a series of
strings, i.e.
# let varlist = ref [];;
# let a = "a";;
# let b = "b";;
# let c = "c";;

Then throughout the course of program operation, I will be adding data
into this list, i.e.:
# varlist.contents <- a::varlist.contents;;
# varlist.contents <- b::varlist.contents;;
# varlist.contents <- c::varlist.contents;;

Which is working fine, i.e.
# varlist.contents;;
- : string list = ["c"; "b"; "a"]

But what I'm trying to do next I can't figure out how to get working.
What I'd like to do is have a function that first checks to see if the
item is already in the ref list. If it is, do nothing. If it isn't,
add it. Here's what I tried, which is of course failing. If anyone has
a suggestion on how to make it work would you please help me out?

# let rec addvariable stringdata listname =
match listname.contents with
| [] -> (listname.contents <- stringdata::listname.contents); ()
| [a] -> if a.contents = stringdata then () else (listname.contents <-
stringdata::listname.contents); ()
| h::t -> if h.contents = stringdata then () else addvariable
stringdata t.contents
;;

Which returns...
This expression has type 'a ref but is here used with type 'a
#

Does anyone have any suggestions on how to change the above to make it
work? Basically I want the function to either 1) add the contents to
the list if no duplicates exist and return unit or 2) return unit if it
determines that the string is already there.

Thanks for any and all help
Joel
bluestorm
2009-06-03 08:19:01 UTC
Permalink
First a few remarks on the programming style :
- you can write "a := b" instead of "a.contents <- b" (write-access)
- you can write "!a" instead of "a.contents" (read-access)
- "a := b" returns an unit, no need for "(a := b); ()"
- your second matching case is useless, as it is a specialization of
the more generic h::t case (where t = []).
- (if foo then bar) is equivalent to (if foo then bar then ()). Hence
you can write :
if a.contents <> stringdata
then listname := stringdata :: !listname;
- beware that (if a then b else c; d) is ((if a then b else c); d) and
NOT (if a then b else (c; d)), wich is probably not what you mean in
the [a] case, though it makes no difference here (it can be a subtle
bug in other settings)
(Have you been using revised syntax ?)

The problem is that you manipulate an ('a list ref) as if every part
of it was mutable : you recurse through the list and potentially
mutate any tail of it, wich is not correct : you have a reference on
the whole liste, but once you called varlist.contents you get an 'a
list wich is not mutable (and pattern-matching on it will produce
other 'a list, no references).

You have to do differently : as only the whole list is mutable, you
have to add your element at the front of the list, and not "inside"
the list as you're trying to do. Inside your "looking-up for
stringdata" routine, wich works on "pure lists" (no references), you
have to keep a reference to the "global" mutable varlist.
let addvariable str varlist =
let rec look = function
| [] -> varlist := str :: !varlist
| ... in
look !varlist
Incidently, lookup is actually a function testing for list membership;
you could juste write (if not (List.mem str !varlist) then
varlist := ...)

If you want to retain the possibility to mutate every tail of the
type 'a mutlist = 'a cell ref
and 'a cell = Empty | Cell of 'a * 'a mutlist
I suppose, however, that what you rather want to keep the standard
list type.

Loading...