Easier way to generate a truth table - functional-programming

I want to create a list of lists in SML, which represents a truth table of the following form:
Example:
[
[("r",true),("p",true),("q",true)],
[("r",false),("p",false),("q",true)],
[("r",false),("p",true),("q",true)],
...
]
I think I could achieve this in two ways:
(1) with the cartesian product
(2) converting truth table index entry to binary, which would represent an encoded line in the list (e.g. 8(decimal) is 1000(binary) => [("r",true),("p",false),("q",false)]), but I think this is to complicated and there is probably an easier way.
What would be the easiest way to go about this?

fun tt [] = [[]]
| tt (x :: xs) =
let
val txs = tt xs
in
map (fn l => (x, true) :: l) txs #
map (fn l => (x, false) :: l) txs
end
- tt ["a", "b", "c"];
val it =
[[("a",true),("b",true),("c",true)],[("a",true),("b",true),("c",false)],
[("a",true),("b",false),("c",true)],[("a",true),("b",false),("c",false)],
[("a",false),("b",true),("c",true)],[("a",false),("b",true),("c",false)],
[("a",false),("b",false),("c",true)],[("a",false),("b",false),("c",false)]]
: (string * bool) list list

Related

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

How to extract tuple from a datatype?

New to SML and trying to learn through a series of exercises. The function I am trying to write deals with flattening a tree with N children. My approach was to simply take the current NTreeNode and add its value to some list that I would return. Then take its second argument, the list of children, and tack that on to another list, which would be my queue. This queue would serve as all the items I still have left to process.
I tried to do this approach by passing the NTreeList and the list I would return with the initial value in flattenNTree, to a helper function.
However, when I try to process an NTreeNode from my queue it gives me back an NTree and I can't use my first/second functions on that, I need a tuple back from the queue. I just don't understand how to get back a tuple, I tried to use the NTreeNode constructor, but even that's giving me an NTree back.
My question is how can I extract a tuple from the NTree datatype I have defined.
datatype NTree =
NTreeNode of int * NTree list
| EmptyNTree
;
fun first (a, _) = a;
fun second (_, b) = b;
fun processTree queue finalList =
if null queue
then finalList
else processTree ((tl queue)#(second(NTreeNode(hd queue)))) finalList#[first (NTreeNode (hd queue)) ]
;
fun flattenNTree EmptyNTree = []
| flattenNTree (NTreeNode x) = processTree (second x) [(first x)]
;
An example input value:
val t =
NTreeNode (1, [
NTreeNode (2, [
NTreeNode (3, [EmptyNTree]),
NTreeNode (4, []),
NTreeNode (5, [EmptyNTree]),
EmptyNTree
]),
NTreeNode (6, [
NTreeNode (7, [EmptyNTree])
])
]);
It's much easier to take things apart with pattern matching than fiddling around with selectors like first or tl.
It's also more efficient to accumulate a list in reverse and fix that when you're finished than to repeatedly append to the end of it.
fun processTree [] final = reverse final
| processTree (EmptyTree::ts) final = processTree ts final
| processTree ((NTreeNode (v,t))::ts) final = processTree (ts # t) (v :: final)
Your processTree function is missing the case for EmptyNTree and you seem to be trying to add NTree constructors before calling first and second, whereas you need rather to strip them away, as you do in flattenNTree.
Both problems can be fixed by applying pattern matching to the head of the queue:
fun processTree queue finalList =
if null queue
then finalList
else case hd queue of
EmptyNTree => processTree (tl queue) finalList
| NTreeNode v => processTree (tl queue # second v) (finalList # [first v])
;
You might also consider an implementation based on list functionals (although the order of the result is not the same):
fun flattenNTree t = case t of
EmptyNTree => []
| NTreeNode (n, nts) => n :: (List.concat (List.map flattenNTree nts));
Given the tree type
datatype 'a tree = Node of 'a * 'a tree list
| Leaf
you can fold it:
fun fold f e0 Leaf = e0
| fold f e0 (Node (x, ts)) =
let val e1 = f (x, e0)
in foldl (fn (t, e2) => fold f e2 t) e1 ts
end
and flatten it:
fun flatten t =
fold op:: [] t

Split list into 2 lists of odd & even positions - SML?

I'm required to write a function that takes a list and splits it into 2 lists. The first list will hold elements in odd position and 2nd list hold elements in even position. Here's my attempt which gives me the following warning:
Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
How to improve this?
fun splt (lst: int list) =
let
fun splt2 (lst: int list, count: int, lst1: int list, lst2: int list) =
if null lst
then []
else if (count mod 2 = 0)
then splt2 (tl lst, count+1, hd lst::lst1, lst2)
else splt2 (tl lst, count+1, lst1, hd lst::lst2)
in
splt2 (lst,1,[],[])
end
Here's a 2nd correct implementation that I found but I'm mainly interested in fixing the 1st one!!
I want to split a list into a tupple of odd and even elements
fun split [] = ([], [])
| split [x] = ([x], [])
| split (x1::x2::xs) =
let
val (ys, zs) = split xs
in
((x1::ys), (x2::zs))
end;
UPDATE: Improvement is just replace
if null lst then
[]
with this:
if null lst then
[lst1]#[lst2]
Here's some feedback for your code:
Give the function a proper name, like split or partition. The connotations that I have for those names are: Splitting (or exploding) takes something and returns one list of sub-components (e.g. string → char list), while partitioning takes a list of something and divides into two based on a predicate (e.g. List.partition), but they're not really set in stone.
Make up some variable names that aren't lst, since this is just an abbreviation of the type - surely redundant when even the type is there. For generic methods, good names can be hard to come by. A lot of ML code uses stuff like xs to imply a generic, plural form.
Ditch the type annotations; you'll get a polymorphic function that reads more easily:
fun split input =
let
fun split' (xys, count, xs, ys) = ...
in
split' (input, 1, [], [])
end
But really, the version you found online has some advantages: Pattern matching ensures that your lists have the right form before the function body is triggered, which minimizes run-time bugs. The functions hd and tl don't.
You could optimize the order of the cases slightly; i.e. list the most common case first. The parenthesis around x::xs and y::ys is unnecessary. Also, the two other cases (of one or zero elements) can be combined for brevity, but it doesn't matter much.
fun split (x1::x2::xs) =
let
val (ys, zs) = split xs
in
(x1::ys, x2::zs)
end
| split rest = (rest, [])
You could also use case-of instead of let-in-end:
fun split (x1::x2::xs) =
(case split xs of
(ys, zs) => (x1::ys, x2::zs))
| split rest = (rest, [])
Lastly, you may want to make this function tail-recursive:
fun split xys =
let fun split' (x1::x2::xs, ys, zs) = split' (xs, x1::ys, x2::zs)
| split' (rest, ys, zs) = (rev (rest # ys), rev zs)
in
split' (xys, [], [])
end
To help get you over the error you are encountering
you need to look at the type of the function which you have given
val splt = fn : int list -> 'a list
and ask yourself what does an 'a list hold?
- val foo = "foo"::(splt[1,2,3,4,5]);
val foo = ["foo"] : string list
- val bar = 52::splt[1,2,3,4,5];
val bar = [52] : int list
it can hold anything, but the compiler cannot tell by itself.

Filtering elements of one list by looking at boolean values from the second list

I have two lists of equal length. I want to filter the elements of the first list by looking, if the element, with the same index in the second list, has a true boolean value.
Example:
[1,2,3,4,5]:int list
[true,false,false,true,false]:bool list
Expected result: [1,4]
I know two ways I could achieve this:
1) Write a function that takes two lists. For every element in the first list, that I want to append, check if the current(head) element of the second list is true.
2) Zip the two lists and filter it according to the boolean value.
There should be an easier to go about this, right?
Not really. The cleanest way to do this is probably
List.map (fn (x,y) => x) (List.filter (fn (x,y) => y) (ListPair.zip (L1,L2)))
or
List.map Option.valOf (List.filter Option.isSome (ListPair.map(fn (x,y) => if y then SOME x else NONE) (L1,L2)))
The recursive function isn't too bad, either:
fun foo ([],[]) = []
| foo ([],L) = raise Fail "Different lengths"
| foo (L,[]) = raise Fail "Different lengths"
| foo (x::xs, b::bs) = if b then x::foo(xs,bs) else foo(xs,bs)
Those are pretty much the two options you have; either recurse two lists at once, or combine them into one list of tuples and recurse that. There are several combinators you could use to achieve the latter.
val foo = [1,2,3,4,5];
val bar = [true,false,true,true,false];
val pairs = ListPair.zip (foo, bar)
Once zipped, here are two other ways you can do it:
val result = List.foldr (fn ((n,b), res) => if b then n::res else res) [] pairs
val result = List.mapPartial (fn (n,b) => if b then SOME n else NONE) pairs
The simplest is probably
ListPair.foldr (fn (x,y,z) => if y then x :: z else z) [] (L1, L2)
Don't know if ML has list comprehension, but if your language has it:
[ x | (x, True) <- zip xs ys ]

Standard ML - Return the index of the occurrences of a given value in a list

I'm trying to figure out how to return a list of the indexes of occurrences of a specific value in another list.
i.e.
indexes(1, [1,2,1,1,2,2,1]);
val it = [1,3,4,7] int list
I'm trying to figure out how lists work and trying to get better at recursion so I don't want to use List.nth (or any library functions) and I don't want to move into pattern matching quiet yet.
This is what I have so far
fun index(x, L) =
if null L then 0
else if x=hd(L) then
1
else
1 + index(x,tl L);
fun inde(x, L) =
if null L then []
else if x=hd(L) then
index(x, tl L) :: inde(x, tl L)
else
inde(x, tl L);
index(4, [4,2,1,3,1,1]);
inde(1,[1,2,1,1,2,2,1]);
This gives me something like [2, 1, 3, 0]. I guess I'm just having a hard time incrementing things properly to get the index. The index function itself works correctly though.
Instead you could also make two passes over the list: first add an index to each element in the list, and second grap the index of the right elements:
fun addIndex (xs, i) =
if null xs then []
else (hd xs, i) :: addIndex(tl xs, i+1)
fun fst (x,y) = x
fun snd (x,y) = y
fun indexi(n, xs) =
if fst(hd xs) = n then ... :: indexi(n, tl xs)
else indexi(n, tl xs)
(I left out part of indexi for the exercise.)
Where addIndex([10,20,30],0) gives you [(10,0),(20,1),(30,2)]. Now you can use addIndex and indexi to implement your original index function:
fun index(n, xs) = indexi(n, addIndex(xs, 0))
When you get that to work, you can try to merge addIndex and indexi into one function that does both.
However, you really want to write this with pattern matching, see for instance addIndex written using patterns:
fun addIndex ([], _) = []
| addIndex (x::xs, i) = (x,i) :: addIndex(xs, i+1)
If you do index(1,[2]), it gives 1, which is not correct. When the list is empty, it gives you zero. In a function like this, you'd probably want to use SOME/NONE feature.

Resources