Case Statements and Pattern Matching - functional-programming

I'm coding in SML for an assignment and I've done a few practice problems and I feel like I'm missing something- I feel like I'm using too many case statements. Here's what I'm doing and the problem statements for what I'm having trouble with.:
Write a function all_except_option, which takes a string and a string list. Return NONE if the string is not in the list, else return SOME lst where lst is like the argument list except the string is not in it.
fun all_except_option(str : string, lst : string list) =
case lst of
[] => NONE
| x::xs => case same_string(x, str) of
true => SOME xs
| false => case all_except_option(str, xs) of
NONE => NONE
| SOME y=> SOME (x::y)
Write a function get_substitutions1, which takes a string list list (a list of list of strings, the substitutions) and a string s and returns a string list. The result has all the strings that are in some list in substitutions that also has s, but s itself should not be in the result.
fun get_substitutions1(lst : string list list, s : string) =
case lst of
[] => []
| x::xs => case all_except_option(s, x) of
NONE => get_substitutions1(xs, s)
| SOME y => y # get_substitutions1(xs, s)
-
same_string is a provided function,
fun same_string(s1 : string, s2 : string) = s1 = s2

First of all I would start using pattern matching in the function definition
instead of having a "top-level" case statement. Its basically boils down to the
same thing after de-sugaring. Also I would get rid of the explicit type annotations, unless strictly needed:
fun all_except_option (str, []) = NONE
| all_except_option (str, x :: xs) =
case same_string(x, str) of
true => SOME xs
| false => case all_except_option(str, xs) of
NONE => NONE
| SOME y => SOME (x::y)
fun get_substitutions1 ([], s) = []
| get_substitutions1 (x :: xs, s) =
case all_except_option(s, x) of
NONE => get_substitutions1(xs, s)
| SOME y => y # get_substitutions1(xs, s)
If speed is not of importance, then you could merge the two cases in the first function:
fun all_except_option (str, []) = NONE
| all_except_option (str, x :: xs) =
case (same_string(x, str), all_except_option(str, xs)) of
(true, _) => SOME xs
| (false, NONE) => NONE
| (false, SOME y) => SOME (x::y)
But since you are using append (#), in the second function, and since it is not
tail recursive, I don't believe that it your major concern. Keep in mind that
append is potential "evil" and you should almost always use concatenation (and
then reverse your result when returning it) and tail recursion when possible (it
always is).
If you really like the explicit type annotations, then you could do it like this:
val rec all_except_option : string * string list -> string list option =
fn (str, []) => NONE
| (str, x :: xs) =>
case (same_string(x, str), all_except_option(str, xs)) of
(true, _) => SOME xs
| (false, NONE) => NONE
| (false, SOME y) => SOME (x::y)
val rec get_substitutions1 : string list list * string -> string list =
fn ([], s) => []
| (x :: xs, s) =>
case all_except_option(s, x) of
NONE => get_substitutions1(xs, s)
| SOME y => y # get_substitutions1(xs, s)
But that is just my preferred way, if I really have to add type annotations.
By the way, why on earth do you have the same_string function? You can just do the comparison directly instead. Using an auxilary function is just wierd, unless you plan to exchange it with some special logic at some point. However your function names doesn't sugest that.

In addition to what Jesper.Reenberg mentioned, I just wanted to mention that a match on a bool for true and false can be replaced with an if-then-else. However, some people consider if-then-else uglier than a case statement

fun same_string( s1: string, s2: string ) = if String.compare( s1, s2 ) = EQUAL then true else false
fun contains( [], s: string ) = false
| contains( h::t, s: string ) = if same_string( s, h ) then true else contains( t, s )
fun all_except_option_successfully( s: string, [] ) = []
| all_except_option_successfully( s: string, h::t ) = if same_string( s, h ) then t else ( h :: all_except_option_successfully( s, t ) )
fun all_except_option( s: string, [] ) = NONE
| all_except_option( s: string, h::t ) = if same_string( s, h ) then SOME t else if contains( t, s ) then SOME ( h :: all_except_option_successfully( s, t ) ) else NONE

Related

SML, recursing a datatype array

I have this datatype
datatype json =
Num of real
| String of string
| False
| True
| Null
| Array of json list
| Object of (string * json) list
and this code
fun mymatch (jobj,str) =
case jobj of
Array [] => NONE
| Array(Object oh::[]) => mymatch (Object oh, str)
| Array (Object oh::ot) =>
if isSome (assoc(str, oh)) then assoc(str, oh) else mymatch (Array ot, str)
| Object xs => assoc (str, xs)
| _ => NONE
with this helper function
fun assoc (k, ls) =
case ls of
[] => NONE
| (a,b)::[] => if k = a then SOME b else NONE
| (a,b)::xs => if k = a then SOME b else assoc (k,xs)
which should take something like this
mymatch (Array [Object [("n", Num (4.0)),("b", True)],Object [("last", Num (4.0)),("foo", True)]],"foo")
and return a match on the string "foo", searching each Object in the Array. As you can see in the code, I'm really only handling the two things in json that qualify for a match, i.e., an Array that contains Objectss, then sending the Objects to be checked. This code works, but it's surely from the brutalist school of programming, i.e., it feels like a kludge. Why? Because of the case in mymatch where I need to recurse down through the Array
...
| Array (Object oh::ot) =>
if isSome (assoc(str, oh)) then assoc(str, oh) else mymatch (Array ot, str)
...
Until now, I've only dealt with recursion on lists where you check the car, then recurse on the cdr. Again, this code works, but I can sense I'm missing something. I need to check the head Object of Array and terminate if it matches; otherwise, keep recursing -- all within the option return world. Is there a more elegant way of doing this?
Write a function for matching the "innards" of arrays:
fun match_array (str, Object ob :: obs) = (case assoc (str, ob) of
NONE => match_array (str, obs)
| something => something)
| match_array _ = NONE;
then rewrite mymatch:
fun mymatch (str, Array a) = match_array (str, a)
| mymatch (str, Object ob) = assoc (str, ob)
| mymatch _ = NONE;
You can also simplify assoc a little bit:
fun assoc (k, []) = NONE
| assoc (k, (a,b)::xs) = if k = a then SOME b else assoc (k,xs);

Issue in sorting SML list

I am new to SML.I got this sorting algo to implement where in each iteration,I have to pick minimum element from the list, remove it and create sorted list.
I did below coding to solve the problem.
I wrote 2 helper functions to pickup minimum element from the list and remove one element from the list.
fun minList(x::xs) =List.foldl (fn (x,y)=> if x<y then x else y) x (x::xs);
fun remElem(x, l) =
case l of
[] => []
| (x1::x2::xs) => if x1=x then (x2::xs) else (x1::xs)
;
Above two programs ran successfully.
Below is my sorting code.
fun simpSort(xs)=
let fun aux(xs,acc)=
case xs of
[] =>acc
| [x] => [x]
| (x::xs) => let val m = minList(xs)
in
aux(remElem(m,xs),acc#[m])
end
in aux(xs,[])
end;
This sorting program is giving error.
simpSort([3,1]);
uncaught exception Match [nonexhaustive match failure]
raised at: stdIn:433.59
Please advise.
Since you've solved your problem, here are some hints for improving a working version of your code:
Find the minimum of a list in a way that supports empty lists:
fun minimum [] = NONE
| minimum (x::xs) = SOME (foldl Int.min x xs)
Simplify pattern matching in the function that removes the first occurrence of an element from a list:
fun remove (_, []) = []
| remove (y, x::xs) =
if x = y
then xs
else x :: remove (y, xs)
Use those in combination to write simpSort:
fun simpSort xs =
case minimum xs of
NONE => []
| SOME x => x :: simpSort (remove (x, xs))
I shouldn't have to say that this sorting algorithm is terribly inefficient. :-P

Changing to Tail Recursive SML

Ok, so I'm trying to change this function into Tail Recursive. The Definition I have of Tail Recursive is to use a "Local Helper Function" to accumulate my answer and return it without calling the primary function recursively.
these functions work properly.
fun same_string(s1 : string, s2 : string) =
s1 = s2
fun all_except_option (name, []) = NONE
| all_except_option (name, x::xs)=
case same_string (x , name) of
true => SOME xs
| false => case all_except_option(name,xs) of
NONE => NONE
| SOME z => SOME(x::z)
fun get_substitutions1 ([],name2) = [] (*get_substitutions2 is same but tail recursive *)
| get_substitutions1 (x::xs,name2) =
case all_except_option (name2,x) of
NONE => get_substitutions1 (xs,name2)
| SOME z => z # get_substitutions1(xs,name2)
So here are my attempts at tail recursion which do not work and I think I am missing something fairly basic that I am overlooking due to my lack of experience in SML.
fun get_substitutions2 (lst,name3) =
let fun aux (xs,acc) =
case all_except_option(name3,x::xs) of
NONE => aux(xs, acc)
| SOME z => aux(xs, z::acc)
in
aux(lst,[])
end
and
fun get_substitutions2 (lst,name3) =
let fun aux (xs,acc) =
case all_except_option(name3,x::xs) of
NONE => aux(xs, acc)
| SOME z => aux(xs, z#acc)
in
aux(lst,[""])
end
Both "get_substitutions" functions are supposed to do the same thing.
compare String1 to string list list, return single list made up of all lists containing String1 minus String1.
My attempts at using Tail Recursion have resulted in the following error.
Error: unbound variable or constructor: x
uncaught exception Error
raised at: ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
../compiler/TopLevel/interact/evalloop.sml:44.55
../compiler/TopLevel/interact/evalloop.sml:296.17-
Here are a few examples of calling get_substitutions2:
get_substitutions2 ([["foo"],["there"]], "foo"); (* = [] *)
get_substitutions2 ([["fred","fredrick","freddie","F","freddy"],["Will","William","Willy","Bill"]],"Bill"); (* = ["Will","William","Willy"] *)
get_substitutions2 ([["a","b"],["a","c"],["x","y"]], "a"); (* = ["c","b"] *)
You need to use the same patterns you had for get_substitutions1 in your aux function definition:
fun get_substitutions2 (lst,name3) =
let fun aux ([],acc) = acc (* BASE CASE *)
| aux (x::xs,acc) = (* BINDING x IN PATTERN *)
case all_except_option(name3,x) of
NONE => aux(xs, acc)
| SOME z => aux(xs, z#acc)
in
aux(lst,[])
end

SML Sum of List using option and Pattern matching

I'm creating sum of list and using option in it. When I pass an empty list, I should get NONE or else SOME value.
I'm able to do that in the following way:
fun sum_list xs =
case xs of
[] => NONE
| x =>
let
fun slist x =
case x of
[] => 0
| x::xs' => x + slist xs'
in
SOME (slist x)
end
But I want to do it in the other way round using pattern matching, in which I want to eval the result of sum_list to see whether it is NONE or contains some other value.
I have tried in various ways but I cannot get a hang of how to do in that way.
I think what you currently have is very clear and easy to understand.
If you want to avoid using slist, you have to call sum_list recursively on the tail of the list, pattern-match on that option value and return appropriate results:
fun sum_list xs =
case xs of
[] => NONE
| x::xs' => (case (sum_list xs') of
NONE => SOME x
| SOME y => SOME (x+y))

Flattening a stream in SML

I have this datatype:
datatype 'a stream' = Susp of unit -> 'a stream
and 'a stream = Empty | Cons of 'a * 'a stream'
and I want to write a flatten function which has the type below.
flatten: ’a stream’ stream’ -> ’a stream’
The flatten function will take stream of streams as input and flatten it by appending them.
How do I do this? Any ideas?
Thanks.
Edit: I know how to do it for lists. It is quite simple:
fun flatten [] = [] | flat (l::ls) = l # flatten ls;
Help me with streams please, I don't know how to pattern match a stream of a stream.
Let's write it for list first:
fun append(xs, ys) = case xs of
[] => ys
| (x::xs) => x :: append(xs, ys)
fun flatten(xss) = case xss of
[] => []
| (xs::xss) => append(xs, flatten(xss))
The above should be obvious. Now we only need to change it slightly to support stream, by Suspending and force-ing at the appropriate steps:
fun force(Susp(xs)) = xs()
fun append(xs, ys) = case force xs of
Empty => ys
| Cons(x,xs) => Susp(fn () => Cons(x, append(xs, ys)))
fun flatten(xss) = case force xss of
Empty => Susp(fn () => Empty)
| Cons(xs,xss) => append(xs, flatten(xss))

Resources