Eras H
Eras H

Reputation: 19

Add elements to list in a loop in OCAML

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

Answers (2)

H. Rittich
H. Rittich

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

octachron
octachron

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

Related Questions