I'm working on Ocaml but i'm still a beginner so i have to ask a little help.
Following book instructions, i created a type that represent an oriented graph:
type 'a graph = Gr of ('a * 'a) list;;
let grafo1 = Gr [(1,2);(1,3);(1,4);(2,6);(3,5);(4,6);(6,5);(6,7);(5,4)];;
Then i created a succ function that take a node as input and it give me his successor as output:
let succ (Gr arcs) n=
let rec aux = function
[] -> []
| (x,y):: rest ->
if n = x then y::(aux rest)
else aux rest
in aux arcs;;
Then i used the succ function to make a modified BFS function, this function show me if exists a path between 2 nodes:
let bfs graph p start =
let rec search visited = function
[] -> raise Nodo_not_reachable
|n:: rest ->
if List.mem n visited
then search visited rest
else if p n then n
else search (n::visited) (rest # (succ graph n))
in search [] [start];;
I call the function using this code:
bfs grafo1 (function x -> x=7) 1;;
The function give true as output if exist a path between node 1 and node 7.
Now, i want to do the same things but with a WEIGHTED graph, so i created a new type, a list where each element is composed by 3 numbers instead 2: (node start - wiegh of edge - node arrive):
type 'b graph_w = Grw of ('b * 'b * 'b) list;;
let grafo2 = Grw [(1,3,2);(1,1,5);(2,2,3);(5,5,3);(5,4,6);(3,1,6);(3,7,4);(6,2,7);(4,4,6)];;
So, i modified my previous function to adapt them on this type:
let succ_w (Grw arcs) n=
let rec aux = function
[]-> []
| (x,y,z)::rest ->
if n=x then z::(aux rest)
else aux rest
in aux arcs;;
let bfs_w graph_w p start =
let rec search visited = function
[] -> raise Nodo_non_raggiungibile
|n:: rest ->
if find n visited
then search visited rest
else if p n then n
else search (n::visited) (rest # (succ_w graph_w n))
in search [] [start];;
(Since i can't use List.mem on this new type, i declared a function called find that give me true as output if an element (x,y,z) is contained on a list):
let rec find (x,y,z) = function
[] -> false
| (v,c,p)::rest -> if (x=v) then true else find (x,y,z) rest;;
find (2,3,1) [(2,2,3);(4,5,6);(8,9,0)];;
Now a little problem, someone can tell me how can i call my bfs_w function using the new graph type?
Using
bfs_w grafo2 (function x -> x=7) 1;;
I get the following error:
This expression has type int graph_w but an expression was expected of type ('a * 'b * 'c) graph_w
/--------------------------------/
Okay now the function work correctly thx ^^, but there is another problem: since i want to solve the longhest path problem using bfs (given a start node and a stop node, say true if exists a path between the nodes with a least weight k) i have to implement the (x,y,z) format on my function, so i tried something like this: (is the same function that you suggest but with (x,y,z) instead n:
let bfs_w2 graph_w start stop =
let rec search visited = function
| [] -> raise Node_not_Reachable
| (v,c,p) :: rest ->
if (find (v,c,p) visited) then search visited rest
else if v = stop then true
else search ((v,c,p)::visited) (rest # (succ_w graph_w (v,c,p))) in
search [] [start];;
When i declare the function:
bfs_w2 grafo2 1 4;;
or
bfs_w2 grafo2 (function x -> x=4) 1;;
i met the same error on "grafo2":
This expression has type int graph_w
but an expression was expected of type ('a * 'b * 'c) graph_w
I can't understand where the problem is, the function is almost identical to the one wich you suggest.
ps: i even tried this but i met same result:
let bfs_w2 graph_w p start =
let rec search visited = function
| [] -> raise Nodo_not_reachable
| (x,y,z) :: rest ->
if (List.mem (x,y,z) visited) then search visited rest
else if p (x,y,z) then (x,y,z)
else search ((x,y,z)::visited) (rest # (succ_w graph_w (x,y,z))) in
search [] [start];;
First of all let me define the model of you graph, so that we can speak the same language. You're using a edge list to represent a graph. For a graph of type 'a graph we will say that 'a is node, and 'a * 'a = node * node is an edge. So the graph itself is represented as edge list. To represent a weighted graph you're using edges that are labeled with weights, so the edge has now type node * weight * node, where weight has type int.
Now, let's go to your problem. If you look carefully in bfs implementation you will notice, that visited has type node list (i.e., int list), not edge list. And you're applying your predicate to a value of type node, not to an edge.
The same should be true to your bfs_w function. But here you, for some reason, decided that you're storing edges in a visited list, and used you own find function, instead of List.mem that is fully applicable here.
let bfs_w graph_w p start =
let rec search visited = function
| [] -> raise Not_found
| n :: rest ->
if List.mem n visited
then search visited rest
else if p n then n
else search (n::visited) (rest # (succ_w graph_w n)) in
search [] [start]
This implementation will have a correct type, expecting a user predicate that accepts a value of type node.
Related
I'm following this article on catamorphism and I'm trying to define a fold function for a recursive data type like this
type Node anyType
= Leaf Id (Maybe anyType)
| Tree Id (List (Node anyType))
What i wrote is this:
foldTree fLeaf fTree acc node =
let
recurse =
foldTree fLeaf fTree
in
case node of
Leaf id v ->
let
newAcc = fLeaf acc (id, v)
in
newAcc
Tree id l ->
let
newAcc = fTree acc id
in
l |> List.foldl recurse newAcc
If i don't infer the types for foldTree the function compiles, but it seems to not be usable:
collectIds node =
let
fLeaf acc (id,v) = id :: acc
fTree acc id = id :: acc
in
foldTree fLeaf fTree [] node
throws the following:
TYPE MISMATCH - The 1st argument to `foldTree` is not what I expect:
173| foldTree fLeaf fTree [] node
#^^^^^#
This `fLeaf` value is a:
#List a# -> ( a, b ) -> #List a#
But `foldTree` needs the 1st argument to be:
#Node anyType# -> ( Id, Maybe anyType ) -> #Node anyType#
Auto inferring the types for foldTree makes it not compilable and throws the following:
-- Auto Inferred
foldTree : (c -> (Id, Maybe anyType) -> a) -> (c -> Id -> b) -> c -> Node anyType -> d
TYPE MISMATCH - Something is off with the 1st branch of this `case` expression:
126| newAcc
#^^^^^^#
This `newAcc` value is a:
#a#
But the type annotation on `foldTree` says it should be:
#d#
#Hint#: Your type annotation uses `a` and `d` as separate type variables. Your
code seems to be saying they are the same though. Maybe they should be the same
in your type annotation? Maybe your code uses them in a weird way?
and if I try to follow the hint, still does not compile
foldTree : (c -> (Id, Maybe anyType) -> a) -> (c -> Id -> b) -> c -> Node anyType -> a
TYPE MISMATCH - This function cannot handle the argument sent through the (|>) pipe:
134| l |> List.foldl recurse newAcc
#^^^^^^^^^^^^^^^^^^^^^^^^^#
The argument is:
List #(Node anyType)#
But (|>) is piping it to a function that expects:
List #c#
#Hint#: Your type annotation uses type variable `c` which means ANY type of value
can flow through, but your code is saying it specifically wants a `Node` value.
Maybe change your type annotation to be more specific? Maybe change the code to
be more general?
Read <https://elm-lang.org/0.19.1/type-annotations> for more advice!Elm
TYPE MISMATCH - The 1st argument to `foldl` is not what I expect:
134| l |> List.foldl recurse newAcc
#^^^^^^^#
This `recurse` value is a:
c -> Node anyType -> #a#
But `foldl` needs the 1st argument to be:
c -> Node anyType -> #Node anyType#
#Hint#: Your type annotation uses type variable `a` which means ANY type of value
can flow through, but your code is saying it specifically wants a `Node` value.
Maybe change your type annotation to be more specific? Maybe change the code to
be more general?
I'm stuck. Following the types on the article precisely also does not seem to work. I understand that the code on the article is F# and I'm working on Elm but I thought that in this case it would have been 100% translatable.
Where am I going wrong?
Thanks in advance!
You've flipped the arguments to List.foldl. The fold function takes the value first and the accumulator second, while your recurse function takes the accumulator first and the value second.
The simple fix to this is to eta expand the recurse function and flip the arguments when passing it to foldTree:
recurse v a = foldTree fLeaf fTree a v
Also, interestingly, annotating the type of recurse will make it compile, but obviously produces the wrong result. I didn't pursue this further to understand why, as it was wrong, but the lesson you should take from this is to always annotate your top level functions. This will give you better error messages, but also prevents your code from accidentally compiling but producing the wrong result.
So let me start by saying this was part of a past homework I couldn't solve but as I am preparing for a test I would like to know how to do this. I have these implementations of map_tree and fold_tree provided by the instructor:
let rec map_tree (f:'a -> 'b) (t:'a tree) : 'b tree =
match t with
| Leaf x -> Leaf (f x)
| Node (x,lt,rt) -> Node (f x,(map_tree f lt),(map_tree f rt))
let fold_tree (f1:'a->'b) (f2:'a->'b->'b->'b) (t:'a tree) : 'b =
let rec aux t =
match t with
| Leaf x -> f1 x
| Node (x,lt,rt) -> f2 x (aux lt) (aux rt)
in aux t
I need to implement a function that verifies a tree is a BST using the above functions, so far this is what I've accomplished and I'm getting the error:
Error: This expression has type bool but an expression was expected of type
'a tree
This is my code:
let rec smaller_than t n : bool =
begin match t with
| Leaf x -> true
| Node(x,lt,rt) -> (x<n) && (smaller_than lt x) && (smaller_than rt x)
end
let rec greater_equal_than t n : bool =
begin match t with
| Leaf x -> true
| Node(x,lt,rt) -> (x>=n) && (greater_equal_than lt x) && (greater_equal_than rt x)
end
let check_bst t =
fold_tree (fun x -> true) (fun x lt rt -> ((check_bst lt) && (check_bst rt)&&(smaller_than lt x)&&(greater_equal_than rt x))) t;;
Any suggestions? I seem to have trouble understanding exactly how higher order functions work in OCAML
What is the specification of a BST? It's a binary tree where:
all the elements in the left subtree (which is also a BST) are strictly smaller than the value stored at the node
and all the ones in the right subtree (which is also a BST) are bigger or equal than the value stored at the node
A fold is an induction principle: you have to explain how to deal with the base cases (the Leaf case here) and how to combine the results for the subcases in the step cases (the Node case here).
A Leaf is always a BST so the base case is going to be pretty simple. However, in the Node case, we need to make sure that the values are in the right subtrees. To be able to perform this check, we are going to need extra information. The idea is to have a fold computing:
whether the given tree is a BST
and the interval in which all of its values live
Let's introduce type synonyms to structure our thoughts:
type is_bst = bool
type 'a interval = 'a * 'a
As predicted, the base case is easy:
let leaf_bst (a : 'a) : is_bst * 'a interval = (true, (a, a))
In the Node case, we have the value a stored at the node and the results computed recursively for the left (lih as in left induction hypothesis) and right subtrees respectively. The tree thus built is a BST if and only if the two subtrees are (b1 && b2) and their values respect the properties described earlier. The interval in which this new tree's values live is now the larger (lb1, ub2).
let node_bst (a : 'a) (lih : is_bst * 'a interval) (rih : is_bst * 'a interval) =
let (b1, (lb1, ub1)) = lih in
let (b2, (lb2, ub2)) = rih in
(b1 && b2 && ub1 < a && a <= lb2, (lb1, ub2))
Finally, the function checking whether a tree is a BST is defined by projecting out the boolean out of the result of calling fold_tree leaf_bst node_bst on it.
let bst (t : 'a tree) : bool =
fst (fold_tree leaf_bst node_bst t)
Given:
type 'a tree = Empty | Node of 'a * 'a tree * 'a tree
Use:
let rec tree_fold f e t = match t with
| Empty -> e
| Node (x, l, r) -> f x (tree_fold f e l) (tree_fold f e r);;
to convert a binary tree to a list.
Example.
let someTree = Node (1,Node(2,Node(3,Empty,Empty),Empty),Empty)
tree2list someTree gives [1;2;3].
My attempt:
let tree2list tr = match tr with
| Empty -> Empty
| Node (x,l,r) -> (tree_fold (fun acc tree-> tree::acc) [] tr)
It gives the error:
This expression has type 'a list but an expression was expected of type 'b -> 'b .
I've been at this for hours and keep getting similar errors. Any help would be very appreciated, even just a small hint would be great.
Thanks.
function that you should pass to a tree_fold accepts three arguments, and you're passing a function that can take only two arguments. That's what compiler tries to say. Of course, ther're some other problems, but I hope tha you'll cope with them!
I am trying to under the execution of lazy evaluation.
I created a lazy list type and according map function.
type 'a zlist = 'a node_t lazy_t
and 'a node_t = Empty | Node of 'a * 'a zlist
let rec zlist_of_list l = lazy (
match l with
| [] -> Empty
| hd::tl -> Printf.printf "transforming %d\n" hd;Node (hd, zlist_of_list tl)
)
let rec list_of_zlist zl =
match Lazy.force zl with
| Empty -> []
| Node (hd, tl) -> hd::(list_of_zlist tl)
let rec map_z f zl = lazy (
match Lazy.force zl with
| Empty -> Empty
| Node (hd, tl) -> Node (f hd, map_z f tl)
)
First question:
From my understanding, lazy just encapsulate things inside () behind without immediate execution.
So for function zlist_of_list, the whole
match l with
| [] -> Empty
| hd::tl -> Node (hd, zlist_of_list tl)
Will be delayed, not a single bit is executed when zlist_of_list is applied, so does map_z.
Am I right?
Below, I try to do double lazy map
let f1 x = Printf.printf "%d\n" x; x
let f2 x = Printf.printf " %d\n" (-x); (-x)
let zl = zlist_of_list [1;2;3]
let zl_m2 = map_z f2 (map_z f1 zl)
let _ = list_of_zlist zl_m2
The outcome is
transforming 1
1
-1
transforming 2
2
-2
transforming 3
3
-3
The I don't understand. It seems the execution is by column, not by row. I thought it should be
Every element is transformed first
Then f1 is mapped to every element
The f2 is mapped to every element
Second question:
Why via lazy, the execution order becomes like that?
To your first question: that's right, map_z will return a thunk that calculates the next part of the list, not the list itself. In particular, the recursive call within the definition of map_z will not descend into the rest of the list until it is forced - you can take one transformed element from the result of a map_z without calculating the rest.
This is also the answer to your second question: the reason you see one element being transformed, then passed to f1, then f2 is that at each step you are taking one element from a lazy list and the others remain suspended.
And that's the whole point of lazy lists! Doing things that way is useful because it provides a fairly natural way to program with infinite (or very large) lists. If an entire list had to be calculated first before using the elements, it would not really be a lazy data structure.
in OCaml
let nth_diff_type i (x, y, z) =
match i with
1 -> x
|2 -> y
|3 -> z
|_ -> raise (Invalid_argument "nth")
So the current type is int->('a,'a,'a)->'a, right?
It implies that x, y, z must have the same type.
So my question is that is it possible to give it maximum polymorphic, so that x, y, z don't need to have the same type?
No, it isn't.
A function in OCaml should have only one return type. You can have different argument types if your return type is unique:
let nth_diff_type i (x, y, z) =
match i with
| 1 -> `Fst x
| 2 -> `Snd y
| 3 -> `Thd z
|_ -> raise (Invalid_argument "nth")
// val nth_diff_type :
// int -> 'a * 'b * 'c -> [> `Fst of 'a | `Snd of 'b | `Thd of 'c ] = <fun>
If you would like to create a few utility functions for triplets, unfortunately you have to define them separately:
let fst3 (x, _, _) = x
let snd3 (_, y, _) = y
let thd3 (_, _, z) = z
If your function were to apply an arbitrary function f to an arbitrary element of a tuple such as the function f has the right type, then you could make this polymorphic to some extend.
In other words, if you think about what you may do with your function, you'll come up with the conclusion that you will need a function of the right type to apply it to the result of nth_diff_type, whatever that type may be.
If we suppose for an instant that nth_diff_type works with any tuple, its result is potentially any type. You might get an int, a string, or instances of more complex datatypes. Let's call that type t. Then what can you do with a value of type t? You may only pass it to a function which accepts values of t.
So the problem now is to select the proper function, and that selection certainly will be done on very similar criteria than the rank of the element within your tuple. If it is so, why not simply pass your tuple, and a tuple of the functions which could be applied to nth_diff_type , and let it do the application by itself?
let nth_diff_type i (a,b,c) (fa,fb,fc) =
match i with
| 1 -> fa a
| 2 -> fb b
| 3 -> fc c
| _ -> failwith "out of range"
-: val nth_diff_type : int -> ( 'a * 'b * 'c) -> (('a -> 'd) * ('b -> 'd) * ('c -> 'd)) -> 'd
The code is already fully-polymorphic in non-dependent type systems. You could move to a dependent type system (but you probably don't want, because of the complexity costs), where the type would be something like:
(n : int) -> (a * b * c) -> (match n with 1 -> a | 2 -> b | 3 -> c | _ -> Error)
Besides pad suggestion, you may also want to use a record or object type to have a direct "out projection" operation instead of having to define it by pattern-matching first.
type ('a, 'b, 'c) triple = { nth1 : 'a; nth2 : 'b; nth3 : 'c }
(* replace *) nth_diff_type 2 v (* by *) v.nth2
With an object type (which adds a structural flavor of not having to define the type beforehand)
(* replace *) nth_diff_type 2 v (* by *) v#nth2
Note that these replacement only work with constant integers (because otherwise you need an integer->type dependency). You could work out something with GADTs and existential types to support passing the particular choice around, but you will have to pay a huge complexity cost for something that is more likely a result of not being familiar enough with the existing simple type system to understand how you really want to do things.