What's the most efficient way to take up to the last n elements of a list - haskell

To clarify : I want all the elements but the last n
Reading some answers on stackoverflow I get the impression that using length on lists is inadvisable. So is there better way to do this than take (length xs - n) xs?

Yes. You first drop n elements from the list, then walk over the two lists concurrently and when the you hit the end of the list with the dropped version of the list, then you return the list of the "iterator" over the entire list:
takeLast :: Int -> [a] -> [a]
takeLast n ls = go (drop n ls) ls
where go [] ls = ls
go (_:xs) ~(_:ys) = go xs ys
This thus works because the first "iterator" is n steps ahead. So if it hits the end of the list, the second iterator is n steps behind the end of the list.
For example:
Prelude> takeLast 2 [1,4,2,5,1,3,0,2]
[0,2]
Prelude> takeLast 3 [1,4,2,5,1,3,0,2]
[3,0,2]
Prelude> takeLast 4 [1,4,2,5,1,3,0,2]
[1,3,0,2]
Prelude> takeLast 5 [1,4,2,5,1,3,0,2]
[5,1,3,0,2]
We can also drop the last n elements in a similar way:
dropLast :: Int -> [a] -> [a]
dropLast n ls = go (drop n ls) ls
where go [] _ = []
go (_:xs) ~(y:ys) = y : go xs ys
For example:
Prelude> dropLast 2 [1,4,2,5,1,3,0,2]
[1,4,2,5,1,3]
Prelude> dropLast 3 [1,4,2,5,1,3,0,2]
[1,4,2,5,1]
Prelude> dropLast 4 [1,4,2,5,1,3,0,2]
[1,4,2,5]
Prelude> dropLast 5 [1,4,2,5,1,3,0,2]
[1,4,2]
If we operate on an infinite list, then dropLast will still yield elements, whereas if we would use take (length ls - n) ls, it would get stuck in an infinite loop.
We can, as #DanielWagner says use zipWith for this:
dropLast :: Int -> [a] -> [a]
dropLast n xs = zipWith const xs (drop n xs)
Here we let zipWith iterate over both lists, and we use const to each time return the element of the first list.

Related

Transform a string into a list of substrings describing sequences

