Custom datatype and function in SML - sml

i have datatype expression defined.
datatype 'a expression = Not of 'a expression
| Or of 'a expression list
| And of 'a expression list
| Eq of 'a expression list
| Imp of 'a expression * 'a expression
| Var of 'a
| True | False;
And i need to implement function of this type
getVars (= ''a expression -> ''a list) goal of this function is to return all Var values in list.
Here is an example of function return.
- getVars (Eq [Var "A", Var "B", Imp (Var "D", Not (Var "Q")), Var "D", Var "B"]); val it = ["A","B","D","Q"] : string list
I have no idea how to implement that. Give me some advice plz.

Since you have provided no attempt, here is a template to get you started:
fun getVars expr =
case expr of
Not subExpr => ...
| Or subExprs => ...
| And subExprs => ...
| ...
Try and provide a bigger attempt to get more specific feedback.

Here is my attempt of solution for this case.
fun getVars expr =
case expr of
Var i => i (*Only those i's are in output list*)
| Not j => getVars(j) (*j is expression so i can use recursion*)
| Or k => map (fn x => getVars(x)) k (*there i need to loop over list and do recursion over every element in list, couse elements are expressions*)
| And k => map (fn x => getVars(x)) k (*same*)
| Eq k => map (fn x => getVars(x)) k (*same*)
| Imp k => (*problem for me couse its not list*)
(*And i need to put solution for True and False too...*);
But this code doesn't output list, only elements, becouse i don't know how to add elements to list if there is a case of no elements added it should return [].
getVars True = [];
getVars Eq [Var 1, Var 2] = [1,2];
getVars Or [Var "te", Var "st"] = ["te", "st"];
getVars And [True, False] = [];
getVars And [Var "st", Var "st"] = ["st"]; (*This one is interesting*)
There is more input and output examples.

Related

Why am I getting these errors in my sml code?

I'm creating an interpreter in Standard ML for Standard ML for an assignment but I can't seem to get past this problem.
I have this conditional in a function eval:
| eval (rho, SetExp (name, value)) =
(case rhoContains rho name of
true => rhoSet rho name value
| false => globalSet (name, value))
and these helper functions:
fun rhoSet [] key value = [(key, value)]
| rhoSet ((elt as (k, v)) :: tail) key value =
if key = k then (key, value) :: tail else elt :: rhoSet tail key value
fun rhoContains rho name =
case rhoGet rho name of SOME _ => true | NONE => false
fun globalSet key value =
let fun f [] = [(key, value)]
| f ((k,v)::tail) = if k = key then (k,value)::tail else (k,v)::f tail
in globals := f (!globals) end
Trying to run this gives me the following errors:
eval.sml:61: error: Clauses in case have different types.
Clause 1: true => rhoSet rho name value : bool -> (string * expression) list
Clause 2: false => globalSet (name, value) : bool -> int -> unit
Reason:
Can't unify (string * expression) list to int -> unit
(Incompatible types)
I'm not sure what's wrong at this point, and any help would be appreciated.

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."

How do you take a slice of a list in OCaml/ReasonML?

For example in Ruby you could do something like:
list = ["foo", "bar", "baz", "qux", "quux", "corge"]
result = list[2..4]
And result would contain ["baz", "qux", "quux"].
How would you do this in OCaml/ReasonML?
There is no in built function for slicing list, but can be done easily.
Since we have a start point and an end point, we can break down the problem in two parts. First part is to drop a few elements till we reach the starting point and second part is to take few elements from the start point till the end point.
let rec drop = (n, list) =>
switch (list) {
| [] => []
| [_, ...xs] as z => n == 0 ? z : drop(n - 1, xs)
};
let rec take = (n, list) =>
switch (list) {
| [] => []
| [x, ...xs] => n == 0 ? [] : [x, ...take(n - 1, xs)]
};
now that we have these two functions, we can combine them to drop initial elements from till start point drop(i, list) and then pass this new list to take elements from start point to end point
take(k - i + 1, drop(i, list));
in total
let slice = (list, i, k) => {
let rec drop = (n, list) =>
switch (list) {
| [] => []
| [_, ...xs] as z => n == 0 ? z : drop(n - 1, xs)
};
let rec take = (n, list) =>
switch (list) {
| [] => []
| [x, ...xs] => n == 0 ? [] : [x, ...take(n - 1, xs)]
};
take(k - i + 1, drop(i, list));
};
A better approach would be to provide starting point and then range rather than end point because here we don't constraint that end point should be bigger than starting point
let slice = (list, start, range) => {
let rec drop = (n, list) =>
switch (list) {
| [] => []
| [_, ...xs] as z => n == 0 ? z : drop(n - 1, xs)
};
let rec take = (n, list) =>
switch (list) {
| [] => []
| [x, ...xs] => n == 0 ? [] : [x, ...take(n - 1, xs)]
};
take(range, drop(start, list));
};
If you have access to bucklescript's Belt libraries, you could do something like:
open Belt;
let myList = ["first", "second", "third", "fourth", "fifth", "sixth"];
/* To get 2..4 */
myList
->List.drop(2)
->Option.getWithDefault([])
->List.take(3)
->Option.getWithDefault([])
->Js.log;
/* Gives you the list ["third", "fourth", "fifth"] */
There is no special language notation for OCaml slicing. You can write your function, say using the pattern matching, or combine head with take functions (those are available in standard libraries). For Reason combine List.hd and List.tk https://reasonml.github.io/api/List.html , also Array module has a sublist Array.sub. The OCaml was discussed here how to get a sub list from a list in ocaml
If you have access to BuckleScript, you can use:
let list = ["foo", "bar", "baz", "qux", "quux", "corge"];
let sliced = Js.Array.slice(~start=2, ~end_=4, list);
See more in the BuckleScript docs
Use
List.filteri (fun i _ -> i >= start && i <= end)

Ocaml use match with lazylist

I am trying to fill ma lazylist by unpaired elements (with recursion), starting with element k. For example: k = 2, list is [2,3,5,7,9,...] The code:
let lgen =
let rec gen k = LCons(k, fun () -> gen k (k + 2))
in gen 1;;
But how can I check is the element k unpaired? (I think that here I need to use match).
Assuming your type for lazy lists is something like this:
type 'a llist = LNil | LCons of 'a * (unit -> 'a llist);;
You can pattern match like this:
let rec lfind e lxs =
match lxs with
| LNil -> false
| LCons(x, _) when x > e -> false
| LCons(x, xs) -> if e=x then true else lfind e (xs ())
;;

ERROR: String list list instead of string list

I have this function that results a string list:
fun get_substitutions1 ([],_) = []
| get_substitutions1 (x::xs,s) = case all_except_option(s,x) of
NONE => [] #get_substitutions1(xs,s)
| SOME lst => lst #get_substitutions1(xs,s)
And this function that takes a string list list and a type:
fun similar_names(slist,full_name:{first:string,middle:string,last:string})=
let
fun aux(slist,acc)=
case full_name of
{first=a,middle=b,last=c} => case get_substitutions1(slist,a) of
[] => full_name::acc
| x::xs' => full_name:: aux(xs',{first=x,middle=b,last=c}::acc)
in aux(slist,[])
end
And i get an error:
Error: operator and operand don't agree.
operator domain: string list list *
{first:string, last:string, middle:string} list
operand: string list *
{first:string, last:string, middle:string} list
in expression:
aux (xs',{first=x,middle=b,last=c} :: acc)
Is there any other way?
Well first of all you might wan't to indent your code so that it is readable.
It is quite obvious why you get the error you do. The function
fun get_substitutions1 ([],_) = []
| get_substitutions1 (x::xs,s) =
case all_except_option(s,x) of
NONE => []#get_substitutions1(xs,s)
| SOME lst => lst #get_substitutions1(xs,s)
has the type
val get_substitutions1 = fn : ''a list list * ''a -> ''a list
and you are trying to use the result of this function in your inner case expression where you take the tail of the returned list (type 'a list) and use them in the recursive function call.
fun similar_names(slist,full_name:{first:string,middle:string,last:string})=
let
fun aux(slist,acc)=
case full_name of
{first=a,middle=b,last=c} =>
case get_substitutions1(slist,a) of
[] => full_name::acc
| x::xs' => full_name:: aux(xs',{first=x,middle=b,last=c}::acc)
in aux(slist,[])
end
However since your first argument of aux is used in get_substitutions1, that argument must be of type 'a list list, but the xs' you use down in the recursive call is only of type 'a list.