I want to implement the List.assoc function using List.find, this is what I have tried:
let rec assoc lista x = match lista with
| [] -> raise Not_found
| (a,b)::l -> try (List.find (fun x -> a = x) lista)
b
with Not_found -> assoc l x;;
but it gives me this error:
This expression has type ('a * 'b) list but an expression was expected of type 'a list
The type variable 'a occurs inside 'a * 'b
I don't know if this is something expected to happen or if I'm doing something wrong. I also tried this as an alternative:
let assoc lista x = match lista with
| [] -> raise Not_found
| (a,b)::l -> match List.split lista with
| (l1,l2) -> let ind = find l1 (List.find (fun s -> compare a x = 0))
in List.nth l2 ind;;
where find is a function that returns the index of the element requested:
let rec find lst x =
match lst with
| [] -> raise Not_found
| h :: t -> if x = h then 0 else 1 + find t x;;
with this code the problem is that the function should have type ('a * 'b) list -> 'a -> 'b, but instead it's (('a list -> 'a) * 'b) list -> ('a list -> 'a) -> 'b, so when I try
assoc [(1,a);(2,b);(3,c)] 2;;
I get:
This expression has type int but an expression was expected of type
'a list -> 'a (refering to the first element of the pair inside the list)
I don't understand why I don't get the expected function type.
First off, a quick suggestion on making your assoc function more idiomatic OCaml: have it take the list as the last argument.
Secondly, why are you attempting to implement this in terms of find? It's much easier without.
let rec assoc x lista =
match lista with
| [] -> raise Not_found
| (a, b) :: xs -> if a = x then b else assoc x xs
Something like this is simpler and substantially more efficient with the way lists work in OCaml.
Having the list as the last argument, even means we can write this more tersely.
let rec assoc x =
function
| [] -> raise Not_found
| (a, b) :: xs -> if a = x then b else assoc x xs
As to your question, OCaml infers the types of functions from how they're used.
find l1 (List.find (fun s -> compare a x = 0))
We know l1 is an int list. So we must be trying to find it in an int list list. So:
List.find (fun s -> compare a x = 0)
Must return an int list list. It's a mess. Try rethinking your function and you'll end up with something much easier to reason about.
Related
I'm required to output a pair of lists and I'm not understanding why the pair I'm returning is not of the correct type.
let rec split l = match l with
| [] -> []
| [y] -> [y]
| x :: xs ->
let rec helper l1 acc = match l1 with
| [] -> []
| x :: xs ->
if ((List.length xs) = ((List.length l) / 2)) then
(xs, (x :: acc))
else helper xs (x :: acc)
in helper l []
(Please take the time to copy/paste and format your code on SO rather than providing a link to an image. It makes it much easier to help, and more useful in the future.)
The first case of the match in your helper function doesn't return a pair. All the cases of a match need to return the same type (of course).
Note that the cases of your outermost match are also of different types (if you assume that helper returns a pair).
I have been trying one of the problems on 99 OCaml problems where you have to a have a list of all consecutive numbers in a list such as [2;3;4;4;5;6;6;6] -> [[2];[3];[4;4];[5];[6;6;6]]
let rec tail = function
| [] -> []
| [x] -> [x]
| x::xs -> tail xs;;
let pack lst =
let rec aux current acc = function
| [] -> current
| [x] -> if (tail acc) = x then (x::acc)::current
else [x]::current
| x::y::xs -> if (x=y) then aux current (x::acc) (y::xs)
else aux (acc::current) [] (y::xs)
in
aux [] [] lst;;
When i run this i get the error
Error: This expression has type 'a list
but an expression was expected of type 'a list list
The type variable 'a occurs inside 'a list
I was wondering what the problem is?
As Bergi pointed out (tail acc)=x is the problem. tail returns 'a list so x must also be of type 'a list. The following x::acc then infers that acc must be of type 'a list list. But tail acc infers acc as 'a list.
At this point OCaml can't unify the types of 'a list list and 'a list and gives the error you see.
I'm trying to implement sets through lists.. This is the code with the implementation (I omitted the interface):
module MySet : Set =
struct
type 'a set = 'a list
let empty : 'a set = []
let add (x: 'a) (s: 'a set) : 'a set =
if not(List.mem x s) then x::s
let remove (x: 'a) (s: 'a set) : 'a set =
let rec foo s res =
match s with
| [] -> List.rev res
| y::ys when y = x -> foo ys res
| y::ys -> foo ys (y::res)
in foo s []
let list_to_set (l: 'a list) : 'a set =
let rec foo l res =
match l with
| [] -> List.rev res
| x::xs when member x xs -> foo xs res
| x::xs -> foo xs (x::res)
in foo l []
let member (x: 'a) (s: 'set) : bool =
List.mem x s
let elements (s: 'a set) : 'a list =
let rec foo s res =
match s with
| [] -> List.rev res
| x::xs -> foo xs (x::res)
in foo s []
end;;
This is the error I get
Characters 162-164:
if not(List.mem x s) then x::s
^^
Error: The variant type unit has no constructor ::
I can't understand the error
It's a very confusing message that we got since 4.01 that stems from the fact that you have no else branch and that () is a valid constructor for unit.
Since you have no else branch the whole if must type to unit and thus the then branch aswell and it tries to unify the expression in the then branch with a value of type unit and detects that :: is not a constructor for values of type unit.
What you wanted to write is:
if not (List.mem x s) then x :: s else s
Without an else branch your add function needs to type to 'a -> 'a set -> unit
The strange error message is being bug tracked in OCaml's issue tracker, see PR 6173.
I really don't understand what's going on. I have the following code:
let rec interleave n l =
match l with
[] -> [[n]]
| head::tail -> (n::l)::(List.map (~f:fun y -> head::y) (interleave n tail))
in let rec aux l =
match l with
[] -> [l]
| head::tail -> List.concat ( List.map (interleave head) (aux tail) )
When compiling with ocaml it compiles and works as expected but under corebuild it gives me the following error:
The expression has type 'a list -> `a list list but an expression was
expected of type 'b list
Does it have something to do with labels again (as you see from ~f:fun y -> ... it has already annoyed me before)? If yes what kind of label should I use and where?
You need to reread part of manual about labeled arguments.
let rec interleave n l =
match l with
| [] -> [[n]]
| head::tail -> (n::l)::(List.map ~f:(fun y -> head::y) (interleave n tail))
and aux l =
match l with
| [] -> [l]
| head::tail -> List.concat ( List.map ~f:(interleave head) (aux tail) );;
N.B. right syntax for labeled arguments is ~label:expression
N.B. In Core List.map function has type 'a list -> f:('a -> 'b) -> 'b list and if you forget to add label to your function f it will try to unify 2nd argument with a function. That's why you have so weird error message.
Suppose I have some code like this:
List.map (fun e -> if (e <> 1) then e + 1 else (*add nothing to the list*))
Is there a way to do this? If so, how?
I want to both manipulate the item if it matches some criteria and ignore it if it does not. Thus List.filter wouldn't seem to be the solution.
SML has a function mapPartial which does exactly this. Sadly this function does not exist in OCaml. However you can easily define it yourself like this:
let map_partial f xs =
let prepend_option x xs = match x with
| None -> xs
| Some x -> x :: xs in
List.rev (List.fold_left (fun acc x -> prepend_option (f x) acc) [] xs)
Usage:
map_partial (fun x -> if x <> 1 then Some (x+1) else None) [0;1;2;3]
will return [1;3;4].
Or you can use filter_map from extlib as ygrek pointed out.
Both Batteries and Extlib provide an equivalent of mapPartial: their extended List module sprovide a filter_map function of the type ('a -> 'b option) -> 'a list -> 'b list, allowing the map function to select items as well.
Another solution would be to use directly a foldl :
let f e l = if (e <> 1)
then (e + 1)::l
else l
in List.fold_left f [] list
But my preference is filter_map as Michael Ekstrand provided
Alternatively you can filter your list then apply the map on the resulted list as follows :
let map_bis predicate map_function lst =
List.map map_function (List.filter predicate lst);;
# val map_bis : ('a -> bool) -> ('a -> 'b) -> 'a list -> 'b list = <fun>
Usage :
# map_bis (fun e -> e<>1) (fun e -> e+1) [0;1;2;3];;
- : int list = [1; 3; 4]
You can also map values to singleton lists if you want to keep them or empty lists if you don't, and then concat the results.
List.concat (List.map (fun e -> if (e <> 1) then [e + 1] else []) my_list)
use
let rec process = function
| 1 :: t -> process t
| h :: t -> (h + 1) :: (process t)
| [] -> []
or tail recursive
let process =
let rec f acc = function
| 1 :: t -> f acc t
| h :: t -> f ((h + 1) :: acc) t
| [] -> List.rev acc in
f []
or with a composition of standard functions
let process l =
l |> List.filter ((<>)1)
|> List.map ((+)1)
The OCaml standard library has had List.filter_map since 4.08. This can therefore now be written as:
List.filter_map (fun e -> if e <> 1 then Some (e + 1) else None)