Home > Net >  How can I sum of a list of list when the length is under 3 long for endless lists?
How can I sum of a list of list when the length is under 3 long for endless lists?

Time:06-27

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)
  • Related