I am trying to split a string into a list of sequences that are substrings of this string. It's a bit hard to explain but I will give you an example so you can understand what I am looking for.
From this string "123456789", I would like to obtain a list like so:
["123", "234, "345, ..., "789", "891", "912"]
At the moment, I only have a function that splits a string into a list of n parts of this string:
splitList :: Int -> [a] -> [[a]]
splitList _ [] = []
splitList n xs = as : splitList n bs
where (as,bs) = splitAt n xs
I would just use a combination of take and drop, with a list comprehension:
splitList :: Int -> [a] -> [[a]]
splitList n xs = [take n $ drop i $ xs ++ xs | i <- [0..(length xs - 1)]]
(the xs ++ xs is only there to get the "cycling" affect, it could be tuned to only add on the first (n-1) elements but I believe Haskell's laziness should mean there is no efficiency loss in doing it this way)
I would do it this way:
import Data.List
splitList n xs = zipWith const chunks xs where
chunks = map (take n) . tails . cycle $ xs
This should have complexity O(m*n), where m is the length of xs and n is the size of each chunk; naively it seems as though it should be hard to do better than that, since that's the size of the output. It also neatly handles a number of awkward edge cases, including working sensibly on infinite list inputs.
If you haven't seen the zipWith const trick before, it's definitely one worth adding to your arsenal. It lets you do roughly the same thing as take (length xs) ys, but without actually computing length xs beforehand.
splitList :: Int -> [a] -> [[a]]
splitList n xs = zipWith const (map (take n) . tails . cycle $ xs) xs
-- splitList n = zipWith const =<< map (take n) . tails . cycle
-- splitList n = map (take n) . tails . cycle >>= zipWith const
does the job, and also works on infinite input, i.e. is properly lazy.
zipWith const is used instead of length and take, counting in list elements instead of numbers.
The pointfree variants are even readable / illuminating somewhat as to what is going on here.
(forgot to mention, tails are from Data.List).

Recursive sum columns of matrix to single row (with zipWith (+))

lstsAdder :: [[Integer]] -> [Integer]
lstsAdder [] = []
lstsAdder (x:xs) = zipWith (+) x (lstsAdder xs)
As the title says, I want it to recursively add this: [[a,b,c],[d,e,f]] like this: [a+d,b+e,c+f], and this with lists of lists of any finite length. But all my implementation returns is []. Why is that, and how do I fix it?
Your recursion base case returns [], and length (zipWith f a b) = min (length a) (length b). This means your result will always have length 0. The identity element for min is +infinity, and the identity element for (+) is 0, so one possible base case is repeat 0.
You could also see if pre-conditions on the data would allow you to do something like
import Data.List (transpose)
lstsAdder = map sum . transpose
It has different edge-case behaviour (QuickCheck gives [[0,0],[0]] as one example input) but maybe those edge cases won't occur for you in practice.
Your base case is too basic... the function will recursively consume all rows. When it's all the way down the recursion stack, it's left with the empty list, i.e. the list with no rows. This returns an empty result.
But then, going back the recursion stack, each layer is supposed to be zipped onto it with +. Well, but zipping any list with an empty list results in an empty list!
There are three ways you can adress this issue:
Add an extra base case for one-row matrices. If there's only one row, the result should be just that row, right?
lstsAdder [] = []
lstsAdder [r] = r
lstsAdder (x:xs) = zipWith (+) x $ lstsAdder xs
Fill missing elements with zeroes in the zipping step.
lstsAdder [] = []
lstsAdder (x:xs) = x ^+^ lstsAdder xs
infixrl 6 ^+^
(^+^) :: [a] -> [a] -> [a]
xs^+^[] = xs
[]^+^ys = ys
(x:xs)^+^(y:ys) = (x+y) : (xs^+^ys)
Give an infinite list of zeroes for the base case:
lstsAdder [] = repeat 0
lstsAdder (x:xs) = zipWith (+) x $ lstsAdder xs
I believe you may also do with a single foldr1 operation as follows;
listsAdd :: (Foldable t, Num c) => t [c] -> [c]
listsAdd = foldr1 (zipWith (+))
*Main> listsAdd [[1,2,3],[4,5,6]]
[5,7,9]

getting every 3rd element & looping back (Haskell)

I want to write a function that returns every nth element of a given list but then updates the head to the second element and does this again until it has gone through all elements in the list.
I know the code for going through every nth element in a list is:
everyf n [] = []
everyf n as = head as : everyf n (drop n as)
but this does not loop back. How can I get this to update head so that i get the following result:
everyf 3 [1,2,3,4,5,6,7]
returns [[1,4,7],[2,5],[3,6]]
Disclaimer: I don't know haskell (at all) :-)
listOfNths :: Int -> [a] -> [[a]]
listOfNths n xs = map (\x -> everyf n (drop x xs)) [0..n-1]
and a slightly "improved" version:
listOfNths :: Int -> [a] -> [[a]]
listOfNths n xs = map pickEveryf [0..n-1]
where pickEveryf = (everyf n) . (`drop` xs)

Haskell program to replicate elements in List

I am new to Haskell.
I am trying to write a program which given a list as an input replicates each element of list k times, where k = position of element in list.
e.g. replic[5,6,7] gives [[5],[6,6],[7,7,7]].
Another condition is solution has to use map function.
Till now code I have written is :
replic [] = []
replic (x:xs) = map (replicate 2 ) [x] ++ replic xs
This replicates every element twice as replicate has input parameter 2.
What I need is replicate function should be given input as 1 ,2 ,3 in consecutive calls. So I need a counter. How can I use the counter there or do anything else that will give me position of element?
Expanding on Satvik, the notation
[1..]
gives you an infinite list of numbers counting up.
The function zip associates allows you to merge two lists into a list of tuples
zip :: [a] -> [b] -> [(a,b)]
for example
> zip [1..] [5,6,7]
[(1,5),(2,6),(3,7)]
this code associates each value in the list with its position in the list
now
replicate :: Int -> a -> [a]
repeats a value an arbitrary number of times. Given these two components, we can design a simple function
replic xs = map (\(a,b) -> replicate a b) (zip [1..] xs)
which I would write pointfree as
replic :: [a] -> [[a]]
replic = map (uncurry replicate) . zip [1..]
this does exactly what you want
> replic [5,6,7]
[[5],[6,6],[7,7,7]]
There are many ways of doing this
Here is a solution similar to what you tried to do. zipping the list with list [1..] gives you the counter you wanted.
replic = repl . zip [1..]
repl [] = []
repl ((x,y):xs) = (replicate x y) : (repl xs)
Another solution using just map
replic = map f . zip [1..]
where
f (c,l) = replicate c l
If you don't like idea of using zip you can also use mapAccumL
import Data.List
replic = snd . mapAccumL f 1
where
f a v = (a+1,replicate a v)
Usually you would write:
replic = zipWith replicate [1..]
Now you can write your own zipWith yourself using map:
zipWith' f xs ys = map (uncurry f) $ zip xs ys
Note that you don't necessarily need an index, e.g.
import Data.List
replic xs = reverse $ transpose (tail $ inits $ reverse xs)
You can do something like this with map when using explicit recursion:
replic = f . map return where
f [] = []
f (x:xs) = x : f (map (\(x:xs) -> x:x:xs) xs)

Remove every nth element from string

How can you remove every nth element of a string?
I'm guessing you would use the drop function in some kind of way.
Like this drops the first n, how can you change this so only drops the nth, and then the nth after that, and so on, rather than all?
dropthem n xs = drop n xs
Simple. Take (n-1) elements, then skip 1, rinse and repeat.
dropEvery _ [] = []
dropEvery n xs = take (n-1) xs ++ dropEvery n (drop n xs)
Or in showS style for efficiency's sake
dropEvery n xs = dropEvery' n xs $ []
where dropEvery' n [] = id
dropEvery' n xs = (take (n-1) xs ++) . dropEvery n (drop n xs)
-- groups is a pretty useful function on its own!
groups :: Int -> [a] -> [[a]]
groups n = map (take n) . takeWhile (not . null) . iterate (drop n)
removeEveryNth :: Int -> [a] -> [a]
removeEveryNth n = concatMap (take (n-1)) . groups n
remove_every_nth :: Int -> [a] -> [a]
remove_every_nth n = foldr step [] . zip [1..]
where step (i,x) acc = if (i `mod` n) == 0 then acc else x:acc
Here's what the function does:
zip [1..] is used to index all items in the list, so e.g. zip [1..] "foo" becomes [(1,'f'), (2,'o'), (3,'o')].
The indexed list is then processed with a right fold which accumulates every element whose index is not divisible by n.
Here's a slightly longer version that does essentially the same thing, but avoids the extra memory allocations from zip [1..] and doesn't need to calculate modulus.
remove_every_nth :: Int -> [a] -> [a]
remove_every_nth = recur 1
where recur _ _ [] = []
recur i n (x:xs) = if i == n
then recur 1 n xs
else x:recur (i+1) n xs
Try to combine take and drop to achieve this.
take 3 "hello world" = "hel"
drop 4 "hello world" = "o world"
I like the following solution:
del_every_nth :: Int -> [a] -> [a]
del_every_nth n = concat . map init . group n
You just have to define a function group which groups a list in portions of length n. But that's quite easy:
group :: Int -> [a] -> [[a]]
group n [] = []
group n xs = take n xs : group n (drop n xs)

Resources