AVL Tree in ML- Rotate left, Warning: match nonexhaustive - sml

I am implementing an AVL tree in SML:
Here are my datatypes:
datatype 'a AVLTree = Nil | Br of ((int*('a))*('a AVLTree)*('a AVLTree));
datatype Balance = RR | LR | LL | RL;
exception NotFound;
exception NullValue
I am now writing the function Rotate- Left, and I wrote it as follows:
fun rotate_left Nil = Nil
|rotate_left (Br(x,A,Br(y,B,C))) = (Br(y,Br(x,A,B),C));
I am getting from the interpreter:
Warning: match nonexhaustive
How do I fix this with the current datatypes I have?
I tried using wildcards but with no success..

I am getting from the interpreter:
Warning: match nonexhaustive
How do I fix this with the current datatypes I have? [...]
Perhaps this warning should not be avoided. You can after all not rotate all trees.
The left rotation would look like this:
A B
/ \ / \
… B ~> A C
/ \ / \ / \
… C … … …
/ \
… …
Given an AVL tree type that does not keep track of the height,
datatype 'a AVLTree = Nil | Br of 'a * 'a AVLTree * 'a AVLTree
(the parentheses are not necessary) your version of rotate_left is correct. I've rewritten it below and renamed the left and right sub-branches. One point is that B's left sub-branch becomes A's new right sub-branch.
fun rotate_left (Br (a, leftA, Br (b, leftB, rightB))) =
Br (b, Br (a, leftA, rightB), rightB)
This is a partial function, and the patterns it fails to match are:
Nil – but a left-rotation of an empty tree isn't well-defined.
Br (a, leftA, Nil) – but the following left-rotation also isn't well-defined:
A ?
/ \ / \
… Nil ~> A …
/ \
… ?
If you were to try and perform a left-rotation of one of those trees, there would not be a meaningful result. Having the Warning: match nonexhaustive is not satisfying either. You could instead raise a meaningful exception, e.g.
fun rotate_left (Br (a, leftA, Br (b, leftB, rightB))) =
Br (b, Br (a, leftA, rightB), rightB)
| rotate_left _ = raise Fail "Invalid left-rotation"
Now, you haven't really explained why there's an extra int in your datatype definition. Maybe this is the pre-computed height of the tree? That'd be neat (you could encode that meaning using a type alias), since then your invariance check becomes cheaper. In that case, a left-rotation would need to update the heights:
type height = int
datatype 'a AVLTree = Nil | Br of height * 'a * 'a AVLTree * 'a AVLTree
According to the top drawing, A's new height is max(height(leftA), height(leftB)) + 1 and B's new height max(height(new A), height(rightB)). Extending the rotate_left function for this:
fun height Nil = 0
| height (Br (h, _, _, _)) = h
fun rotate_left (Br (ha, a, leftA, Br (hb, b, leftB, rightB))) =
let val ha' = Int.max (height leftA, height leftB) + 1
val hb' = Int.max (ha', height rightB) + 1
in Br (hb', b, Br (ha', a, leftA, rightB), rightB) end
| rotate_left _ = raise Fail "Invalid left-rotation"
Edit: It occurs to me that the extra int is in a nested tuple and so probably turns the tree into a mapping from some integer to some 'a. In that case, disregard the optimization of keeping the height in the tree.

You function is not defined for values with the shape Br(x, A, NIL).
What should happen in this case?
fun rotate_left Nil = Nil
| rotate_left (Br(x,A,Br(y,B,C))) = (Br(y,Br(x,A,B),C))
| rotate_left (Br(x, A, Nil)) = (* ??? *);

I played around with this functions a little bit and this is what I got that works:
fun RL node =
case node of Br(x,A,Br(y,B,C)) => (Br(y,Br(x,A,B),C))
| _ => node;

Related

How to return a SOME list instead of a normal list?

