Reputation: 19
Hi i'm a beginner in OCAML and i would like to create a function to add elements a the end of a list and return this list. Here is my code :
let test () : int list =
let mylist = [] in
for i = 1 to 3 do
mylist@[i];
done;
mylist;;
It says that mylist@[i] should have type unit. When I call this function it returns an empty list. Can anyone help me ? Thanks
Upvotes: 1
Views: 4013
Reputation: 845
Ocaml lists are immutable, i.e., they cannot be changed. The expression
mylist@[i]
creates a new list. However, since you do nothing with the result, it is just thrown away. If you want to build a list like that, you need to store it in a reference.
let l = ref [] in
for i = 0 to 3 do
l := !l @ [i]
done;
List.iter (fun item -> print_int item; print_newline ()) !l
I would, however, recommend to do this differently. Appending two lists is a rather expensive operation, because a new list is created and all elements are copied every time. A much more efficient way to do what you want is to create the list in reverse order and use List.cons
(the :: operator), which adds new elements to the beginning of the list.
let l = ref [] in
for i = 3 downto 0 do
l := i :: !l
done;
List.iter (fun item -> print_int item; print_newline ()) !l
The cons
operation runs in constant time, because it can reuse the already existing list.
Alternatively, you can also create the list using recursion.
let rec l i =
if i <= 3 then i :: l (i+1) else [] in
List.iter (fun item -> print_int item; print_newline ()) (l 0)
This variant also does not need to copy the list, but it is not tail-recursive, i.e., it uses as much stack space as elements in the list.
let rec l acc i =
if i >= 0 then l (i :: acc) (i-1) else acc in
List.iter (fun item -> print_int item; print_newline ()) (l [] 3)
This variant is efficient, tail-recursive, but harder to read (IMHO).
As a final remark, you might want to check out the Queue module or the DynArray module in ExtLib or in Batteries.
Upvotes: 6
Reputation: 18892
Lists are immutable in OCaml. In particular, the line
mylist@[i];
is equivalent to
mylist@[i]; ()
Or in other words, first append the list [i]
at the end of mylist then discard the result of this computation. This is why the compiler is warning you that mylist @ [i]
should have type unit: unit is the result type used for functions that only perform side-effects and do not return a value.
If you want to create a range
function, the idiomatic way is to define a recursive function
let rec range start stop =
if start > stop then
...
else
... range (start+1) stop ...
Upvotes: 0