Использование функции карты 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
, потому что это действительно сделано для работы.