insertSorted comparison function in SML NJ - sml

I've posted a similar question to this before, but I think I need to rephrase what I am asking about. I got some great help earlier with what I want to accomplish.
So, what I am currently wondering right now is how can I pass down the comp function in my code so that it can be customizable.
I would like to be able to run
insertSorted(5, fn(a, b) => a > b, [8, 6, 3, 1];
Which would return
val it = [8, 6, 5, 3, 1]
While also being able to flip a sign and be able to run
insertSorted(5, fn(a, b) => a < b, [8, 6, 3, 1];
Which would return
val it = [1, 3, 5, 6, 8]
Heres What I have so far:
* insertSorted *
fun insertSorted (x, comp, nil) = [x]
| insertSorted (x, comp, y::ys) = if x comp y then y::x::ys else y :: insertSorted (x,ys);
This is the "comp" that is in question:
Line 2: if x comp y then y::x::ys else y :: insertSorted (x,ys);

In my mind, insertSorted could be two things: Either (a) a function that assumes that its input list is sorted with the same relation as the one given, so that insertion is O(n), or (b) a function that first sorts the input list according to the relation, and then inserts the element.
I will argue that (a) makes sense, even though it is a little fragile, and that (b) is a little bad:
If you are allowed to assume that your input is already sorted, your function is O(n) rather than O(n log n). You could call this data structure a pre-sorted list. Now, nothing prevents you from feeding an unsorted list to insertSorted, so that could result in bugs. There are ways you can overcome this. But that is another topic.
If that's what you want, then this is how to do that:
fun insertSorted (x, comp, y::ys) =
if comp (x, y)
then y :: insertSorted (x, comp, ys)
else x :: y :: ys
| insertSorted (x, _, []) = [x]
Testing this:
- insertSorted (5, op <, [8,7,6,4,3,2]);
val it = [8,7,6,5,4,3,2] : int list
- insertSorted (5, op >, [2,3,4,6,7,8]);
val it = [2,3,4,5,6,7,8] : int list
If you do not assume that your input is already sorted, then insertSorted is a really bad name: We're not inserting into something that is sorted. Additionally, since we need to sort the entire input anyway, being O(n log n), there is no real point in then inserting for an additional O(n), unless we're allowed to assume that the input is "nearly" sorted, in which case some sorting algorithms are better than others.
Assuming that "unsorted" is the opposite of "sorted", and since you use SML/NJ which comes with a sorting function, what you can do is:
fun insertUnsorted (x, comp, ys) =
ListMergeSort.sort comp (x::ys)
Testing this:
- insertUnsorted (5, op <, [1,9,3,4,6,2]);
val it = [9,6,5,4,3,2,1] : int list
- insertUnsorted (5, op >, [1,9,3,4,6,2]);
val it = [1,2,3,4,5,6,9] : int list
I don't think this function is very useful compared to sort.
For sorting functions in other SML compilers than SML/NJ, see this answer.

Related

Currying and summation of two lists of varying size

I'm self-learning SML and am currently am stuck with the concept of recursion between two lists of varying sizes.
Suppose you have two int lists of varying size, and a function that multiplies two numbers, like so:
val mul = fn(a, b) => a * b;
I want to use this function to be passed as a parameter into another function, which multiplies the numbers in the same index recursively until at least one of the lists is empty. So
val list1 = [1, 3, 5, 7];
val list2 = [2, 6, 3];
would be passed through that same function with mul and 35 would be returned, as 1*2 + 3*6 + 5*3 would be calculated.
My knowledge of how SML works is a bit limited, as I'm not exactly sure how to carry the result of the sum forward during the recursion, nor how to handle the base case when one of either lists terminates early. Could someone point me in the right direction in thinking of this problem?
You can use pattern-matching and recursion to operate over two lists simultaneously. You then need an accumulator to pass the sum along.
fun mulAndSum acc ([], []) = ...
| mulAndSum acc ([], _) = ...
| mulAndSum acc (_, []) = ...
| mulAndSum acc ((x::xs), (y::ys)) = mulAndSum (...) (xs, ys)
Then when you call the function, you provide zero as the initial state of the accumulator.
mulAndSum 0 ([1, 3, 5, 7], [2, 4, 6])
To add to Chris' answer, recursion over two lists at once can also be achieved with map and zip which are higher-order list combinators (i.e. functions that take another function as argument and operate on lists):
fun add (x, y) = x + y
fun mul (x, y) = x * y
fun sum xs = foldl add 0 xs
val zip = ListPair.zip
fun mulAndSum xs ys = sum (map mul (zip xs ys))
zip will also throw away elements if one of its input lists is longer than the other.

Custom comparator sort in SML?

I am a bit stuck with this problem in SML / SMLNJ and I would love some guidance.
So I have a problem where I need to make a function called insertSorted, where it takes a number, a comparison statement, and an (assumed sorted) list that it needs to insert into. I'm not sure how to start approaching this so any help would be amazing.
My thought is to split the two lists up where the number would be, insert the number, and then concatenate both lists.
fun insertSorted (x, comp, []) = [x]
| insertSorted (x, comp, a::rest) = ...
Update: I got a bit farther now I just need to know how to debug this, any guidance?
fun insertSorted (x, []) = [x]
| insertSorted (x, y::ys) =
if (x < y)
then x::y::ys
else if (x > y)
then y::x::ys
else y::insertSorted (x, ys);
Update 2: My new goal is to figure out how to merge these two functions into one. Ultimately named insertSorted.
fun insertSorted (x, nil) = [x]
| insertSorted (x,y::ys) = if x<y then x::y::ys else y :: insertSorted (x,ys);
fun insertSorted (x, nil) = [x]
| insertSorted (x,y::ys) = if x>y then y::x::ys else y :: insertSorted (x,ys);
There are three cases:
The list is nil.
You've already covered this. :-)
The list is not nil, and its first element is less than x, so we need to keep searching for where to insert x.
In this case, the result should be the first element, followed by the result of inserting x into the rest of the list.
The list is not nil, and its first element is greater than or equal to x, so we can insert x right here.
In this case, the result should be x, followed by the entire list.
Distinguishing cases #2 and #3 involves if/then/else; implementing case #2 involves recursion.

Creating an intersection and difference in ML New Jersey

Using the function contains constructed earlier, write a function intersection that takes two list (modeling sets) and returns a list that comes up with the intersection of two sets. So
intersection([1, 2, 3], [1, 3])
would return [1, 3].
Using the function contains constructed earlier, write a function difference which takes two list and returns a list modeling the difference of the first set from the second set (Set A – Set B).
I've created this code contains which is down below, now my goal is to create both an intersection and difference function.
fun contains(x, []) = false
| contains(x, y::rest) =
if x = y
then true
else contains(x, rest);
fun intersection([], y) = []
| intersection(x, y) = if x = y
then [x,y]
else [];;
Trying it:
- intersection([1, 2], [2, 3]);
val it = [] : int list list
As for your contains function, it can be improved slightly:
fun contains(x, []) = false
| contains(x, y::rest) =
x = y orelse contains(x, rest)
That is, if P then true else Q is the same as P orelse Q.
The higher-order standard-library solution is to write
fun contains (x, ys) = List.exists (fn y => x = y) ys
But the former is preferrable if the exercise is to demonstrate understanding of basic recursion.
As for your intersection function, it seems that however you managed to make contains, you are not applying the same principles of list recursion. You compare x = y, but here x and y are lists, not individual elements. Whereas in contains, x is a single value within a list/set, and y is the first element of the list/set y::rest.
So you should probably start to either annotate each argument with a type, or name it in such a way that you're not in doubt about what it's supposed to represent. For intersection the rule is that you only want elements that are members of both lists/sets.
For example:
fun intersection (xs, ys) = ...
Or with types annotated:
fun intersection (xs : ''a list, ys : ''a list) = ...
And you may think that you need to use recursion on lists by pattern matching on the empty/non-empty lists on either xs or ys. I've picked xs here because it happened to be the first argument, but this is arbitrary:
fun intersection ([], ys) = ...
| intersection (x::xs, ys) = ...
Or with types annotated:
fun intersection ([] : ''a list, ys : ''a list) = ...
| intersection (x::xs : ''a list, ys : ''a list) = ...
Then you can ask yourself:
What is the intersection between the empty list/set and ys?
Is x a part of the intersection of x::xs and ys?
What other elements might be part of this intersection (recursively)?

replacing an element in a list of lists in haskell

I am trying to write a function like this:
updateMatrix:: [[a]] -> a -> (x, y) ->[[a]]
This is supposed to take in a list of lists such as:
[ [1, 2, 3, 4],
[5, 6, 7, 8]]
and put the given element at the specified coordinates, so, given:
[ [1, 2, 3, 4],
[5, 6, 7, 8]] 9 (0, 1)
it should return
[ [1, 9, 3, 4],
[5, 6, 7, 8]]
I can't figure out how to do this without having to rebuild the whole matrix, please help!
You need to rebuild the matrix every time. So as long as you don't need high performance computing, you could use this legible implementation:
replace :: (a -> a) -> Int -> [a] -> [a]
replace f 0 (x:xs) = (f x):xs
replace f i (x:xs) = x : replace f (i-1) xs
replace f i [] = []
replace2D :: (a -> a) -> (Int, Int) -> [[a]] -> [[a]]
replace2D f (x,y) = replace (replace f y) x
Your function would be:
updateMatrix ll x c = replace2D (const x) c ll
Here's an implementation:
updateMatrix :: [[a]] -> a -> (Int, Int) -> [[a]]
updateMatrix m x (r,c) =
take r m ++
[take c (m !! r) ++ [x] ++ drop (c + 1) (m !! r)] ++
drop (r + 1) m
But maybe this "rebuilds the whole matrix" as you say? Note that
lists are not mutable in Haskell, so you can't destructively update
one entry, if that's what you would mean by not "rebuilding the whole
matrix".
Here’s a short one:
replace p f xs = [ if i == p then f x else x | (x, i) <- zip xs [0..] ]
replace2D v (x,y) = replace y (replace x (const v))
Now you can use it exactly like you wanted:
λ → let m = [[1, 2, 3, 4], [5, 6, 7, 8]]
λ → replace2D 9 (0, 1) m
[[1,2,3,4],[9,6,7,8]]
As others already said,
This approach is of course rather slow, and only makes sense if the structure is more complex than the lists are long. There’s easy documentation about the internal structure and complexity of things in Haskell out there.
Think of m as a pointer to a linked list of pointers, and you can see why it’s slower than a pure stream of bytes. There are better libs that use something closer to the latter.
Haskell’s values are immutable because there are no side-effects. Which is good for reliability. So you can’t change m. You can only build something out of m.
Haskell can simulate mutable references, with the help of monads. Like IORef. But using it for this would be rather wrong. There are many other questions here on Stack Overflow, explaining its usage, pros and cons.
Being a purely functional language, Haskell requires you to return a "brand new" matrix when you update an item, so you need to rebuild the whole matrix indeed (if you're actually interested in matrix processing, cast a look at matrix library rather than implementing your own).
Beware, lists are not a good choice for such manipulations, but if you do it for educational purposes, start with implementing a function that "replaces" an element in [a], then use it twice (function composition can help there) in order to get your updateMatrix function. Here is an answer that can help you on your way.

Prolog: How to remove every second element of a list

I need to write a program in Prolog that should remove every second element of a list. Should work this: [1,2,3,4,5,6,7] -> [1,3,5,7]
so far I have this, but it just returns "false".
r([], []).
r([H|[T1|T]], R) :- del(T1,[H[T1|T]], R), r(R).
del(X,[X|L],L).
del(X,[Y|L],[Y|L1]):- del(X,L,L1).
This is pretty much Landei's answer in specific Prolog syntax:
r([], []).
r([X], [X]).
r([X,_|Xs], [X|Ys]) :- r(Xs, Ys).
The second predicate is not required.
Alternative solution using foldl/4:
fold_step(Item, true:[Item|Tail], false:Tail).
fold_step(_Item, false:Tail, true:Tail).
odd(List, Odd) :-
foldl(fold_step, List, true:Odd, _:[]).
Usage:
?- odd([1, 2, 3, 4, 5, 6, 7], Odd).
Odd = [1, 3, 5, 7]
The idea is to go through the list, while keeping "odd/even" flag and flipping its value (false -> true, true -> false) on each element. We also gradually construct the list, by appending those elements which have "odd/even" flag equal to true, and skipping others.
This fine answer by #code_x386 utilizes difference-lists and foldl/4.
Let's use only one fold_step/3 clause and make the relation more general, like so:
fold_step(X, [X|Xs]+Ys, Ys+Xs).
list_odds_evens(List, Odds, Evens) :-
foldl(fold_step, List, Odds+Evens, []+[]).
Sample queries:
?– list_odds_evens([a,b,c,d,e,f], Odds, Evens).
Evens = [b,d,f], Odds = [a,c,e]
?– list_odds_evens([a,b,c,d,e,f,g], Odds, Evens).
Evens = [b,d,f], Odds = [a,c,e,g]
Edit
Why not use one clause less and do away with predicate fold_step/3?
lambda to the rescue!
:- use_module(library(lambda)).
list_odds_evens(List, Odds, Evens) :-
foldl(\X^([X|Xs]+Ys)^(Ys+Xs)^true, List, Odds+Evens, []+[]).
Another possibility is to use DCGs, they are usually a worthwhile consideration when describing lists:
list_oddindices(L,O) :-
phrase(oddindices(L),O). % the list O is described by oddindices//1
oddindices([]) --> % if L is empty
[]. % O is empty as well
oddindices([X]) --> % if L has just one element
[X]. % it's in O
oddindices([O,_E|OEs]) --> % if L's head consists of at least two elements
[O], % the first is in O
oddindices(OEs). % the same holds for the tail
This is certainly less elegant than the solutions using foldl/4 but the code is very easily readable, yet it solves the task described by the OP and works both ways as well:
?- list_oddindices([1,2,3,4,5,6,7],O).
O = [1, 3, 5, 7] ;
false.
?- list_oddindices(L,[1,3,5,7]).
L = [1, _G4412, 3, _G4418, 5, _G4424, 7] ;
L = [1, _G4412, 3, _G4418, 5, _G4424, 7, _G4430] ;
false.
I have no Prolog here to try it out, and I got a little bit rusty, but it should be along the lines of
r([]) :- [].
r([X]) :- [X].
r([X,Y|Z]) :- R=r(Z),[X|R].
[Edit]
Of course pad is right. My solution would work in functional languages like Haskell or Erlang:
--Haskell
r [] = []
r [x] = [x]
r (x:_:xs) = x : (r xs)
In Prolog you have to "pull" the right sides into the argument list in order to trigger unification.
I just needed a function like this and took a more "mathematical" approach:
odds(Xs, Ys) :- findall(X, (nth1(I,Xs,X), I mod 2 =:= 1), Ys).
It doesn't work both ways like some of the other fine answers here, but it's short and sweet.