Why would using head/tail instead of pattern matching make evaluation terminate? - list

As an exercise, I'm trying to define a ruler value
ruler :: (Num a, Enum a) => [a]
which corresponds to the ruler function
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2...
where the n'th element of the list (assuming the first element corresponds to n=1) is the largest power of 2 which evenly divides n. To make it more interesting, I'm trying to implement ruler without having to do any divisibility testing.
Using a helper function
interleave :: [a] -> [a] -> [a]
which simply alternates the elements from the two given lists, I came up with this - but alas it doesn't work:
interleave :: [a] -> [a] -> [a]
interleave (x:xs) (y:ys) = x : y : interleave xs ys
interleave _ _ = []
ruler :: (Num a, Enum a) => [a]
ruler = foldr1 interleave . map repeat $ [0..]
main :: IO ()
main = print (take 20 ruler)
The program eventually uses up all stack space.
Now, what's strange is that the program works just fine if I adjust the definition of interleave so that it reads
interleave (x:xs) ys = x : head ys : interleave xs (tail ys)
I.e. I no longer use pattern matching on the second argument. Why does using head and tail here make ruler terminate - after all, the pattern matching is rather defensive (I only evaluate the first element of the list spine, no?).

You are applying foldr with an strict combination function to an infinite list.
Boiled down to a minimal example, you can view this behaviour here:
*Main> :t const
const :: a -> b -> a
*Main> :t flip seq
flip seq :: c -> a -> c
*Main> foldr1 const [0..]
0
*Main> foldr1 (flip seq) [0..]
^CInterrupted.
The fix is, as explained in other answers, to make interleave lazy.
More concretely, here is what happens. First we resolve the foldr1, replacing every : of the outer list with interleave:
foldr1 interleave [[0..], [1...], ...]
= interleave [0...] (interleave [1...] (...))
In order to make progress, the first interleave wants to evaluate the second argument before producing the first value. But then the second wants to evaluate its second argument, and so on.
With the lazy definition of interleave, the first value is produced before evaluating the second argument. In particular, interleave [1...] (...) will evaluate to 1 : ... (which helps the first interleave to make progress) before evaluating stuff further down.

The difference is that pattern matching forces the first item in the spine, head/tail do not.
You could use lazy patterns to achieve the same goal:
interleave (x:xs) ~(y:ys) = x : y : interleave xs ys
Note the ~: this is equivalent to defining y and ys using head and tail.
For example: the list below is undefined.
fix (\ (x:xs) -> 1:x:xs)
where fix is the fixed point combinator (e.g. from Data.Function). By comparison, this other list repeats 1 forever:
fix (\ ~(x:xs) -> 1:x:xs)
This is because the 1 is produced before the list is split between x and xs.
Why forcing the first item in the spine triggers the problem?
When reasoning about a recursive equation such as
x = f x
it often helps to regard x as the value "approached" by the sequence of values
undefined
f undefined
f (f undefined)
f (f (f undefined))
...
(The above intuition can be made precise through a bit of denotational semantics and the Kleene's fixed point theorem.)
For instance, the equation
x = 1 : x
defines the "limit" of the sequence
undefined
1 : undefined
1 : 1 : undefined
...
which clearly converges to the repeated ones list.
When using pattern matching to define recursive values, the equation becomes, e.g.
(y:ys) = 1:y:ys
which, due to pattern matching, translates to
x = case x of (y:ys) -> 1:y:ys
Let us consider its approximating sequence
undefined
case undefined of (y:ys) -> .... = undefined
case undefined of (y:ys) -> .... = undefined
...
At the second step, the case diverges, making the result still undefined.
The sequence does not approach the intended "repeated ones" list, but is constantly undefined.
Using lazy patterns, instead
x = case x of ~(y:ys) -> 1:y:ys
we obtain the sequence
undefined
case undefined of ~(y:ys) -> 1:y:ys
= 1 : (case undefined of (y:_) -> y) : (case undefined of (_:ys) -> ys)
= 1 : undefined : undefined -- name this L1
case L1 of ~(y:ys) -> 1:y:ys
= 1 : (case L1 of (y:_) -> y) : (case L1 of (_:ys) -> ys)
= 1 : 1 : undefined : undefined -- name this L2
case L2 of ~(y:ys) -> 1:y:ys
= 1 : (case L2 of (y:_) -> y) : (case L2 of (_:ys) -> ys)
= 1 : 1 : 1 : undefined : undefined
which does converge to the intended list. Note how lazy patterns are "pushed forward" without evaluating the case argument early. This is what makes them lazy. In this way, the 1 is produced before the pattern matching is performed, making the result of the recursively defined entity non trivial.

The problem here is not so much about the pattern matching or using head and tail. The issue is how it's done, by defining your function as
interleave :: [a] -> [a] -> [a]
interleave (x:xs) (y:ys) = x : y : interleave xs ys
interleave _ _ = []
You're strictly pattern matching your arguments, that is, we need to know that they are lists of at least one element before we can choose the first branch. Since you're folding this function over an infinite list of lists, we can't really figure this out, and we run out of stack space.
To expand on this (to clarify things brought up in the comments), the first time we try to evaluate interleave (in ruler), we'd get something like
interleave (repeat 0) (foldr1 interleave (map repeat [1..]))
The first argument here of course matches the pattern, but to figure out if the second argument does, we have to try to evaluate it, so we get to
interleave (repeat 1) (foldr1 interleave (map repeat [2..]))
Now we can't evaluate this unless we know more about the second argument. Since the list [2..] never ends, this process can go on forever.
One solution to this is to do a lazy pattern binding on the second argument:
interleave (x:xs) ~(y:ys) = x : y : interleave xs ys
This acts like a promise that the second argument does match the pattern, so don't worry about it (of course this will fail if that isn't true). This means that that first evaluation of interleave can go on without looking deeper into the repeated fold, which in a domino-like effect solves the issue.
A sidenote is that your this version of interleave (as well as your head/tail version) will only work on lists where the second list is as long as or longer than the first.

