In the List module of OCaml, how is : val assoc : 'a -> ('a * 'b) list -> 'b implemented and (therefore) what is the complexity of this operation ? Is there a hashtbl hidden behind the scenes ?
The code is available online here: https://github.com/ocaml/ocaml/blob/trunk/stdlib/list.ml#L180-L182
let rec assoc x = function
[] -> raise Not_found
| (a,b)::l -> if compare a x = 0 then b else assoc x l
As you can see it's implemented as a linear search over the list.
Related
I am trying to implement boolean disjunction. I have successfully implemented it using recursion, and for a bit of practice I am trying to do the same using List.fold_right but I am failing miserably.
let q1fold lst =
List.fold_right (fun lst acc ->
match lst with
| [] -> acc
| h::t -> if h then true else false )
Consider the type signature of List.fold_right:
# List.fold_right;;
- : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = <fun>
Three arguments are taken. You've only passed one.
List.fold_right and List.fold_left consider an initial value and each element of the input list and runs a function on them to generate the initial value for the next iteration, returning that value at the end of the list.
What do you think the "update" and the initial values should be in the following to implement the functionality you want?
List.fold_right (fun x init -> ...) [true; false; false] ...
Note 1: I would use List.fold_left as it is tail-recursive, though for a small dataset it is unlikely to make a practical difference here.
Note 2: if h then true else false is equivalent to h.
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.
I have haskell background, now trying to learn OCaml.
I'm trying to implement zip as an exercise, but I'm getting a syntax error; what's the correct syntax?
let rec zip (l1 : 'a list) (l2 : 'b list) : ('a * 'b) list =
match l1 l2 with
[] _ -> []
| _ [] -> []
| (x::xs) (y::ys) -> (x,y)::(zip xs ys)
When you deconstruct/construct a pair, you need to put a , between its members.
Which means your zip function becomes:
let rec zip (l1 : 'a list) (l2 : 'b list) : ('a * 'b) list =
match l1,l2 with
| [],_ -> []
| _,[] -> []
| (x::xs),(y::ys) -> (x,y)::(zip xs ys)
Also, note that OCaml provide or-patterns when several cases of the pattern matching return the same value
This is not a pattern:
_ []
There is no pattern that consists of a function application. This makes it a syntax error and not just a semantic problem as in the previous expression l1 l2.
(Edited to give an actually erroneous pattern.)
I am trying to write a function that filters positive integers from a list of list of integers, returning a list of only negative integers.
For example, if I have a list of list such as [[-1; 1]; [1]; [-1;-1]] it would return [[-1]; []; [-1;-1]].
I tried to use filter and transform functions, which was in my textbook.
let rec transform (f:'a -> 'b) (l:'a list) : 'b list =
begin match l with
| [] -> []
| x::tl -> (f x)::(transform f tl)
end
and for filter, I had previously written:
let rec filter (pred: 'a -> bool) (l: 'a list) : 'a list =
begin match l with
| [] -> []
| x :: tl -> if pred x then x :: (filter pred tl) else filter pred tl
end
So, using these, I wrote
let filter_negatives (l: int list list) : int list list =
transform (fun l -> (filter(fun i -> i<0)) + l) [] l
but I'm still having trouble fully understanding anonymous functions, and I'm getting error messages which I don't know what to make of.
This function has type ('a -> 'b) -> 'a list -> 'b list
It is applied to too many arguments; maybe you forgot a `;'.
(For what it's worth this transform function is more commonly called map.)
The error message is telling you a simple, true fact. The transform function takes two arguments: a function and a list. You're giving it 3 arguments. So something must be wrong.
The transformation you want to happen to each element of the list is a filtering. So, if you remove the + (which really doesn't make any sense) from your transforming function you have something very close to what you want.
Possibly you just need to remove the [] from the arguments of transform. It's not clear (to me) why it's there.
I don't understand that the function (my_path_mapper) doesn't subject to the value restriction.
# let rec my_map ~f l =
match l with
[] -> []
| h::t -> f h::my_map f t;;
val my_map : f:('a -> 'b) -> 'a list -> 'b list = <fun>
# let my_path_mapper =
my_map ["/usr/sbin"; "/usr/bin"; "/sbin"; "/bin"; "/usr/games"; "/usr/local/games"];;
val my_path_mapper : f:(string -> 'a) -> 'a list = <fun>
Please teach me Why ?
OCaml has a "relaxed value restriction." You can read about it here:
Jacques Garrigue, Relaxing the Value Restriction
Here is a previous discussion on StackOverflow:
When does the relaxed value restriction kick in in OCaml?