picks :: [a] -> [(a, [a])]
picks [] = []
picks (x:xs) = (x,xs) : [(y,x:ys)| (y,ys) <- picks xs]
picks [1..4] = [(1,[2,3,4]),(2,[1,3,4]),(3,[1,2,4]),(4,[1,2,3])]
This Haskell function works like a charm, but why? The first two tuples in the list are obvious enough, but how is the rest build up, just cracks my brain.
What does picks do? It returns all possible ways to choose one element from the list, in the form of a tuple (choice, rest), where choice is the item you chose and rest is the elements you did not choose. Note that [choice] ++ rest always contains the same elements as the original list, though not necessarily in the same order.
So how does picks work? When the argument is empty, it's simple: there are no ways to choose one element, so we return the empty list.
picks [] = []
When the argument is not empty, we can do one of two things. Either x is the first element of the tuple, or it is part of the second element. The easy thing to do is pick the first element; we unpack the list with (x:xs) and produce (x, xs).
picks (x:xs) = (x, xs) : ?
The other thing we can do is not pick x, but instead pick an element from xs. How do choose an element from xs? We use picks! This time, picks returns a list of tuples of where x is neither the first element nor a member of the second element. We just combine (x, xs) with this list.
-- x != y, x `elem` ys == False
picks (x:xs) = (x, xs) : [ (y, ?) | (y, ys) <- picks xs]
But x does need to be an member of the second element, because it's not the first element. So we have to put it back. The easiest place to put it is at the beginning of ys in each case:
picks (x:xs) = (x, xs) : [ (y, x:ys) | (y, ys) <- picks xs]
In many cases it helps to expand an expression manually:
picks [1..4] = (1, [2..4]) : [(y, 1:ys) | (y, ys) <- picks [2..4]]
-- continuing with picks [2..4]
picks [2..4] = (2, [3..4]) : [(y, 2:ys) | (y, ys) <- picks [3..4]]
-- continuing with picks [3..4]
picks [3, 4] = (3, [4]) : [(y, 3:ys) | (y, ys) <- picks [4]]
-- continuing with picks [4]
picks [4] = (4, []) : [(y, 4:ys) | (y, ys) <- picks []]
= (4, []) : [(y, 4:ys) | (y, ys) <- []]
= (4, []) : []
= [(4, [])]
picks [3, 4] = (3, [4]) : [(y, 3:ys) | (y, ys) <- [(4, [])]]
= (3, [4]) : [(4, 3:[])]
= (3, [4]) : [(4, [3])]
= [(3, [4]), (4, [3])]
-- and so one
The recursive case will return a list where the first item (x, xs) return a 2-tuple with as first item (the item we have picked) x, and with as remaining items the picks we make on the tail of the list and prepend all these items with x.
If we run this on a singleton list, for example [1], we thus get as options:
picks [1] = (1, []) : [(y, 1: ys) | (y,ys) <- picks []]
since picks [] is equivalent to [], this thus means that we retrieve:
picks [1] = (1, []) : [(y, 1: ys) | (y,ys) <- []]
hence the list comprehension will generate an empty list, and thus the result of picks [1] is:
picks [1] = [(1, [])]
If we now work with lists with two elements, the recursive call will return a list with one element: the only element of the tail and an empty list.
This thus means that if we run picks on picks [1,2], we thus get:
picks [1, 2] = (1, [2]) : [(y, 1:ys) | (y,ys) <- picks [2]]
and since picks [2] returns [(2, [])], we thus will prepend the empty list in the (2, []) tuple with one, and we thus obtain:
picks [1, 2] = (1, [2]) : [(y, 1:ys) | (y,ys) <- [(2, [])]]
= (1, [2]) : [(2, [1])]
= [(1, [2]), (2, [1])]
The x:ys part in the list comprehension will prepend the head of the list, so 1 to the lists returned by picks [2]. Since is not picked by the recursive call (we call picks recursively on the tail of the item), we thus need to insert it somewhere in the list, and the most easiest way is to prepend it.
If we thus work with three items, we will retrieve the data as:
picks [1, 2, 3] = (1, [2, 3]) : [(y, 1:ys) | (y,ys) <- picks [2, 3]]
= (1, [2]) : [(y, 1:ys) | (y,ys) <- [(2, [3]), (3, [2])]]
= (1, [2]) : [(2, [1, 3]), (3, [1, 2])]
= [(1, [2]), (2, [1, 3]), (3, [1, 2])]
and so for lists with more than three items, the recursion each time will prepend the item that is definitely not picked by the recursive tail of picks xs, and prepend the lists of non-picked items with x.
This is a case where the formulation of the algorithm using higher order functions actually looks clearer than with a list comprehensions--based one:
picks [] = []
picks (x:xs) = -- (x,xs) : [(y, x:ys) | (y,ys) <- picks xs]
(x,xs) : map (second (x:)) (picks xs)
In case you don't understand what second (x:) is, you can read it as a pseudocode: it applies (x:) to the second part of a pair, so that second (x:) (a,b) = (a, x:b). And map does so for every element in its argument list (the map's second argument).
Thus we have, building our understanding from the ground up, stating with one-element lists, then two elements, three, and so on, to see the pattern:
picks ([1]) = picks (1:[]) =
-- picks (x:xs) --
= (x,xs) : map (second (x:)) (picks xs)
= (1,[]) : map (second (1:)) (picks [])
= (1,[]) : map (second (1:)) []
= (1,[]) : []
= [(1,[])]
picks [2,1] = picks (2:[1]) =
= (2,[1]) : map (second (2:)) (picks [1])
= (2,[1]) : map (second (2:)) [(1, [])]
= (2,[1]) : [(1,[2])]
= [(2,[1]) , (1,[2])]
picks [3,2,1] =
= (3,[2,1]) : map (second (3:)) (picks [2,1])
= (3,[2,1]) : map (second (3:)) [(2, [1]) , (1, [2])]
= [(3,[2,1]) , (2,[3,1]) , (1,[3,2])]
picks [4,3,2,1] =
= (4,[3,2,1]) : map (second (4:)) [(3,[2,1]) , (2,[3,1]) , (1,[3,2])]
= [(4,[3,2,1]) , (3,[4,2,1]) , (2,[4,3,1]) , (1,[4,3,2])]
picks [5,4,3,2,1] =
= [([5,[4,3,2,1]), (4,[5,3,2,1]), (3,[5,4,2,1]), (2,[5,4,3,1]), (1,[5,4,3,2])]
....
Putting them together to better see the pattern, the results are:
picks [ ] = [ ]
picks [ 1] = [ (1,[ ])]
picks [ 2,1] = [ (2,[ 1]) , (1,[ 2])]
picks [ 3,2,1] = [ (3,[ 2,1]) , (2,[ 3,1]) , (1,[ 3,2])]
picks [ 4,3,2,1] = [ (4,[ 3,2,1]) , (3,[ 4,2,1]) , (2,[ 4,3,1]) , (1,[ 4,3,2])]
picks [5,4,3,2,1] =
= [([5,[4,3,2,1]) , (4,[5,3,2,1]) , (3,[5,4,2,1]) , (2,[5,4,3,1]) , (1,[5,4,3,2])]
....
And so picks produces all the ways to pick an element, pairing it up with the remaining elements in the list after the element is removed from it.
It evidently does so for the length 0 (empty) list case, [], and the length 1 (singleton) case [] and the length 2 case [2,1] as seen above; and if it does so for a list of length n, then for the n+1 we know that it's right as well since it starts with the first pick, and then the map adds the first element into each of the remainders in the result produced for the n case. Which is correct.
Yes, you can read this as both "the n case is correct" and "hence, n+1 is correct". Thus (and given the correctness of the 0 case) by the induction principle the results are correct for any n. That is to say, for all of them. Yes there are infinitely many of them but each n in itself is finite.
If the starting point is right, and each step is right, then the whole journey must be right as well. We don't need to understand how exactly it does what it does for an n case, unrolling all the layers of recursion. That's hard. Instead, we prove the inductive step is right, the base case is right, and that's that.
Recursion is a leap of faith.
The three most important rules in trying to understand how does a recursive function exactly do what it does, are:
The first rule is, we do not talk about how does a recursive function exactly do what it does.
The second rule is, we do not talk about how does a recursive function exactly do what it does.
The third rule is, we do not talk about how does a recursive function exactly do what it does.
Of course this version of picks is not too good. It is quadratic, and it destroys information.
We can address both flaws at once with
-- unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
picks3 :: [a] -> [([a], a, [a])]
picks3 xs = unfoldr (\case { (_,[]) -> Nothing ;
(a,x:xs) -> Just ((a,x,xs), (x:a,xs)) })
([],xs)
So that
> picks3 [1..4]
[([],1,[2,3,4]),([1],2,[3,4]),([2,1],3,[4]),([3,2,1],4,[])]
Now this is linear, and it is easy to produce the output of picks from it, if we so choose and are willing to pay the price.
Without brain - cracking, simple to explain (but Eq a =>), has approximately 1.7 slower execution time trade off
mypicks :: Eq a => [a] -> [(a,[a])]
mypicks [] = []
mypicks lst = [ zsplit x lst | x <- lst]
zsplit :: Eq a => a -> [a] -> (a,[a])
zsplit z lst = esplit z lst []
where
esplit :: Eq a => a -> [a] -> [a] -> (a, [a])
esplit e [] lr = (e, lr)
esplit e (x:xs) lr
| e == x = (e, reverse lr ++ xs)
| otherwise = esplit e xs (x:lr)
--- using
perms2 [] = [[]]
perms2 xs = [x:zs | (x,ys) <- picks xs, zs <- perms2 ys]
perms2' [] = [[]]
perms2' xs = [x:zs | (x,ys) <- mypicks xs, zs <- perms2' ys]
--- and
T.measureTime $ length $ perms2 [1..9]
T.measureTime $ length $ perms2' [1..9]
--- delivers:
e: 362880
Computation time: 0.075562000 sec.
e: 362880
Computation time: 0.125598000 sec.
I'm trying to write a function in SML that takes in a list of ints and will output a list of ordered pairs of ints. The ordered pairs first int is the int that occurred in the input list and the second int in the ordered pair is the number of times it occurred in the input list. Also the list returned should be in ascending order according to the first int in the ordered pairs.
For example input list [1, 1, 1, 2, 3, 3, 5] would output as [(1,3), (2, 1), (3, 2), (5, 1)].
So far I have a function that uses foldl
UPDATED the code since original post.
fun turnIntoPairs l = foldl (fn (e, a) => if List.exists (fn (x, _) => x = e) a then x + 1 else a # [(e, 1)]) [] l;
I'm having trouble updating the list where I find the ordered pair that is already in the list - I want to add one to the second int in the ordered pair that was found while it's still in the list.
Any help would be greatly appreciated!
C:\Program Files (x86)\SMLNJ\\bin\.run\run.x86-win32.exe: Fatal error -- Uncaught exception Error with 0
raised at ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
[autoloading done]
C:\Users\Localadmin\Desktop\CS 671\Program 3\commonFactors.sml:1.87 Error: unbound variable or constructor: x
C:\Users\Localadmin\Desktop\CS 671\Program 3\commonFactors.sml:1.44-1.110 Error: types of if branches do not agree [literal]
then branch: int
else branch: (''Z * int) list
in expression:
if (List.exists (fn <pat> => <exp>)) a
then <errorvar> + 1
else a # (e,1) :: nil
[Finished in 0.5s with exit code 1]
Not really sure how to fix your current program, but you can solve this problem by splitting it in two: grouping equal elements and then ordering the list.
(* Groups successive equal elements into a tuples (value, count) *)
fun group (l as (x :: _)) =
let val (firstGroup, rest) = List.partition (fn y => x = y) l
in
(x, List.length firstGroup) :: (group rest)
end
| group [] = []
(* Now that we have our elements grouped, what's left is to order
them as required. *)
fun turnIntoPairs xs =
ListMergeSort.sort (fn ((x, _), (y, _)) => x >= y) (group xs)
Let's just look at the function you're passing to foldl:
(fn (e, a) => if List.exists (fn (x, _) => x = e) a then x + 1 else a # [(e, 1)])
The first problem (which the type-checker is complaining about) is that your if expression returns either x + 1, or a # [(e, 1)], which seems problematic on account of the former being a value of type int and the latter being of type (int * int) list.
Let's rewrite your code using some helper functions that I won't define and see if it gets clearer:
(fn (e, a) => if List.exists (fn (x, _) => x = e) a then increment a e else a # [(e, 1)])
Where increment has the type (int * int) list -> int -> (int * int) list.
Can you implement increment?
Like Gian, I would prefer to divide this into two functions: One that folds and one helper function that inserts. Incidentally, the insert function would take an element and an existing (int * int) list just as the accumulator function that fold accepts these two arguments.
Normally I would write an insert function curried (i.e. insert x xs) but if I write it uncurried (i.e. insert (x, xs)), I can pass it directly to foldl:
fun insert (x, []) = [(x,1)]
| insert (x, ((y,c)::xs)) =
if x = y then (y,c+1)::xs else (y,c)::insert (x, xs)
fun turnIntoPairs xs = foldl insert [] xs
We want to find the largest value in a given nonempty list of
integers. Then we have to compare elements in the list. Since data
values are given as a sequence, we can do comparisons from the
beginning or from the end of the list. Define in both ways. a)
comparison from the beginning b) comparison from the end (How can
we do this when data values are in a list?)
What I have done is find the largest number by comparison from the beginning.
How can I do it from the end? What logic should I apply?
Here is my code for comparing from the beginning.
- fun largest[x] = x
= | largest(x::y::xs) =
= if x>y then largest(x::xs) else largest(y::xs)
= | largest[] = 0;
val largest = fn : int list -> int
output
- largest [1,4,2,3,6,5,4,6,7];
val it = 7 : int
In your function, first two elements of the list are compared and the bigger value is compared to the remaining elements. I think comparison from the end means that you try to find the largest number of the tail of the list first and compare it with the head element later.
fun largest [] = raise Empty
| largest [x] = x
| largest (x::xs) =
let
val y = largest xs
in
if x > y then x else y
end
Although it is not required, you should handle the case of empty list for completeness. And you can shorten the function if using max function.
fun largest [] = raise Empty
| largest [x] = x
| largest (x::xs) = max(x, largest xs)
To be honest, I would prefer your version which is tail-recursive (it doesn't blow the stack on big lists). My function could be rewritten to be tail-recursive as other answers demonstrated, but certainly it is more sophisticated than your function.
As #pad demonstrates with his code, the logic that should be applied is making a recursive call that solves the sub-problem recursively before solving the problem of the current scope of the function.
In the case of largest, there is not really any point in solving it backwards, since it simply uses more stack space, which becomes apparent when executing the code "by hand". The design pattern is however useful in other situations. Imagine a function called zip:
(* zip ([1,2,3,4], [a,b,c]) = [(1,a),(2,b),(3,c)] *)
fun zip (x::xs, y::ys) = (x,y) :: zip (xs, ys)
| zip _ = []
This function turns a tuple of two lists into a list of many tuples, discarding left-over values. It may be useful in circumstances, and defining it is not that bad either. Making its counterpart, unzip, is however slightly trickier:
(* unzip ([(1,a),(2,b),(3,c)] = ([1,2,3],[a,b,c]) *)
fun unzip [] = ([], [])
| unzip ((x,y)::xys) =
let
val (xs, ys) = unzip xys
in
(x::xs, y::ys)
end
Running the regular "from the beginning"-largest by hand might look like this:
largest [1,4,2,3,6,5,4,6,7]
~> largest [4,2,3,6,5,4,6,7]
~> largest [4,3,6,5,4,6,7]
~> largest [4,6,5,4,6,7]
~> largest [6,5,4,6,7]
~> largest [6,4,6,7]
~> largest [6,6,7]
~> largest [6,7]
~> largest [7]
~> 7
Running the "from the end"-largest by hand, imagining that every indentation level requires saving the current context of a function call in stack memory, calling a new function and returning the result after the ~> arrow, might look like this:
largest [1,4,2,3,6,5,4,6,7] ~> 7
\_
largest [4,2,3,6,5,4,6,7] ~> 7
\_
largest [2,3,6,5,4,6,7] ~> 7
\_
largest [3,6,5,4,6,7] ~> 7
\_
largest [6,5,4,6,7] ~> 7
\_
largest [5,4,6,7] ~> 7
\_
largest [4,6,7] ~> 7
\_
largest [6,7] ~> 7
\_
largest [7] ~> 7
So why are these non-tail-recursive functions that make early recursive calls useful if they simply use more memory? Well, if we go back to unzip and we want to solve it without this annoying "thinking in reverse", we have a problem constructing the new result, which is a tuple, because we don't have anywhere to put the x and y:
(* Attempt 1 to solve unzip without abusing the call stack *)
fun unzip [] = ([], [])
| unzip ((x,y)::xys) = ...something with x, y and unzip xys...
One idea for making such a place would to create an auxiliary function (helper function) that has a few extra function parameters for building xs and ys. When the end of the xys list is reached, those values are returned:
(* Attempt 2 to solve unzip without abusing the call stack *)
local
fun unzipAux ([], xs, ys) = (xs, ys)
| unzipAux ((x,y)::xys, xs, ys) = unzipAux (xys, x::xs, y::ys)
in
fun unzip xys = unzipAux (xys, [], [])
end
But you might have realized that those (xs, ys) end up reversed in the result. A quick way to fix this is by using rev on them, once only, which is best achieved at the base case:
(* Attempt 3 to solve unzip without abusing the call stack *)
local
fun unzipAux ([], xs, ys) = (rev xs, rev ys)
| unzipAux ((x,y)::xys, xs, ys) = unzipAux (xys, x::xs, y::ys)
in
fun unzip xys = unzipAux (xys, [], [])
end
Which brings forth an interesting question: How is rev implemented?
I suggest using a tail recursive helper, which passes the current maximum as an accumulator.
local
fun helper max [] = max
| helper max (n::ns) = helper (if n > max then n else max) ns
in
fun largest ns = helper 0 ns
end;
(*Find the max between two comparable items*)
fun max(a,b) = if a>b then a else b;
(*Find the max item in list which calls the maxL function recursively*)
fun maxL(L) = if L=[] then 0 else max(hd(L), maxL(tl(L)));
I know it is too late to answer your question, but hopefully this will help:
fun insert (x, []) = [x]
| insert (x, y::ys) = if x<=y then x::y::ys else y::insert(x,ys);
fun insertion_sort [] = []
| insertion_sort (x::xs) = insert(x, insertion_sort xs);
fun get_last_element [] = 0
| get_last_element [x] = x
| get_last_element (x::xs) = if(xs=nil)
then x
else
get_last_element(xs);
fun get_min L = if(insertion_sort(L)=[])
then 0
else
hd(insertion_sort(L));
fun get_max L = get_last_element(insertion_sort(L));
You also can tweak the code e.g. passing anonymous function in insert function ...