I am trying to implement a program that takes a string and a list and returns NONE if it doesn't find any match and the list without the element if it does.
fun all_except_option ("string",["he","she","string"]) = SOME["he","she"]
I have managed to make it working, but without the options type and i have no idea how to make it return SOME list instead a normal list.
fun all_except_option(str,lst)=
case lst of
[] => []
| x::lst' => if same_string(x,str) = false
then let fun append (word, list) = word::list
in append(x,[]) :: all_except_option(str,lst')
end
else all_except_option(str,lst')
Thank you. I managed to make it working, but i still don't understand the "else case" and how is my programm handling it. Here is the working code. I would be glad if you can explain me the "else case all_except_option(str,list') of".
fun all_except_option(str,list)=
case list of
[] => NONE
| x::list' => if same_string(x,str) then
SOME( list')
else case all_except_option(str,list') of
NONE=>NONE
| SOME list'=>SOME(x::list')
implement a program that takes a string and a list and returns NONE if it doesn't find any match and the list without the element if it does.
all_except_option ("string",["he","she","string"]) = SOME ["he","she"]
How is SOME [] different from NONE? As in, if this function returned just a list, it would be possible to say that removing occurrences of "string" results in no other strings: Either the list was empty already, or it contained only occurrences of "string". I am not sure why NONE vs. SOME [] is warranted in one case over the other.
So the better function is one that simply returns a plain list:
fun except (x, ys) = List.filter (fn y => x <> y)
When is it useful to return 'a option?
For example when the return type does not have a way to indicate no result already:
fun lookup k1 [] = NONE
| lookup k1 ((k2,v)::pairs) =
if k1 = k2
then SOME v
else lookup k1 pairs
This function returns 0 or 1 thing. But it's also a simple function because it never aggregates results over its recursion. Recursive functions become complicated when they return composite data types like 'a option when it needs to unpack the result of the recursion.
A good example is an eval function that sometimes fails:
datatype expr
= Add of expr * expr
| Sub of expr * expr
| Mul of expr * expr
| Div of expr * expr
| Int of int
fun eval (Int n) = SOME n
| eval (Add (e1, e2)) = evalHelper ( op+ ) (e1, e2)
| eval (Sub (e1, e2)) = evalHelper ( op- ) (e1, e2)
| eval (Mul (e1, e2)) = evalHelper ( op* ) (e1, e2)
| eval (Div (e1, e2)) =
case eval e1 of
NONE => NONE
| SOME x => case eval e2 of
NONE => NONE
| SOME 0 => NONE
| SOME y => SOME (x div y)
and evalHelper binop (e1, e2) =
case eval e1 of
NONE => NONE
| SOME x => case eval e2 of
NONE => NONE
| SOME y => SOME (binop (x, y))
Here the return type is int option, which means that you most often return an int, but if you ever divide by zero, that results in "no value", so rather than raise an exception, we return NONE, which necessitates us to return SOME n when there is a result, so that the type fits in both cases.
A quick demonstration:
- eval (Div (Int 5, Int 2));
> val it = SOME 2 : int option
- eval (Div (Int 5, Int 0));
> val it = NONE : int option
- eval (Div (Int 2, Sub (Int 3, Int 3)));
> val it = NONE : int option
- eval (Div (Int 0, Int 1));
> val it = SOME 0 : int option
Here SOME 0 actually means "the result is 0", which is not the same as "cannot divide by zero."

Same let binding leads to different compilation results

type 'a tree =
| Leaf of 'a
| Node of 'a * 'a tree * 'a tree
let rec foldtree init op = function
| Leaf c -> op c init init
| Node (c, l, r) -> op c (foldtree init op l) (foldtree init op r)
let size' = foldtree 0 (fun _ l r -> 1 + l + r) (* this compiles fine *)
let size'' = foldtree 0 (fun _ l r -> 1 + l + r) (* this doesn't *)
In the above OCaml code, the definitions of size' and size'' are identical yet the latter causes a compilation error:
Error: The type of this expression, '_weak1 tree -> int,
contains type variables that cannot be generalized
They should both fail to compile, as both contain weak type variables. The compiler will only report one fatal error at a time, however.
Incidentally, you would usually fix this problem by eta-expansion:
let size tree = foldtree 0 (fun _ l r -> 1 + l + r) tree

SML only accepting int list when it should be a real list

I am trying to write a simple add function that takes two real lists and adds the matching indices together and generates a real list, but for some reason I can't get it to accept real lists as the parameters, but instead only int lists.
fun add (nil, _) = nil
| add (_, nil) = nil
| add (a :: b, x :: y) = (a + x) :: add (b,y)
When I try running my test input, val addTest = add([1.0, 2.0, 3.0], [0.1, 0.2, 0.3]); it gives me:
Error: operator and operand do not agree [tycon mismatch]
operator domain: int list * int list
operand: real list * real list
And I am just curious as to why SML is defaulting to an int list even though the "+" operand is used for both reals and ints. Shouldn't it be accepting `a list instead of just int lists?
Yes, + (along with other arithmetic operators) is overloaded but not parametrically polymorphic.
So you can do 1.0 + 1.0 and 1 + 1 and they give a real and an int respectively.
But fun f x y = x + y can infer to either, so the compiler defaults to the int overload.
As an addition to your own answer, you can do with a single : real in your code:
fun add ([], _) = []
| add (_, []) = []
| add (x::xs, y::ys) = (x + y : real) :: add (xs, ys)
and it will infer that you must mean real in all the other places, too.
You could generalise this operation into one called zipWith:
- fun zipWith f [] _ = []
| zipWith f _ [] = []
| zipWith f (x::xs) (y::ys) = f (x, y) :: zipWith f xs ys
> val ('a, 'b, 'c) zipWith = fn :
('a * 'b -> 'c) -> 'a list -> 'b list -> 'c list
- val add = zipWith (op + : real * real -> real)
> val add = fn : real list -> real list -> real list
- add [1.0, 2.0, 3.0] [4.0, 5.0, 6.0];
> val it = [5.0, 7.0, 9.0] : real list
I found out that the default behavior for SML in a case like this is to default to int behavior, so if you have an operand that works for either reals or ints it will be evaluated as an int. As for the method above I was able to get my desired behavior by specifying the parameters in the tuple to be real lists like so:
fun add (nil, _) = nil
| add (_, nil) = nil
| add (a::b : real list, x::y : real list) = (a + x) :: add (b,y)

When is OCaml's warning 27 "Innocuous unused variable" useful?

This is the description of warning 27 from the OCaml manual:
27 Innocuous unused variable: unused variable that is not bound with let nor as, and doesn't start with an underscore (_) character.
This warning is turned on by jbuilder --dev, and I'm curious to know in which cases people find it useful. For me, it's an annoyance to get warnings when I write code like this:
$ utop -w +27
utop # fun (x, y) -> x;;
Characters 8-9:
Warning 27: unused variable y.
- : 'a * 'b -> 'a = <fun>
or like that:
utop # let error loc msg = failwith (loc ^ ": " ^ msg);;
val error : string -> string -> 'a = <fun>
utop # let rec eval = function
| `Plus (loc, a, b) -> eval a + eval b
| `Minus (loc, a, b) -> eval a - eval b
| `Star (loc, a, b) -> eval a * eval b
| `Slash (loc, a, b) ->
let denom = eval b in
if denom = 0 then
error loc "division by zero"
else
eval a / denom
| `Int (loc, x) -> x
;;
Characters 33-36:
Warning 27: unused variable loc.
Characters 73-76:
Warning 27: unused variable loc.
Characters 112-115:
Warning 27: unused variable loc.
Characters 287-290:
Warning 27: unused variable loc.
val eval :
([< `Int of 'b * int
| `Minus of 'c * 'a * 'a
| `Plus of 'd * 'a * 'a
| `Slash of 'e * 'a * 'a
| `Star of 'f * 'a * 'a ]
as 'a) ->
int = <fun>
I know that prepending an underscore to the identifiers as in _loc suppresses the warnings, but it's not compatible with my notions that:
variables starting with an underscore are ugly and are meant for use in generated code, hidden from the programmer;
a name given to something should not have to change based on how it's used (including unused).
Using underscores, the code becomes:
(* Here we have _loc or loc depending on whether it's used. *)
let rec eval = function
| `Plus (_loc, a, b) -> eval a + eval b
| `Minus (_loc, a, b) -> eval a - eval b
| `Star (_loc, a, b) -> eval a * eval b
| `Slash (loc, a, b) ->
let denom = eval b in
if denom = 0 then
error loc "division by zero"
else
eval a / denom
| `Int (_loc, x) -> x
or
(* Here it can be hard to know what _ stands for. *)
let rec eval = function
| `Plus (_, a, b) -> eval a + eval b
| `Minus (_, a, b) -> eval a - eval b
| `Star (_, a, b) -> eval a * eval b
| `Slash (loc, a, b) ->
let denom = eval b in
if denom = 0 then
error loc "division by zero"
else
eval a / denom
| `Int (_, x) -> x
It is very useful in the monadic code, where instead of the common syntactic let bindings you're forced to use monadic >>= bind operator. Basically, where
let x = something in
code
translates to
something >>= fun x ->
code
If x is not used in code then only with the 27 warning enabled the latter will be highlighted, while the former will produce a warning by default. Enabling this warning, revealed lots of bugs for us. For example, it showed us that this code is buggy :)
Another source of use cases are higher-order functions, i.e., map, fold, etc. It captures one of the most common bugs:
let bug init =
List.fold ~init ~f:(fun acc xs ->
List.fold ~init ~f:(fun acc x -> x :: acc))
Concerning the ugliness, I totally agree that underscores are ugly, but in most cases, this is the main purpose of them - to highlight the suspicious code. Concerning the example, that you're showing, in the modern OCaml it could be easily addressed with the inline records, e.g.,
type exp =
| Plus of {loc : loc; lhs : exp; rhs: exp}
| ...
so that instead of using the underscores, you can just omit the unused field,
let rec eval = function
| Plus {lhs; rhs} -> eval lhs + eval rhs
You can use the same approach without using inline records by sparing some extra space in your program and defining all those records separately. The real-world example.
For me this warning is useful in order to remind me to explicit more my intention. If we take your example :
fun (x, y) -> x;;
Your intention is to use only the first element. If we rewrite it this way :
fun (x, _ ) -> x;;
You use a pattern matching in the parameter to make your code more concise, but you explain your intention of using only the first element. The added value in this example is small, related to the very simple implementation. But in real life functions, this warning promote a good habit in coding.

OCaml: Instantiating parameterized type imported from another module

I have a module in one file that contains the type type move = Move of int. In another file, I open this module and can refer to the type by ModuleName1.move. But is it possible to construct an instance of this type in the second file, given that I'd have to use the Move i syntax, and since that Move parameter/keyword isn't really accessible from the second file?
Here's the module where I want to instantiate the type from the first module (which is called Game and contains the type type move = Move of int. It's right at the end, in the next_move function, that I want to construct a (Move 0) and pass it to make_tree, however it doesn't recognize Move since it's a parameterized type constructor from the other module:
#use "sig_player.ml" ;;
#use "game.ml" ;;
module TestAIPlayer =
struct
module PlayerGame = Game
open PlayerGame
let max_depth = 4 ;;
(* Data Definition *)
type tree = Node of PlayerGame.state * PlayerGame.move * tree list ;;
let rec make_tree (root: PlayerGame.state * PlayerGame.move) (d: int): tree =
let (s, m) = root in
let lms = PlayerGame.legal_moves s in
match lms, d with
| [], _ | _, 0 -> Node (s, m, [])
| _, _ -> Node (s, m, (List.map
(fun mv -> make_tree ((PlayerGame.next_state s mv), mv) (d - 1))
lms)) ;;
let compare_node (n1: PlayerGame.move * float) (n2: PlayerGame.move * float)
(comp: 'a -> 'a -> 'a): PlayerGame.move * float =
match n1, n2 with
| (m1, f1), (m2, f2) -> if (comp f1 f2) = f1 then n1 else n2 ;;
let rec minimax (t: tree) (mm: bool): PlayerGame.move * float =
match t with
| Node (s, m, []) -> (m, PlayerGame.estimate_value s)
| Node (s, m, children) -> let propagated = List.map
(fun c -> minimax c (not mm)) children in
(match mm with
| true -> List.fold_right
(fun x y -> compare_node x y max)
propagated (m, neg_infinity)
| false -> List.fold_right
(fun x y -> compare_node x y min)
propagated (m, infinity)) ;;
let next_move s = minimax (make_tree (s, (Move 0)) max_depth) true ;;
end ;;
module AIPlayer = (TestAIPlayer : PLAYER with module PlayerGame := Game) ;;