My code is working but not for endless lists. How can I make it work ?
sumsOf :: Num a => [[a]] -> [a]
sumsOf a = map sum [ x | x <- a , length x < 3]
Examples :
sumsOf [[],[1,2,4],[],[6]] == [0,0,6]
sumsOf [[1],[8],[6],[],[9,9]] == [1,8,6,0,18]
sumsOf [[1,2,9,10],[7,8,9],[6,9,4,2,0],[9,9,9]] == []
sumsOf [[1..],[7..],[6..],[9..],[10..],[100..]] == []
sumsOf [[1,2], [1..], [], [4]] == [3,0,4]
CodePudding user response:
Easy-lazy solution:
You are only concerned about whether the length is less than 3, so the result is not changed if you truncate your input list at 3 elements:
$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/ :? for help
λ>
λ> sumsOf a = map sum [ x | x <- a , length (take 3 x) < 3]
λ>
λ> sumsOf [[],[1,2,4],[],[6]]
[0,0,6]
λ> sumsOf [[1],[8],[6],[],[9,9]]
[1,8,6,0,18]
λ> sumsOf [[1,2,9,10],[7,8,9],[6,9,4,2,0],[9,9,9]]
[]
λ> sumsOf [[1..],[7..],[6..],[9..],[10..],[100..]]
[]
λ> sumsOf [[1,2], [1..], [], [4]]
[3,0,4]
λ>
So this readily solves the problem with unlimited lists.
In slightly more idiomatic fashion:
sumsOf :: Num a => [[a]] -> [a]
sumsOf xss = map sum [ xs | xs <- xss , length (take 3 xs) < 3]
The sole drawback is that library function take
has to duplicate the list nodes.
An idea mentioned in a comment by n.1.8e9-where's-my-sharem: it is possible to have something a bit more efficient, by writing some (possibly recursive) boundedLength
function:
boundedLength :: Int -> [a] -> Int
boundedLength k xs = ... TODO ...
which returns at most k
. Left as an exercise to the reader.
CodePudding user response:
length
is one of the list functions that you should as a rule of thumb never use except when you know exactly why it's ok. (The other being head
and !!
.) It's not just because it doesn't work with infinite lists, but also because it does a usually completely unnecessary O(n) traversal over the entire list. Even if you need to traverse it anyway later, doing it twice can make a big performance difference.
In this case think about what you really need: a sum
that avoids going into an long or infinite list but instead gives up after two elements. That could actually be implemented in a stupid exhaustion way:
sumIfLenLt3 :: Num a => [a] -> Maybe a
sumIfLenLt3 [] = Just 0
sumIfLenLt3 [x] = Just x
sumIfLenLt3 [x,y] = Just $ x y
sumIfLenLt3 _ = Nothing
To make it general for any max-length, you could use recursion:
sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe _ [] = Just 0
sumIfLenLe 0 _ = Nothing
sumIfLenLe n (h:t) = h sumIfLenLe n t
Actually that's not such a good implementation, for two reasons: it blows up with negative arguments, and it isn't tail recursive. A better, albeit a bit cumbersome, version would be
sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe n l
| n<0 = Nothing
| otherwise = go n l 0
where go n [] acc = Just acc
go 0 _ _ = Nothing
go n (h:t) acc = go (n-1) t (h acc)
A bit more elegant is to use a standard function to take the max usable count of elements, and see if anything remains:
sumIfLenLe :: Num a => Int -> [a] -> Maybe a
sumIfLenLe n l
| null remains = Just $ sum relevant
| otherwise = Nothing
where (relevant, remains) = splitAt n l
...or, some equivalent forms,
sumIfLenLe n l = case splitAt n l of
(relevant, []) -> Just $ sum relevant
_ -> Nothing
or
sumIfLenLe n l
| (relevant, []) <- splitAt n l
= Just $ sum relevant
| otherwise = Nothing
Then you can just map that over the entire given list and gather all the result that succeeded.
import Data.Maybe (catMaybes)
sumsOf :: Num a => [[a]] -> [a]
sumsOf = catMaybes . map (sumIfLe 2)