let rec add_lists (xs : float list) (ys : float list): float list option =
match xs, ys with
| [], [] -> None
| x :: xs, [] -> None
| [], y :: ys -> None
| x :: xs, y :: ys -> (x +. y) :: add_lists xs ys
The question asks us to add the elements of two lists together, if they are different lengths return None. I tried this code, but I get an error message says "This variant expression is expected to have type float list option. There is no constructor :: within type option" Is there any ways to fix it? Thx
Well, if both are empty lists, are they really of different lengths? No. So you should be returning Some []
.
let rec add_lists (xs : float list) (ys : float list): float list option =
match xs, ys with
| [], [] -> Some []
Your next two patterns make sense, but as you don't use x
or xs
, they can be simplified.
let rec add_lists (xs : float list) (ys : float list): float list option =
match xs, ys with
| [], [] -> Some []
| _, [] | [], _ -> None
Your final case needs to return an option type. The fact that your attempt does not do this is the source of your error.
The constructor used (None
or Some
) is going to be dependent on the outcome of the recursion. Pattern-matching can used to determine if that generates a None
or Some lst
. In the former case, we know we want to return None
, but in the latter, we can tack the addition result onto the front of the recursive results.
let rec add_lists (xs : float list) (ys : float list): float list option =
match xs, ys with
| [], [] -> Some []
| _, [] | [], _ -> None
| x::xs, y::ys ->
match add_lists xs ys with
| None -> None
| Some lst -> Some (x +. y :: lst)
If we wish to be clever, we can realize this behavior with option types can be generalized, and we might even give it an operator name.
let (>>>) opt f =
match opt with
| None -> None
| Some x -> Some (f x)
Or utilizing the Option
module:
let (>>>) o f =
Option.(
bind o @@ fun x -> some @@ f x
)
We can also realize that we directly return None
when one but not both lists are empty. If we match both being empty, and both being not empty, the default option is for only one being empty. So if we re-order the patterns, the code is further simplified.
Now we can write:
let rec add_lists (xs : float list) (ys : float list): float list option =
match xs, ys with
| [], [] -> Some []
| x::xs, y::ys -> add_lists xs ys >>> List.cons (x +. y)
| _ -> None
Or you might just take advantage of List.map2
which raises Invalid_argument
on lists of unequal lengths.
let add_lists xs ys =
try Some (List.map2 (+.) xs ys)
with Invalid_argument _ -> None