Related

Haskell - using foldl or foldr instead of pattern matching for updating a list with a new value at a given index

I have implemented a function (!!=) that given a list and a tuple containing an index in the list and a
new value, updates the given list with the new value at the given
index.
(!!=) :: [a] -> (Int,a) -> [a]
(!!=) xs (0, a) = a : tail xs
(!!=) [] (i, a) = error "Index not in the list"
(!!=) (x:xs) (i, a) = x : xs !!= (i-1, a)
Being a beginner with the concept of folding I was wondering if there is a way to achieve the same result using foldl or foldr instead?
Thanks a lot in advance.
I'll give you the foldl version which is easier to understand I think and the easiest / most straight-forward version I can think of.
But please note that you should not use foldl (use foldl': https://wiki.haskell.org/Foldr_Foldl_Foldl') - nor should you use ++ like this (use : and reverse after) ;)
Anway this is the idea:
(!!=) xs (i, a) = snd $ foldl
(\(j, ys) x -> (j+1, if j == i then ys ++ [a] else ys ++ [x]))
(0, [])
xs
as the state/accumulator for the fold I take a tuple of the current index and the accumulated result list (therefore the snd because I only want this in the end)
then the folding function just have to look if we are at the index and exchange the element - returning the next index and the new accumulated list
as an exercise you can try to:
use : instead of ++ and a reverse
rewrite as foldr
look at zipWith and rewrite this using this (zipWith (...) [0..] xs) instead of the fold (this is similar to using a map with index
Neither foldl nor foldr can do this particular job efficiently (unless you "cheat" by pattern matching on the list as you fold over it), though foldr can do it a bit less badly. No, what you really need is a different style of fold, sometimes called para:
para :: (a -> [a] -> b -> b) -> b -> [a] -> b
para _f n [] = n
para f n (a : as) = f a as (para f n as)
para is very similar to foldr. Each of them takes a combining function and, for each element, passes the combining function that element and the result of folding up the rest of the list. But para adds something extra: it also passes in the rest of the list! So there's no need to reconstruct the tail of the list once you've reached the replacement point.
But ... how do you count from the beginning with foldr or para? That brings in a classic trick, sometimes called a "higher-order fold". Instead of para go stop xs producing a list, it's going to produce a function that takes the insertion position as an argument.
(!!=) :: [a] -> (Int, a) -> [a]
xs0 !!= (i0, new) = para go stop xs0 i0
where
-- If the list is empty, then no matter what index
-- you seek, it's not there.
stop = \_ -> error "Index not in the list"
-- We produce a function that takes an index. If the
-- index is 0, we combine the new element with "the rest of the list".
-- Otherwise we apply the function we get from folding up the rest of
-- the list to the predecessor of the index, and tack on the current
-- element.
go x xs r = \i -> case i of
0 -> new : xs
_ -> x : r (i - 1)
Note that para is easily powerful enough to implement foldr:
foldr c = para (\a _ b -> c a b)
What's perhaps less obvious is that foldr can implement a (very inefficient version of) para:
para f n = snd . foldr go ([], n)
where
go x ~(xs, r) = (x : xs, f x xs r)
Lest you get the wrong idea and think that para is "better than" foldr, know that when its extra power isn't needed, foldr is simpler to use and will very often be compiled to more efficient code.

Breaking a list into sublists of a specified size using foldr

I'm taking a functional programming class and I'm having a hard time leaving the OOP mindset behind and finding answers to a lot of my questions.
I have to create a function that takes an ordered list and converts it into specified size sublists using a variation of fold.
This isn't right, but it's what I have:
splitList :: (Ord a) => Int -> [a] -> [[a]]
splitList size xs
| [condition] = foldr (\item subList -> item:subList) [] xs
| otherwise =
I've been searching and I found out that foldr is the variation that works better for what I want, and I think I've understood how fold works, I just don't know how I'll set up the guards so that when length sublist == size haskell resets the accumulator and goes on to the next list.
If I didn't explain myself correctly, here's the result I want:
> splitList 3 [1..10]
> [[1,2,3],[4,5,6],[7,8,9],[10]]
Thanks!
While Fabián's and chi's answers are entirely correct, there is actually an option to solve this puzzle using foldr. Consider the following code:
splitList :: Int -> [a] -> [[a]]
splitList n =
foldr (\el acc -> case acc of
[] -> [[el]]
(h : t) | length h < n -> (el : h) : t
_ -> [el] : acc
) []
The strategy here is to build up a list by extending its head as long as its length is lesser than desired. This solution has, however, two drawbacks:
It does something slightly different than in your example;
splitList 3 [1..10] produces [[1],[2,3,4],[5,6,7],[8,9,10]]
It's complexity is O(n * length l), as we measure length of up to n–sized list on each of the element which yields linear number of linear operations.
Let's first take care of first issue. In order to start counting at the beginning we need to traverse the list left–to–right, while foldr does it right–to–left. There is a common trick called "continuation passing" which will allow us to reverse the direction of the walk:
splitList :: Int -> [a] -> [[a]]
splitList n l = map reverse . reverse $
foldr (\el cont acc ->
case acc of
[] -> cont [[el]]
(h : t) | length h < n -> cont ((el : h) : t)
_ -> cont ([el] : acc)
) id l []
Here, instead of building the list in the accumulator we build up a function that will transform the list in the right direction. See this question for details. The side effect is reversing the list so we need to counter that by reverse application to the whole list and all of its elements. This goes linearly and tail-recursively tho.
Now let's work on the performance issue. The problem was that the length is linear on casual lists. There are two solutions for this:
Use another structure that caches length for a constant time access
Cache the value by ourselves
Because I guess it is a list exercise, let's go for the latter option:
splitList :: Int -> [a] -> [[a]]
splitList n l = map reverse . reverse . snd $
foldr (\el cont (countAcc, listAcc) ->
case listAcc of
[] -> cont (countAcc, [[el]])
(h : t) | countAcc < n -> cont (countAcc + 1, (el : h) : t)
(h : t) -> cont (1, [el] : (h : t))
) id l (1, [])
Here we extend our computational state with a counter that at each points stores the current length of the list. This gives us a constant check on each element and results in linear time complexity in the end.
A way to simplify this problem would be to split this into multiple functions. There are two things you need to do:
take n elements from the list, and
keep taking from the list as much as possible.
Lets try taking first:
taking :: Int -> [a] -> [a]
taking n [] = undefined
taking n (x:xs) = undefined
If there are no elemensts then we cannot take any more elements so we can only return an empty list, on the other hand if we do have an element then we can think of taking n (x:xs) as x : taking (n-1) xs, we would only need to check that n > 0.
taking n (x:xs)
| n > 0 = x :taking (n-1) xs
| otherwise = []
Now, we need to do that multiple times with the remainder so we should probably also return whatever remains from taking n elements from a list, in this case it would be whatever remains when n = 0 so we could try to adapt it to
| otherwise = ([], x:xs)
and then you would need to modify the type signature to return ([a], [a]) and the other 2 definitions to ensure you do return whatever remained after taking n.
With this approach your splitList would look like:
splitList n [] = []
splitList n l = chunk : splitList n remainder
where (chunk, remainder) = taking n l
Note however that folding would not be appropriate since it "flattens" whatever you are working on, for example given a [Int] you could fold to produce a sum which would be an Int. (foldr :: (a -> b -> b) -> b -> [a] -> b or "foldr function zero list produces an element of the function return type")
You want:
splitList 3 [1..10]
> [[1,2,3],[4,5,6],[7,8,9],[10]]
Since the "remainder" [10] in on the tail, I recommend you use foldl instead. E.g.
splitList :: (Ord a) => Int -> [a] -> [[a]]
splitList size xs
| size > 0 = foldl go [] xs
| otherwise = error "need a positive size"
where go acc x = ....
What should go do? Essentially, on your example, we must have:
splitList 3 [1..10]
= go (splitList 3 [1..9]) 10
= go [[1,2,3],[4,5,6],[7,8,9]] 10
= [[1,2,3],[4,5,6],[7,8,9],[10]]
splitList 3 [1..9]
= go (splitList 3 [1..8]) 9
= go [[1,2,3],[4,5,6],[7,8]] 9
= [[1,2,3],[4,5,6],[7,8,9]]
splitList 3 [1..8]
= go (splitList 3 [1..7]) 8
= go [[1,2,3],[4,5,6],[7]] 8
= [[1,2,3],[4,5,6],[7,8]]
and
splitList 3 [1]
= go [] 1
= [[1]]
Hence, go acc x should
check if acc is empty, if so, produce a singleton list [[x]].
otherwise, check the last list in acc:
if its length is less than size, append x
otherwise, append a new list [x] to acc
Try doing this by hand on your example to understand all the cases.
This will not be efficient, but it will work.
You don't really need the Ord a constraint.
Checking the accumulator's first sublist's length would lead to information flow from the right and the first chunk ending up the shorter one, potentially, instead of the last. Such function won't work on infinite lists either (not to mention the foldl-based variants).
A standard way to arrange for the information flow from the left with foldr is using an additional argument. The general scheme is
subLists n xs = foldr g z xs n
where
g x r i = cons x i (r (i-1))
....
The i argument to cons will guide its decision as to where to add the current element into. The i-1 decrements the counter on the way forward from the left, instead of on the way back from the right. z must have the same type as r and as the foldr itself as a whole, so,
z _ = [[]]
This means there must be a post-processing step, and some edge cases must be handled as well,
subLists n xs = post . foldr g z xs $ n
where
z _ = [[]]
g x r i | i == 1 = cons x i (r n)
g x r i = cons x i (r (i-1))
....
cons must be lazy enough not to force the results of the recursive call prematurely.
I leave it as an exercise finishing this up.
For a simpler version with a pre-processing step instead, see this recent answer of mine.
Just going to give another answer: this is quite similar to trying to write groupBy as a fold, and actually has a couple gotchas w.r.t. laziness that you have to bear in mind for an efficient and correct implementation. The following is the fastest version I found that maintains all the relevant laziness properties:
splitList :: Int -> [a] -> [[a]]
splitList m xs = snd (foldr f (const ([],[])) xs 1)
where
f x a i
| i <= 1 = let (ys,zs) = a m in ([], (x : ys) : zs)
| otherwise = let (ys,zs) = a (i-1) in (x : ys , zs)
The ys and the zs gotten from the recursive processing of the rest of list indicate the first and the rest of the groups into which the rest of the list will be broken up, by said recursive processing. So we either prepend the current element before that first subgroup if it is still shorter than needed, or we prepend before the first subgroup when it is just right and start a new, empty subgroup.

How to use foldr to add variables to each other in a list?

When given a list [x0, x1, x2, . . . , xn−1], the function
should return the list [y0, y1, y2, . . . , yn−1] where y0 = x0, y1 = x0 + x1, ...
So if you had [1,2,3] as input, you would get [1,3,6] as output
I don't completely understand foldr, so maybe if I could get some help in trying to figure out how to change that last line to get the right answer.
scan :: [Integer] -> [Integer]
scan [] = []
scan [x] = [x]
scan (x:xs) = x : foldr (/y -> y (+) x) 0 (scan xs)
My initial solution (that works) uses the map function.
scan :: [Integer] -> [Integer]
scan [] = []
scan [x] = [x]
scan (x:xs) = x : map (+x) (scan xs)
EDIT, I added this first section to better address your two implementations.
First, addressing your issue with your implementation using foldr, here are a few remarks:
Lambdas start with a backslash in Haskell, not a slash. That's because backslashes kind of look like the lambda greek letter (λ).
Functions named using only special characters, like +, are infix by default. If you use parens around them, it turns them into prefix functions:
$> (+) 1 5
$> 6
The function passed to foldr takes two argument, whereas you're only supplying one in your lambda. If you really want to ignore the second one, you can use a _ instead of binding it to a variable (\x _ -> x).
I think this you're going down a rabbit hole with this implementation. See the discussion below for my take on the right way to tackle this issue.
Note: It is possible to implement map using foldr (source), that's one way you could use foldr in your working (second) implementation.
Implementing this with foldr is not optimal, since it folds, as the name implies, from the right:
foldr1 (+) [1..5]
--is equivalent to:
(1+(2+(3+(4+5))))
As you can see, the summing operation is done starting from the tail of the list, which is not what you're looking for. To make this work, you would have to "cheat", and reverse your list twice, once before folding it and once after:
scan = tail . reverse . foldr step [0] . reverse where
step e acc#(a:_) = (e + a) : acc
You can make this better using a left fold, which folds from the left:
foldl1 (+) [1..5]
--is equivalent to:
((((1+2)+3)+4)+5)
This, however, still isn't ideal, because to keep the order of elements in your accumulator the same, you would have to use the ++ function, which amounts to quadratic time complexity in such a function. A compromise is to use the : function, but then you still have to reverse your accumulator list after the fold, which is only linear complexity:
scan' :: [Integer] -> [Integer]
scan' = tail . reverse . foldl step [0] where
step acc#(a:_) e = (e + a) : acc
This still isn't very good, since the reverse adds an extra computation. The ideal solution would therefore be to use scanl1, which, as a bonus, doesn't require you to give a starting value ([0] in the examples above):
scan'' :: [Integer] -> [Integer]
scan'' = scanl1 (+)
scanl1 is implemented in terms of scanl, which is defined roughly like this:
scanl f init list = init : (case list of
[] -> []
x:xs -> scanl f (f init x) xs)
You can therefore simply do:
$> scanl1 (+) [1..3]
$> [1,3,6]
As a final note, your scan function is unnecessarily specialized to Integer, as it only requires a Num constraint:
scan :: Num a => [a] -> [a]
This might even lead to an increase in performance, but that's where my abilities end, so I won't go any further :)

