Nested Pattern Matching - SML - Stepping Through an Example and Very Confused - functional-programming

I'm trying to understand pattern matching and recursion and how the program is executing.
In a simple case:
fun sum_list_num xs =
case xs of
[] => NONE
| x::xs' => case (sum_list_num xs') of
NONE => (print "x: "; print (Int.toString x);
print "\n"; SOME x)
| SOME y => (print "x: "; print (Int.toString x);
print " y: "; print (Int.toString y); print"\n";
SOME (x+y))
I can see that the y1 in SOME y is basically acting as an accumulator as the above is returning in sum_list_num [1,2,3,4,5];:
x: 5
x: 4 y: 5
x: 3 y: 9
x: 2 y: 12
x: 1 y: 14
val it = SOME 15 : int option
Am I right to be thinking that or have I missed the mark?.
Now for a more advanced example I'm trying to wrap my head around pattern matching and I've come across this post but I can't make heads or tails of how it works. I've modified the function to print out what's going on at each 'match':
fun smatch (str, in_stringlist) =
case in_stringlist of
[] => NONE
| x::x' => case(same_string(x, str), smatch (str, x')) of
(true, _) => (print "MATCH CASE x: "; print x;
print " x': "; print(gather(x'));
print "\n"; SOME x')
| (false, NONE) => (print "NONE CASE x: ";print x;
print "\n"; NONE)
| (false, SOME z) => (print "SOME Z CASE x: "; print x;
print " z: ";print(gather(z));
print " x': ";print(gather(x'));print "\n";
SOME (x :: z))
The output I was totally not expecting when called using smatch("this", ["1", "2", "this", "4", "5", "6"])
NONE CASE x: 6
NONE CASE x: 5
NONE CASE x: 4
MATCH CASE x: this x': 4 5 6
SOME Z CASE x: 2 z: 4 5 6 x': this 4 5 6
SOME Z CASE x: 1 z: 2 4 5 6 x': 2 this 4 5 6
val it = SOME ["1","2","4","5","6"] : string list option
Where I'm lost:
Why am I returning x' in the match case?
Why is everything in the list after the string I'm looking for evaluate to the NONE case?
I've been sitting here with a pad of paper and many edits and I'm having a tough time 'following' what exactly is happening in how the recursive calls are being stacked up and evaluated.
Any help would be appreciated. Reading SML guides on pattern matching all day have not made this any clearer!
note: the second example IS a homework question to a Coursera class (I answered it using a let function that used an accumulator to build up the list rather than a nested case statement, I came across the above while googling pattern matching SML and have been trying to wrap my head around it all day).
FYI gather is just a helper function so I can print out the contents of a list of strings:
fun gather xs =
foldl (fn (x,acc) =>
acc ^ " " ^ x) (hd xs) (tl xs)

I can see that the y1 in SOME y is basically acting as an accumulator
Am I right to be thinking that or have I missed the mark?
Yes, you are right.
The sum_list_num function calls itself recursively and returns the accumulated result to itself. Since the result is wrapped in SOME/NONE, pattern matching is used to unpack the result and pack it back into SOME again. E.g. SOME y becomes SOME (x+y).
Returning int option seems a little superfluous, since the sum of an empty list is well-defined:
fun sum_list_num [] = 0
| sum_list_num (x::xs) = x + sum_list_num xs
Or
fun sum_list_num xs = foldl op+ 0 xs
But for some problems you do want to pattern match on a resulting SOME/NONE and pack the result back in. In those cases you may also want to consider using the library functions Option.map and Option.getOpt. For example:
fun maximum [] = NONE
| maximum [x] = SOME x
| maximum (x::xs) = Option.map (fn y => Int.max (x, y)) (maximum xs)
Moving on,
fun smatch (str, in_stringlist) =
case in_stringlist of
[] => NONE
| x::x' => case(same_string(x, str), smatch (str, x')) of
(true, _) => (print "MATCH CASE x: "; print x;
print " x': "; print(gather(x'));
print "\n"; SOME x')
| (false, NONE) => (print "NONE CASE x: ";print x;
print "\n"; NONE)
| (false, SOME z) => (print "SOME Z CASE x: "; print x;
print " z: ";print(gather(z));
print " x': ";print(gather(x'));print "\n";
SOME (x :: z))
Why am I returning x' in the match case?
A better question might be: What is this function doing? Since then a part of that answer could be "... and return the rest of the list (x')." One part of this code that makes it hard to read, I think, is that smatch (str, x') is evaluated in each loop iteration, before the case body, regardless of whether its return value is used or not.
By handling the tail of the input list first, it is handled backwards (either by design or by accident).
Why is everything in the list after the string I'm looking for evaluate to the NONE case?
This is perhaps best shown by evaluating the function by hand for a small input. Every time I write a ~>, I've evaluated the next term (an input to a function before it's run, or a function call if all its inputs are evaluated. The first few term rewrites are:
smatch ("2", ["1", "2", "3"]) ~>
case (same_string("2", "1"), smatch ("2", ["2", "3"])) of ... ~>
case (false, smatch ("2", ["2", "3"])) of ... ~>
case (false, case (same_string("2", "2"), smatch ("2", ["3"])) of ...) of ... ~>
case (false, case (true, case (same_string ("2", "3"), smatch ("2", [])) of ...) of ...) of ... ~>
case (false, case (true, case (false, smatch ("2", [])) of ...) of ...) of ... ~>
case (false, case (true, case (false, NONE) of ...) of ...) of ...
At this point, the list has been fully traversed, the last element has been determined not to be "2", and the first case of a case-of statement where the pair it's matching ((false, NONE)) is actually being compared. We continue...
case (false, case (true, (print "NONE CASE x: ";print x; print "\n"; NONE)) of ...) of ... ~>
case (false, case (true, NONE) of ...) of ... ~>
case (false, (print "MATCH CASE x: "; print x; print " x': "; print(gather(x')); print "\n"; SOME ["3"])) of ... ~>
case (false, SOME ["3"]) of ... ~>
At this point, the (false, NONE) pattern case was matched against first, then the (true, _) pattern, and eventually the (false, SOME z) pattern:
(print "SOME Z CASE x: "; print x; print " z: "; print(gather(z)); print " x': "; print(gather(x')); print "\n"; SOME ("1" :: "3")) ~>
SOME ["1", "3"]
I've been sitting here with a pad of paper and many edits and I'm having a tough time 'following' what exactly is happening in how the recursive calls are being stacked up and evaluated.
Besides evaluating your function by hand (which is difficult when your term rewrite results in a huge case-of), you could structure your code differently so that it is more apparent when you evaluate the tail of your list before the actual elements. There doesn't really seem to be a need for nested pattern matching, either. And since this function really just filters out the first instance of str, why not write it like:
fun smatch (str, []) = []
| smatch (str, str2::strs) =
if str = str2
then strs
else str2::smatch(str, strs)
This function is much easier to evaluate by hand:
smatch ("2", ["1", "2", "3"]) ~>
if "2" = "1" then ... else "1"::smatch("2", ["2", "3"]) ~>
"1"::smatch("2", ["2", "3"]) ~>
"1"::(if "2" = "2" then ["3"] else ...) ~>
"1"::["3"]
["1", "3"]
By the way, your gather function already exists in the standard library as String.concatWith " ".

Related

converting to tail recursion

Consider a function g(x):
g(x) = x == 0 ? 0 : g(g(x - 1))
I don't think this is tail recursive, since the call to g(x) can be broken down into two parts:
let y = g(x - 1);
return g(y);
how to convert this to tail recursion?
continuation-passing style
You can convert from direct style to continuation-passing style -
g'(x,return) =
x == 0
? return(0)
: g'(x - 1, x' => # tail
g'(x', return)) # tail
Now write g to call g' with the default continuation -
g(x) = g'(x, x' => x')
Depending on the language you use, you may have to rename return to something else. k is a popular choice.
another example
We can see this technique applied to other problems like fib -
# direct style
fib(x) =
x < 2
? x
: fib(x - 1) + fib(x - 2) # "+" has tail position; not fib
# continuation-passing style
fib'(x, return) =
x < 2
? return(x)
: fib'(x - 1, x' => # tail| first compute x - 1
fib(x - 2, x'' => # tail| then compute x - 2
return(x' + x''))) # tail| then return sum
fib(x) = fib'(x, z => z)
other uses
Continuation-passing style is useful in other ways too. For example, it can be used to provide branching or early-exit behavior in this search program -
search(t, index, match, ifFound, notFound) =
i >= length(t)
? notFound()
: t[index] == match
? ifFound(index)
: search(t, index + 1, match, ifFound, notFound)
We can call search with continuations for each possible outcome -
search(["bird", "cat", "dog"],
0,
"cat",
matchedIndex => print("found at: " + matchedIndex),
() => print("not found")
)
how to
A function written in continuation-passing style takes an extra argument: an explicit "continuation"; i.e., a function of one argument — wikipedia
(* direct style *)
add(a, b) = ...
(* continuation-passing style takes extra argument *)
add(a, b, k) = ...
When the CPS function has computed its result value, it "returns" it by calling the continuation function with this value as the argument.
(* direct style *)
add(a, b) =
a + b
(* continuation-passing style "returns" by calling continuation *)
add(a, b, k) =
k(a + b) (* call k with the return value *)
That means that when invoking a CPS function, the calling function is required to supply a procedure to be invoked with the subroutine's "return" value.
(* direct style *)
print(add(5, 3)) (* 8 *)
(* continuation-passing style *)
add(5, 3, print) (* 8 *)
Expressing code in this form makes a number of things explicit which are implicit in direct style. These include: procedure returns, which become apparent as calls to a continuation; intermediate values, which are all given names; order of argument evaluation, which is made explicit; and tail calls, which simply call a procedure with the same continuation, unmodified, that was passed to the caller.
you've probably used continuations before
If you've ever run into someone saying "callback", what they really mean is continuation.
"When the button is clicked, continue the program with event => ..." -
document.querySelector("#foo").addEventListener("click", event => {
console.log("this code runs inside a continuation!")
})
<button id="foo">click me</button>
"When the file contents are read, continue the program with (err, data) => ..." -
import { readFile } from 'fs';
readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});

A function that compare a two lists of string

I am a new at F# and i try to do this task:
Make a function compare : string list -> string list -> int that takes two string lists and returns: -1, 0 or 1
Please help. I spend a lot of time, and i can not understand how to implement this task.
Given the task I assume what your professor wants to teach you with this exercise. I'll try to give you a starting point without
Confusing you
Presenting a 'done-deal' solution
I assume the goal of this task is to work with recursive functions and pattern matching to element-wise compare their elements. It could looks somewhat like this here
open System
let aList = [ "Apple"; "Banana"; "Coconut" ]
let bList = [ "Apple"; "Banana"; "Coconut" ]
let cList = [ "Apple"; "Zebra" ]
let rec doSomething f (a : string list) (b : string list) =
match (a, b) with
| ([], []) ->
printfn "Both are empty"
| (x::xs, []) ->
printfn "A has elements (we can unpack the first element as x and the rest as xs) and B is empty"
| ([], x::xs) ->
printfn "A is empty and B has elements (we can unpack the first element as x and the rest as xs)"
| (x::xs, y::ys) ->
f x y
printfn "Both A and B have elements. We can unpack them as the first elements x and y and their respective tails xs and ys"
doSomething f xs ys
let isItTheSame (a : string) (b : string) =
if String.Equals(a, b) then
printfn "%s is equals to %s" a b
else
printfn "%s is not equals to %s" a b
doSomething isItTheSame aList bList
doSomething isItTheSame aList cList
The example has three different lists, two of them being equal and one of them being different. The doSomething function takes a function (string -> string -> unit) and two lists of strings.
Within the function you see a pattern match as well as a recursive call of doSomething in the last match block. The signatures aren't exactly what you need and you might want to think about how to change the parametrization for cases where you don't want to stop the recursion (the last match block - if the strings are equal you want to keep on comparing, right?).
Just take the code and try it out in FSI. I'm confident, that you'll find the solution 🙂
In F# many collections are comparable if their element type is:
let s1 = [ "a"; "b" ]
let s2 = [ "foo"; "bar" ]
compare s1 s2 // -5
let f1 = [ (fun () -> 1); fun () -> 2 ]
let f2 = [ (fun () -> 3); fun () -> 42 ]
// compare f1 f2 (* error FS0001: The type '(unit -> int)' does not support the 'comparison' constraint. *)
so
let slcomp (s1 : string list) s2 = compare s1 s2 |> sign
Posting for reference as the original question is answered already.

SML syntactical restrictions within recursive bindings?

There seems to be syntactical restrictions within SML's recursive bindings, which I'm unable to understand. What are these restrictions I'm not encountering in the second case (see source below) and I'm encountering when using a custom operator in the first case?
Below is the case with which I encountered the issue. It fails when I want to use a custom operator, as explained in comments. Of the major SML implementations I'm testing SML sources with, only Poly/ML accepts it as valid, and all of MLton, ML Kit and HaMLet rejects it.
Error messages are rather confusing to me. The clearest one to my eyes, is the one from HaMLet, which complains about “illegal expression within recursive value binding”.
(* A datatype to pass recursion as result and to arguments. *)
datatype 'a process = Chain of ('a -> 'a process)
(* A controlling iterator, where the item handler is
* of type `'a -> 'a process`, so that while handling an item,
* it's also able to return the next handler to be used, making
* the handler less passive. *)
val rec iter =
fn process: int -> int process =>
fn first: int =>
fn last: int =>
let
val rec step =
fn (i: int, Chain process) (* -> unit *) =>
if i < first then ()
else if i = last then (process i; ())
else if i > last then ()
else
let val Chain process = process i
in step (i + 1, Chain process)
end
in step (first, Chain process)
end
(* An attempt to set‑up a syntax to make use of the `Chain` constructor,
* a bit more convenient and readable. *)
val chain: unit * ('a -> 'a process) -> 'a process =
fn (a, b) => (a; Chain b)
infixr 0 THEN
val op THEN = chain
(* A test of this syntax:
* - OK with Poly/ML, which displays “0-2|4-6|8-10|12-14|16-18|20”.
* - fails with MLton, which complains about a syntax error on line #44.
* - fails with ML Kit, which complains about a syntax error on line #51.
* - fails with HaMLet, which complains about a syntax error on line #45.
* The clearest (while not helpful to me) message comes from HaMLet, which
* says “illegal expression within recursive value binding”. *)
val rec process: int -> int process =
(fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process))))
val () = iter process 0 20
val () = print "\n"
(* Here is the same without the `THEN` operator. This one works with
* all of Poly/ML, MLton, ML Kit and HaMLet. *)
val rec process =
fn x =>
(print (Int.toString x);
Chain (fn x => (print "-";
Chain (fn x => (print (Int.toString x);
Chain (fn x => (print "|";
Chain process)))))))
val () = iter process 0 20
val () = print "\n"
(* SML implementations version notes:
* - MLton, is the last version, built just yesterday
* - Poly/ML is Poly/ML 5.5.2
* - ML Kit is MLKit 4.3.7
* - HaMLet is HaMLet 2.0.0 *)
Update
I could work around the issue, but still don't understand it. If I remove the outermost parentheses, then it validates:
val rec process: int -> int process =
fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process)))
Instead of:
val rec process: int -> int process =
(fn x => print (Int.toString x) THEN
(fn x => print "-" THEN
(fn x => print (Int.toString x) THEN
(fn x => print "|" THEN
process))))
But why is this so? An SML syntax subtlety? What's its rational?
It's just an over-restrictive sentence in the language definition, which says:
For each value binding "pat = exp" within rec, exp must be of the form "fn match".
Strictly speaking, that doesn't allow any parentheses. In practice, that's rarely a problem, because you almost always use the fun declaration syntax anyway.

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))

Case Statements and Pattern Matching

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

Resources