OCaml Beginner here.
I have a trie with signature
type ('k, 'v) trie = Trie of 'v option * (('k * ('k, 'v) trie) list)
I need the insert method below, but am clueless. What is the best approach to go about implementing this with OCaml(Standard libs are fine)? Should I recurse over the trie, or the array inside of it? If so how do I do it with OCaml?:
val insert : ('k, 'v) trie -> 'k list -> 'v -> ('k, 'v) trie
insert t ks v returns a new trie that is the same as t, but ks is mapped to v.
Here are examples of a tries with mappings:
(* maps ['a'] to 1 *)
Trie (None, ['a', Trie (Some 1, [])])
(* maps [] to 1, ['a'] to 2, ['a'; 'b'] to 3 and ['a'; 'c'] to 4 *)
Trie (Some 1, ['a', Trie (Some 2, ['b', Trie (Some 3, []); 'c', Trie (Some 4, [])])])
It doesn't make any sense to say "recurse over the trie," at least not to me.
An obvious opportunity for recursion is when there is an internal structure with the same type as the containing structure. Your definition of a trie, for example, has a list of (key, trie) pairs inside it. It would stand to reason that your recursion would involve picking the appropriate one of these internal pairs and making a recursive call involving its trie.
Update
Some answers to new comments/questions.
I'm going to assume you keep your list of pairs sorted by key. You definitely do need to test whether the key is already there or not. This is no different, in essence, from adding a new element to a sorted list.
(If you don't keep the list sorted, you need to check whether your key is there and if not add the new key to the beginning. It's not much different, in the big picture.)
OCaml lists are immutable, so you can't actually add an element to a list. What you do instead is return a new list that has all the old elements plus the new one. The old list is not changed in the process (being immutable, it can't be changed).
I don't want to just write your code for you. But here's a function that "adds" a new element to a sorted list.
let rec add_to_list l e =
match l with
| [] -> [e]
| h :: t -> if e < h then e :: l else h :: add_to_list t e
Related
I'm writing a quicksort function for an exercise. I already know of the 5-line functional quicksort; but I wanted to improve the partition by having it scan through the list once and return a pair of lists splitting the original list in half. So I wrote:
fun partition nil = (nil, nil)
| partition (pivot :: rest) =
let
fun part (lst, pivot, (lesseq, greater)) =
case lst of
[] => (lesseq, greater)
| (h::t) =>
if h <= pivot then part (t, pivot, (h :: lesseq, greater))
else part (t, pivot, (lesseq, h :: greater))
in
part (rest, pivot, ([pivot], []))
end;
This partitions well enough. It gives me a signature val partition = fn : int list -> int list * int list. It runs as expected.
It's when I use the quicksort below that things start to break.
fun quicksort_2 nil = nil
| quicksort_2 lst =
let
val (lesseq, greater) = partition lst
in
quicksort_2 lesseq # quicksort_2 greater
end;
I can run the above function if I eliminate the recursive calls to quicksort_2; but if I put them back in (to actually go and sort the thing), it will cease to run. The signature will be incorrect as well, giving me val quicksort_2 = fn : int list -> 'a list. The warning I receive when I call the function on a list is:
Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...)
What is the problem here? I'm not using any ref variables; the type annotation I've tried doesn't seem to help...
The main issue is that you're lacking the singleton list base case for your quicksort function. It ought to be
fun quicksort [ ] = [ ]
| quicksort [x] = [x]
| quicksort xs =
let
val (l, r) = partition xs
in
quicksort l # quicksort r
end
which should then have type int list -> int list given the type of your partition. We have to add this case as otherwise you'll never hit a base case and instead recurse indefinitely.
For some more detail on why you saw the issues you were having though:
The signature will be incorrect as well, giving me val quicksort_2 = fn : int list -> 'a list
This is because the codomain of your function was never restricted to be less general than 'a list. Taking a look at the possible branches in your original implementation we can see that in the nil branch you return nil (of most general type 'a list) and in the recursive case you get two 'a lists (per our assumptions thus far) and append them, resulting in an 'a list---this is fine so your type is not further restricted.
[Value Restriction Warning]
What is the problem here? I'm not using any ref variables
The value restriction isn't really related to refs (though can often arise when using them). Instead it is the prohibition that anything polymorphic at the top level must be a value by its syntax (and thus precludes the possibility that a computation is behind a type abstractor at the top level). Here it is because given xs : int list we (ignoring the value restriction) have quicksort_2 xs : 'a list---which would otherwise be polymorphic, but is not a syntactic value. Correspondingly it is value restricted.
I was recently using the Map type from Data.Map inside a State Monad and so I wanted to write a function, that looks up a value in the Map and also deletes it from the Map inside the State Monad.
My current implementation looks like this:
lookupDelete :: (Ord k) => k -> State (Map k v) (Maybe v)
lookupDelete k = do
m <- get
put (M.delete k m)
return $ M.lookup k m
While this works, it feels quite inefficient. With mutable maps in imperative languages, it is not uncommon to find delete functions, that also return the value that was deleted.
I couldn't find a function for this, so I would really appreciate if someone knows one (or can explain why there is none)
A simple implementation is in terms of alterF:
lookupDelete :: Ord k => k -> State (Map k v) (Maybe v)
lookupDelete = state . alterF (\x -> (x, Nothing))
The x in alterF's argument is the Maybe value stored at the key given to lookupDelete. This anonymous function returns a (Maybe v, Maybe v). (,) (Maybe v) is a functor, and basically it serves as a "context" through which we can save whatever data we want from x. In this case we just save the whole x. The Nothing in the right element specifies that we want deletion. Once fully applied, alterF then gives us (Maybe v, Map k v), where the context (left element) is whatever we saved in the anonymous function and the right element is the mutated map. Then we wrap this stateful operation in state.
alterF is quite powerful: lots of operations can be built out of it simply by choosing the correct "context" functor. E.g. insert and delete come from using Identity, and lookup comes from using Const (Maybe v). A specialized function for lookupDelete is not necessary when we have alterF. One way to understand why alterF is so powerful is to recognize its type:
flip alterF k :: Functor f => (Maybe a -> f (Maybe a)) -> Map k a -> f (Map k a)
Things with types in this pattern
SomeClass f => (a -> f b) -> s -> f t
are called "optics" (when SomeClass is Functor, they're called "lenses"), and they represent how to "find" and "mutate" and "collate" "fields" inside "structures", because they let us focus on part of a structure, modify it (with the function argument), and save some information through a context (by letting us choose f). See the lens package for other uses of this pattern. (As the docs for alterF note, it's basically at from lens.)
There is no function specifically for "delete and lookup". Instead you use a more general tool: updateLookupWithKey is "lookup and update", where update can be delete or modify.
updateLookupWithKey :: Ord k =>
(k -> a -> Maybe a) -> k -> Map k a -> (Maybe a, Map k a)
lookupDelete k = do
(ret, m) <- gets $ updateLookupWithKey (\_ _ -> Nothing) k
put m
pure ret
I am actually sitting over a hour on a problem and donĀ“t find a solution for it.
I have this data type:
type 'a tree = Empty | Node of 'a * 'a tree * 'a tree
And i have to find a function which converts a given tree in a ordered list. There is also no invariant like that the left child has to be less then the right. I already found a "normal" recursion solution but not a tail recursive solution. I already thought about to build a unordered list and sort it with List.sort, but this uses a merge sort which is not tail recursive. Maybe someone has a good advice.
Thank you!
If you want to traverse the tree in order and return a list, that means our function inorder must have the type 'a tree -> 'a list.
let rec inorder t =
match t with
| Empty -> []
| Node (v, l, r) -> List.append (inorder l) (v :: (inorder r)) (* ! *)
However List.append is in tail position, not inorder. Another problem is we have two calls to inorder. If we put inorder l in tail position, inorder r could not possibly be in tail position - and vice versa.
A neat way to work around this problem is continuation passing style. We take our function above and convert it into a helper function with an extra parameter for our continuation, return
(* convert to helper function, add an extra parameter *)
let rec loop t return =
match t with
| Empty -> ...
| Node (v, l, r) -> ...
The continuation represents "what to do next", so instead of sending values directly out of our function, we must hand them to the continuation instead. That means for the Empty case, we'll return [] - instead of simply []
let rec loop t return =
match t with
| Empty -> return []
| Node (v, l, r) -> ...
For the Node (v, l, r) case, now that we have an extra parameter we can write our own continuation that informs loop what to do next. So to construct our sorted list, we will need to loop l, then loop r (or vice versa), then we can append them. We'll write our program just like this.
let rec loop t return =
match t with
| Empty -> return []
| Node (v, l, r) ->
loop l ... (* build the left_result *)
loop r ... (* build the right_result *)
return (List.append left_result (v :: right_result))
In this next step, we'll fill in the actual lambda syntax for the continuations.
let rec loop t return =
match t with
| Empty -> return []
| Node (v, l, r) ->
loop l (fun left ->
loop r (fun right ->
return (List.append left (v :: right))))
Last, we define inorder which is a call to loop with the default continuation, identity.
let identity x =
x
let inorder t =
let rec loop t return =
match t with
| Empty -> return []
| Node (v, l, r) ->
loop r (fun right ->
loop l (fun left ->
return (List.append left (v :: right))))
in
loop t identity
As you can see loop r (fun right -> ...) is in tail position for the Node branch. loop l (fun left -> ...) is in tail position of the first continuation. And List.append ... is in tail position of the second continuation. Provided List.append is a tail-recursive procedure, inorder will not grow the stack.
Note using List.append could be a costly choice for big trees. Our function calls it once per Node. Can you think of a way to avoid it? This exercise is left for the reader.
This is my function
let rec helper inputList = function
| [] -> []
| a :: b :: hd ->
if a = b then helper ([b::hd])
else a :: helper (b::hd)
It's not complete, however I can't see why I keep getting the error in the title at helper ([b::hd]). I've tried helper (b::hd) or helper (b::hd::[]) however all come up with errors. How do I make it so that it works?
When you use function you are supplying a pattern for the parameter of the function. But you already have a parameter named inputList. So this function helper is expecting two parameters (but it ignores the first).
You can fix this by removing inputList.
You also have a problem in your first recursive call to helper. Your expression [b :: hd] is a list of lists. I suspect that you want something more like just b :: hd here.
There is at least one other problem, but I hope this helps get you started.
There are multiple errors here. One is that the keyword function means we have an implicit parameter over which we are working. So the pattern matching happens on that "invisible" parameter. But here you defined probably the explicit one: inputList. So we can remove that one:
let rec helper = function
| [] -> []
| a :: b :: hd -> if a = b then helper ([b::hd]) else a :: helper (b:: hd)
Next there is a problem with the types: in the recursion, you use:
helper ([b::hd]); and
a :: helper (b:: hd)
But you put these on the same line, and that makes no sense, since the first one passes a list of lists of elements, and the second a list of elements. So the result of the first one would be a list of list of elements, and the second one a list of elements. It does not make sense to merge these.
If I understood correctly that you want to ensure that no two consecutive elements should occur that are equal, then we should rewrite it to:
let rec helper = function
| [] -> []
| a :: b :: hd -> if a = b then helper (b::hd) else a :: helper (b:: hd)
You have defined two patterns here:
one for the empty list; and
one for a list with at least two elements.
The second one will perform recursion on the tail of the list b :: hd. So that means that eventually when we pass it a list with n elements, it will recursively work on a list with n-1 elements, n-2 elements, etc. But eventually it will have one element. And there is no case for that. So we need to add a case for the one element pattern:
let rec helper = function
| [] -> []
| h :: [] -> h :: []
| a :: b :: hd -> if a = b then helper (b::hd) else a :: helper (b:: hd)
I trying to create a datatype for linked list which can hold all types at same time i.e linked list of void* elements , the designing is to create a Node datatype which hold a record contains Value and Next .
What I did so far is -
datatype 'a anything = dummy of 'a ; (* suppose to hold any type (i.e void*) *)
datatype linkedList = Node of {Value:dummy, Next:linkedList}; (* Node contain this record *)
As you can see the above trying does not works out , but I believe my idea is clear enough , so what changes are required here to make it work ?
I am not sure if you are being forced to use a record type. Because otherwise I think it is simpler to do:
datatype 'a linkedlist = Empty | Cons of 'a * 'a linkedlist
Then you can use it somewhat like:
val jedis = Cons ("Obi-wan", Cons("Luke", Cons("Yoda", Cons("Anakin", Empty))));
I think the use of the record is a poor choice here. I cannot even think how I could represent an empty list with that approach.
-EDIT-
To answer your comment about supporting multiple types:
datatype polymorphic = N of int | S of string | B of bool
Cons(S("A"), Cons(N(5), Cons(N(6), Cons(B(true), Empty))));
Given the circumstances you may prefer SML lists instead:
S("A")::N(5)::N(6)::B(true)::[];
Which produces the list
[S "A",N 5,N 6,B true]
That is, a list of the same type (i.e. polymorphic), but this type is capable of containing different kinds of things through its multiple constructors.
FYI, if it is important that the types of your polymorphic list remain open, you can use SML's built-in exception type: exn. The exn type is open and can be extended anywhere in the program.
exception INT of int
exception STR of string
val xs = [STR "A", INT 5, INT 6] : exn list
You can case selectively on particular types as usual:
val inc_ints = List.map (fn INT i => INT (i + 1) | other => other)
And you can later extend the type without mention of its previous definition:
exception BOOL of bool
val ys = [STR "A", INT 5, INT 6, BOOL true] : exn list
Notice that you can put the construction of any exception in there (here the div-by-zero exception):
val zs = Div :: ys : exn list
That said, this (ab)use really has very few good use cases and you are generally better off with a closed sum type as explained by Edwin in the answer above.