Function that removes element if it exists but adds it of it does not

I'm looking for a cleaner way to write a function that adds an element to a list if the list does not contain it. Or otherwise removes it if the list does contain it, I'm using an if clause now and the function is working.
But I'm trying to find a more haskell-ish way to right this.
This is my code:
removeElemOrAdd :: Eq a => a -> [a] -> [a]
removeElemOrAdd elem list = if (List.elem elem list)
then (filter(\x -> x /= elem) list)
else (elem:list)
Note: a small ambiguity in your question is what to do when x already occurs multiple times in the original list. I assumed this won't happen and in case it does, only the first occurrence is removed. Meaning that removeElemOrAdd 2 [4,2,5,2,7] will result in [4,5,2,7]. Furthermore it is unspecified where the item should be added. Because it has some advantages, I've opted to do this at the end of the list.
An implementation without using any library methods is the following:
removeElemOrAdd :: Eq a => a -> [a] -> [a]
removeElemOrAdd x (y:ys) | x == y = ys
| otherwise = y : removeElemOrAdd x ys
removeElemOrAdd x _ = [x]
Or a shorter version:
removeElemOrAdd :: Eq a => a -> [a] -> [a]
removeElemOrAdd x = reoa
where reoa (y:ys) | x == y = ys
| otherwise = y : reoa ys
reoa _ = [x]
or an equivalent implementation (see discussion below):
removeElemOrAdd :: Eq a => a -> [a] -> [a]
removeElemOrAdd x = reoa
where reoa (y:ys) | x == y = ys
| otherwise = y : reoa ys
reoa [] = [x]
The function works as follows: in case we are talking about a list with at least one item (y:ys), we compare x with y and if they are equal, we return ys: in that case we have removed the element and we are done.
Now in case the two are not equal, we return a list construction (:) with y in the head since we need to retain y and in the tail, we will do a recursive call removeElemOrAdd with x and ys. Indeed: it is possible that there is an x somewhere in the tail ys to remove, and furthermore we still need to add x to the list if it does not occur.
That clause will loop recursively through the list. From the moment it finds an y such that x == y it will remove that y. It is however possible that we reach the end of the list, and still have not found the element. In that case we will call the final clause. Here we know the list is empty (we could have written removeElemOrAdd x []) but to make the function definition syntactically total, I have opted to use an underscore. We can only reach this state if we have failed to find x in the list, so then we add it to the tail of the list by returning [x].
An advantage of this approach over using the if-then-else is that this does all tasks at once (checking, removing and adding) making it more efficient.
Another advantage is that this can run on an "infinite" list (like for instance the list of prime numbers). The list is evaluated lazily, so if you want to take the first three items, this function will only check the equality of the first three items.
I like the other approaches, but don't like that they behave differently than the specification. So here is an approach that:
Like the original, deletes all copies, if there are any, AND
like the original, inserts the new value at the beginning, if necessary, BUT
unlike the original, uses a clever trick based on the ideas of beautiful folding (and follow-up developments) to make only one pass through the data.
The basic idea is that we will have a single value which tracks both whether all values so far have been a mismatch as well as the resulting filtered list. The injectNE operation will perform this operation for a single element of the list, and we will then use foldMap to expand from one element to the whole input list.
import Data.Monoid
injectNE :: Eq a => a -> a -> (All, [a])
injectNE old new = (All ne, [new | ne]) where
ne = old /= new
removeElemOrAdd :: Eq a => a -> [a] -> [a]
removeElemOrAdd x xs = case foldMap (injectNE x) xs of
(All nex, noxs) -> [x | nex] ++ noxs
In the final pattern, you should read nex as "no element was equal to x", and noxs as "the list without any copies of x" (get it? "no xs"? ba-dum-tsh).
It is slightly unfortunate that the spec was written the way it was, though: in particular, one of the selling points of beautiful folding is that its resulting one-pass folds can be friendlier to the garbage collector. But the spec makes that quite hard, because we must traverse the entire input list before deciding what the first element of the result list should be. We can improve the friendliness to the garbage collector significantly by relaxing point (2) above (but leaving (1) and (3)); and moreover the difference is merely swapping the arguments to (++), a nicely semantic diff to see in your revision history:
-- <snipped identical code>
removeElemOrAdd x xs = case ... of
... -> noxs ++ [x | nex]
I'd use a fold to remove all copies:
removeOrAdd x xs = foldr go (bool [x] []) xs False where
go y r found
| y == x = r True
| otherwise = y : r found
To remove just one, a paramorphism seems to be in order:
removeOrAdd x = para go [x] where
go y ys r
| y == x = ys
| otherwise = y : r

