Haskell
addm::[Int]->Int
addm (x:xs) = sum(x:xs)
sum
関数を使用してリストの合計を取得することはできましたが、関数を使用してリストの合計を取得することはできmap
ますか?また、マップ機能の用途は何ですか?
map
mapは各リスト要素を他の要素から独立して扱うため、実際にリストを合計するために使用することはできません。map
たとえば、次のようにリスト内の各値をインクリメントするために使用できます。
map (+1) [1,2,3,4] -- gives [2,3,4,5]
addmを実装する別の方法は、foldlを使用することです。
addm' = foldl (+) 0
これが、おそらく不可能な定義sum
ですmap
。
sum' xs = let { ys = 0 : map (\(a,b) -> a + b) (zip xs ys) } in last ys
これは実際に(とと)scanl
の観点からどのように実装できるかを示しています。上記は:と同等です。map
zip
last
foldl (+) 0 xs === last $ scanl (+) 0 xs
scanl' f z xs = let { ys = z : map (uncurry f) (zip ys xs) } in ys
を使って多くのことを計算できmap
、あらゆる種類の情報が流れるように調整できると思いzip
ます。
編集:上記は zipWith
もちろん変装です(そしてzipWith
一種のmap2
):
sum' 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。Functional Programming 9(4):355–372、1999年7月。
いくつかの洞察の後、私は別の答えを追加する必要があります:でリストの合計を取得することはできませんが、map
そのモナディックバージョンで合計を取得することはできますmapM
。あなたがする必要があるのは、モノイド(LYAHFGGを参照)の上にWriter
モナド(LYAHFGGを参照)を使用することです。Sum
私は特別なバージョンを書きましたが、それはおそらく理解しやすいでしょう:
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
モナドを作成できます。ここでいくつかの作業を行います。操作>>=
(別名「バインド」)が実行されると、新しい結果と、その結果の現在の合計の値に元の現在の合計を加えた値が返されます。このtoAdder
関数はIntを受け取り、Adder
その引数をラップされた値と実行中の合計の両方として保持するを作成します(実際には、値には関心がなく、合計部分のみに関心があります)。次に、insum'
mapM
はその魔法を実行できます。map
モナドに埋め込まれた値の場合と同様に機能しますが、のような「モナド」関数を実行し、これらの呼び出しtoAdder
をチェーンします(sequence
これをする)。この時点で、モナドの「バックドア」を通過して、標準map
が欠落しているリスト要素間の相互作用を取得します。
リストの各要素を出力の要素に「マップ」します。
let f(x) = x*x
map f [1,2,3]
これにより、正方形のリストが返されます。
リスト内のすべての要素を合計するには、foldを使用します。
foldl (+) 0 [1,2,3]
+は適用する関数、0は初期値(合計は0、製品は1など)
他の回答が指摘しているように、「通常の」方法はfold
関数の1つを使用することです。while
ただし、命令型言語でループに非常によく似たものを書くことは可能です。
sum' [] = 0
sum' xs = head $ until single loop xs where
single [_] = True
single _ = False
loop (x1 : x2 : xs) = (x1 + x2) : xs
リストの最初の2つの要素を、1つの要素のリストになるまで追加し、その値を返します(を使用して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
、実際に仕事のために作られたので、を使用することをお勧めします。