9

Haskell

addm::[Int]->Int
addm (x:xs) = sum(x:xs)

I was able to achieve to get a sum of a list using sum function but is it possible to get the sum of a list using map function? Also what the use of map function?

Obito
  • 391
  • 3
  • 8
Sudantha
  • 15,684
  • 43
  • 105
  • 161

8 Answers8

23

You can't really use map to sum up a list, because map treats each list element independently from the others. You can use map for example to increment each value in a list like in

map (+1) [1,2,3,4] -- gives [2,3,4,5]

Another way to implement your addm would be to use foldl:

addm' = foldl (+) 0
Waldheinz
  • 10,399
  • 3
  • 31
  • 61
6

Here it is, the supposedly impossible definition of sum in terms of map:

sum' xs  =  let { ys = 0 : map (\(a,b) -> a + b) (zip xs ys) } in last ys

this actually shows how scanl can be implemented in terms of map (and zip and last), the above being equivalent to foldl (+) 0 xs === last $ scanl (+) 0 xs:

scanl' f z xs  =  let { ys = z : map (uncurry f) (zip ys xs) } in ys

I expect one can calculate many things with map, arranging for all kinds of information flow through zip.

edit: the above is just a zipWith in disguise of course (and zipWith is kind of a map2):

sum' xs  =  let { ys = 0 : zipWith (+) ys xs } in last ys

This seems to suggest that scanl is more versatile than foldl.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • 2
    Ok, it is possible, but I don't think that's idiomatic Haskell. But +1 because technically you're right. :-) – Waldheinz Aug 14 '12 at 12:15
  • 2
    I think you're cheating here, Will. Basically, I read the question as asking whether you can use the `Functor []` instance to calculate the length of a list; and your answer is that you can use an `Applicative []` instance to do so (basically `ZipList`). I realize I'm "enriching" the question (so to speak) by reading `Functor` into it, but by that argument you are enriching it more than I am, since `Applicative` is more powerful than `Functor`. – Luis Casillas Aug 28 '12 at 17:38
  • @LuisCasillas the point of this answer is that lists are not sets I guess; their structure (items' positions; first/last element) is perfectly visible; `map` is not just `fmap`. IOW `instance Functor []` talks of lists as sets, since only `fmap` is available &nothing else. with sets there's only membership, no position, so there's no order. we can very well define `Applicative` for lists-as-sets too, with cartesian product the only possible implementation - precisely because there's no concept of position. But with it, inner product becomes another possibility (i.e. `zip`, i.e. `ZipList`). – Will Ness Aug 26 '15 at 22:46
  • lists are [indexed families](https://en.wikipedia.org/wiki/Indexed_family), it seems, or [sequences](https://en.wikipedia.org/wiki/Sequence). If you accept this, you can't object to the usage of `zip`. If you do object, that means that you treat lists as non-sequenced, unordered collections. – Will Ness Aug 27 '15 at 00:11
  • another thing is, the OP isn't saying that `map` is the only function that is allowed. So yes, this answer uses `map`, `zip` and `last`, in effect implementing `sum = foldl (+) 0`, but still, it uses `map` in a non-trivial manner, to accomplish this. – Will Ness Dec 10 '16 at 18:54
5

After some insights I have to add another answer: You can't get the sum of a list with map, but you can get the sum with its monadic version mapM. All you need to do is to use a Writer monad (see LYAHFGG) over the Sum monoid (see LYAHFGG).

I wrote a specialized version, which is probably easier to understand:

data Adder a = Adder a Int

instance Monad Adder where
  return x = Adder x 0
  (Adder x s) >>= f = let Adder x' s' = f x
                      in Adder x' (s + s') 

toAdder x = Adder x x

sum' xs = let Adder _ s = mapM toAdder xs in s  

main = print $ sum' [1..100]
--5050

Adder is just a wrapper around some type which also keeps a "running sum." We can make Adder a monad, and here it does some work: When the operation >>= (a.k.a. "bind") is executed, it returns the new result and the value of the running sum of that result plus the original running sum. The toAdder function takes an Int and creates an Adder that holds that argument both as wrapped value and as running sum (actually we're not interested in the value, but only in the sum part). Then in sum' mapM can do its magic: While it works similar to map for the values embedded in the monad, it executes "monadic" functions like toAdder, and chains these calls (it uses sequence to do this). At this point, we get through the "backdoor" of our monad the interaction between list elements that the standard map is missing.

Obito
  • 391
  • 3
  • 8
Landei
  • 54,104
  • 13
  • 100
  • 195
5

It is not possible to use map to reduce a list to its sum. That recursive pattern is a fold.

sum :: [Int] -> Int
sum = foldr (+) 0

As an aside, note that you can define map as a fold as well:

map :: (a -> b) -> ([a] -> [b])
map f = fold (\x xs -> f x : xs) []

This is because foldr is the canonical recursive function on lists.


References: A tutorial on the universality and expressiveness of fold, Graham Hutton, J. Functional Programming 9 (4): 355–372, July 1999.

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
2

Map "maps" each element of your list to an element in your output:

let f(x) = x*x
map f [1,2,3]

This will return a list of the squares.

To sum all elements in a list, use fold:

foldl (+) 0 [1,2,3]

+ is the function you want to apply, and 0 is the initial value (0 for sum, 1 for product etc)

Frank Schmitt
  • 30,195
  • 12
  • 73
  • 107
1

I realize this question has been answered, but I wanted to add this thought...

listLen2 :: [a] -> Int
listLen2 = sum . map (const 1)

I believe it returns the constant 1 for each item in the list, and returns the sum! Might not be the best coding practice, but it was an example my professor gave to us students that seems to relate to this question well.

1

map can never be the primary tool for summing the elements of a container, in much the same way that a screwdriver can never be the primary tool for watching a movie. But you can use a screwdriver to fix a movie projector. If you really want, you can write

import Data.Monoid
import Data.Foldable

mySum :: (Foldable f, Functor f, Num a)
      => f a -> a
mySum = getSum . fold . fmap Sum

Of course, this is silly. You can get a more general, and possibly more efficient, version:

mySum' :: (Foldable f, Num a) => f a -> a
mySum' = getSum . foldMap Sum

Or better, just use sum, because its actually made for the job.

dfeuer
  • 48,079
  • 5
  • 63
  • 167
1

As the other answers point out, the "normal" way is to use one of the fold functions. However it is possible to write something pretty similar to a while loop in imperative languages:

sum' [] = 0
sum' xs = head $ until single loop xs where 
   single [_] = True
   single _ = False
   loop (x1 : x2 : xs) = (x1 + x2) : xs 

It adds the first two elements of the list together until it ends up with a one-element list, and returns that value (using head).

Landei
  • 54,104
  • 13
  • 100
  • 195