Need to partition a list into lists based on breaks in ascending order of elements (Haskell)

Say I have any list like this:
[4,5,6,7,1,2,3,4,5,6,1,2]
I need a Haskell function that will transform this list into a list of lists which are composed of the segments of the original list which form a series in ascending order. So the result should look like this:
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]
Any suggestions?
You can do this by resorting to manual recursion, but I like to believe Haskell is a more evolved language. Let's see if we can develop a solution that uses existing recursion strategies. First some preliminaries.
{-# LANGUAGE NoMonomorphismRestriction #-}
-- because who wants to write type signatures, amirite?
import Data.List.Split -- from package split on Hackage
Step one is to observe that we want to split the list based on a criteria that looks at two elements of the list at once. So we'll need a new list with elements representing a "previous" and "next" value. There's a very standard trick for this:
previousAndNext xs = zip xs (drop 1 xs)
However, for our purposes, this won't quite work: this function always outputs a list that's shorter than the input, and we will always want a list of the same length as the input (and in particular we want some output even when the input is a list of length one). So we'll modify the standard trick just a bit with a "null terminator".
pan xs = zip xs (map Just (drop 1 xs) ++ [Nothing])
Now we're going to look through this list for places where the previous element is bigger than the next element (or the next element doesn't exist). Let's write a predicate that does that check.
bigger (x, y) = maybe False (x >) y
Now let's write the function that actually does the split. Our "delimiters" will be values that satisfy bigger; and we never want to throw them away, so let's keep them.
ascendingTuples = split . keepDelimsR $ whenElt bigger
The final step is just to throw together the bit that constructs the tuples, the bit that splits the tuples, and a last bit of munging to throw away the bits of the tuples we don't care about:
ascending = map (map fst) . ascendingTuples . pan
Let's try it out in ghci:
*Main> ascending [4,5,6,7,1,2,3,4,5,6,1,2]
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]
*Main> ascending [7,6..1]
[[7],[6],[5],[4],[3],[2],[1]]
*Main> ascending []
[[]]
*Main> ascending [1]
[[1]]
P.S. In the current release of split, keepDelimsR is slightly stricter than it needs to be, and as a result ascending currently doesn't work with infinite lists. I've submitted a patch that makes it lazier, though.
ascend :: Ord a => [a] -> [[a]]
ascend xs = foldr f [] xs
where
f a [] = [[a]]
f a xs'#(y:ys) | a < head y = (a:y):ys
| otherwise = [a]:xs'
In ghci
*Main> ascend [4,5,6,7,1,2,3,4,5,6,1,2]
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]
This problem is a natural fit for a paramorphism-based solution. Having (as defined in that post)
para :: (a -> [a] -> b -> b) -> b -> [a] -> b
foldr :: (a -> b -> b) -> b -> [a] -> b
para c n (x : xs) = c x xs (para c n xs)
foldr c n (x : xs) = c x (foldr c n xs)
para c n [] = n
foldr c n [] = n
we can write
partition_asc xs = para c [] xs where
c x (y:_) ~(a:b) | x<y = (x:a):b
c x _ r = [x]:r
Trivial, since the abstraction fits.
BTW they have two kinds of map in Common Lisp - mapcar
(processing elements of an input list one by one)
and maplist (processing "tails" of a list). With this idea we get
import Data.List (tails)
partition_asc2 xs = foldr c [] . init . tails $ xs where
c (x:y:_) ~(a:b) | x<y = (x:a):b
c (x:_) r = [x]:r
Lazy patterns in both versions make it work with infinite input lists
in a productive manner (as first shown in Daniel Fischer's answer).
update 2020-05-08: not so trivial after all. Both head . head . partition_asc $ [4] ++ undefined and the same for partition_asc2 fail with *** Exception: Prelude.undefined. The combining function g forces the next element y prematurely. It needs to be more carefully written to be productive right away before ever looking at the next element, as e.g. for the second version,
partition_asc2' xs = foldr c [] . init . tails $ xs where
c (x:ys) r#(~(a:b)) = (x:g):gs
where
(g,gs) | not (null ys)
&& x < head ys = (a,b)
| otherwise = ([],r)
(again, as first shown in Daniel's answer).
You can use a right fold to break up the list at down-steps:
foldr foo [] xs
where
foo x yss = (x:zs) : ws
where
(zs, ws) = case yss of
(ys#(y:_)) : rest
| x < y -> (ys,rest)
| otherwise -> ([],yss)
_ -> ([],[])
(It's a bit complicated in order to have the combining function lazy in the second argument, so that it works well for infinite lists too.)
One other way of approaching this task (which, in fact lays the fundamentals of a very efficient sorting algorithm) is using the Continuation Passing Style a.k.a CPS which, in this particular case applied to folding from right; foldr.
As is, this answer would only chunk up the ascending chunks however, it would be nice to chunk up the descending ones at the same time... preferably in reverse order all in O(n) which would leave us with only binary merging of the obtained chunks for a perfectly sorted output. Yet that's another answer for another question.
chunks :: Ord a => [a] -> [[a]]
chunks xs = foldr go return xs $ []
where
go :: Ord a => a -> ([a] -> [[a]]) -> ([a] -> [[a]])
go c f = \ps -> let (r:rs) = f [c]
in case ps of
[] -> r:rs
[p] -> if c > p then (p:r):rs else [p]:(r:rs)
*Main> chunks [4,5,6,7,1,2,3,4,5,6,1,2]
[[4,5,6,7],[1,2,3,4,5,6],[1,2]]
*Main> chunks [4,5,6,7,1,2,3,4,5,4,3,2,6,1,2]
[[4,5,6,7],[1,2,3,4,5],[4],[3],[2,6],[1,2]]
In the above code c stands for current and p is for previous and again, remember we are folding from right so previous, is actually the next item to process.