Использование функции карты Haskell для вычисления суммы списка

Хаскелл

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

я смог добиться, чтобы получить сумму списка, используя sum функция, но можно ли получить сумму списка, используя

8 ответов


вы не можете использовать map чтобы суммировать список, потому что map обрабатывает каждый элемент списка независимо от других. Вы можете использовать map например, чтобы увеличить каждое значение в списке, как в

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

другой способ реализовать ваш addm будет использовать foldl:

addm' = foldl (+) 0

вот оно, якобы невозможное определение sum С точки зрения map:

summ xs = let ys = 0 : map (\(a,b)->a+b) (zip xs ys) in last ys

это на самом деле показывает, как scanl может быть реализован в условиях map, что эквивалентно foldl (+) 0 xs === last $ scanl (+) 0 xs:

scannl f z xs = let ys = z : map (uncurry f) (zip ys xs) in ys

я ожидаю, что можно рассчитать много вещей с map, организацию всех видов информационного потока.

edit: это просто zipWith в маскировке конечно (и zipWith - это своего рода map2):

summ xs = let ys = 0 : zipWith (+) ys xs in last ys

это, кажется, предполагает, что scanl является более фундаментальным, чем foldl, по крайней мере, с этого угла.


невозможно использовать map чтобы уменьшить список до его суммы. Этот рекурсивный шаблон является fold.

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

кроме того, обратите внимание, что вы можете определить map как складка, а также:

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

это так foldr является канонической рекурсивной функцией в списках.


ссылки: учебник по универсальности и выразительности раза, Graham Hutton, J. Функциональное Программирование 9 (4): 355-372, Июль 1999.


после Какие выводы я должен добавить еще один ответ: Вы не можете получить список с map, но вы можете получить сумму с ее монадической версией mapM. Все, что вам нужно сделать, это использовать Writer монады (см. LYAHFGG) за Sum моноидом (см. LYAHFGG).

я написал специализированную версию, которую, наверное, легче понять:

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 это просто обертка вокруг какого-то типа, который также держит " работает сумма."Мы можем сделать Adder монада, и вот она делает какую-то работу: когда операция >>= (a.к. a." bind") выполняется, он возвращает новый результат и значение текущей суммы этого результата плюс исходная текущая сумма. The toAdder функция принимает Int и создает Adder это содержит этот аргумент как обернутое значение, так и как текущая сумма (на самом деле нас не интересует значение, а только часть суммы). Потом в sum' mapM может сделать свое волшебство: пока оно работает похожие на map для значений, встроенных в монаду, он выполняет "монадические" функции, такие как toAdder и цепи эти вызовы (он использует sequence для этого). На данный момент мы получаем через "бэкдор" нашей монады взаимодействие между элементами списка, которые стандарт map отсутствует.


Map "maps" каждый элемент вашего списка к элементу в вашем выходе:

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

это вернет список квадратов.

чтобы суммировать все элементы в списке, используйте fold:

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

+ - это функция, которую вы хотите применить, а 0-начальное значение (0 для суммы, 1 для продукта и т. д.)


как указывают другие ответы, "нормальный" способ-использовать один из fold функции. Однако можно написать что-то очень похожее на while цикл на императивных языках:

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

Он добавляет первые два элемента списка вместе, пока не закончится список из одного элемента ,и возвращает это значение (используя head).


Я понимаю, что на этот вопрос был дан ответ, но я хотел добавить эту мысль...

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

Я полагаю, это возвращает константу 1 для каждого элемента в списке, и возвращает сумму! Возможно, это не лучшая практика кодирования, но это был пример, который мой профессор дал нам, студентам, который, похоже, хорошо относится к этому вопросу.


map никогда не может быть основным инструментом для суммирования элементов контейнера, точно так же, как отвертка никогда не может быть основным инструментом для просмотра фильма. Но вы можете использовать отвертку, чтобы починить кинопроектор. Если вы действительно хотите, вы можете написать

import Data.Monoid
import Data.Foldable

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

конечно, это глупо. Вы можете получить более общую и, возможно, более эффективную версию:

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

или лучше, просто использовать sum, потому что это действительно сделано для работы.