別の考え方 - 次の再帰パターンが頻繁に使用されるため、foldr が存在します。
-- Example 1: Sum up numbers
summa :: Num a => [a] -> a
summa [] = 0
summa (x:xs) = x + suma xs
数値の積を取得したり、リストを逆にしたりすることは、構造的に前の再帰関数と非常によく似ています。
-- Example 2: Reverse numbers
reverso :: [a] -> [a]
reverso [] = []
reverso (x:xs) = x `op` reverso xs
where
op = (\curr acc -> acc ++ [curr])
上記の例の構造は、最初の値と再帰呼び出しの間の演算子 (summa と reverso の場合) とともに、初期値 (summa と reverso の場合) のみが異なり0
ます。したがって、上記の例の関数構造は、一般的に次のように見ることができます。[]
+
(\q qs -> qs ++ [q])
-- Generic function structure
foo :: (a -> [a] -> [a]) -> [a] -> [a] -> [a]
foo op init_val [] = init_val
foo op init_val (x:xs) = x `op` foo op init_val xs
この「一般的な」foo が機能することを確認するために、foo を使用し、それに演算子、初期値、およびリスト自体を渡すことで、reverso を書き直すことができます。
-- Test: reverso using foo
foo (\curr acc -> acc ++ [curr]) [] [1,2,3,4]
foo により一般的な型シグネチャを与えて、他の問題でも機能するようにしましょう。
foo :: (a -> b -> b) -> b -> [a] -> b
さて、あなたの質問に戻ります - 次のようにフィルタを書くことができます:
-- Example 3: filter
filtero :: (a -> Bool) -> [a] -> [a]
filtero p [] = []
filtero p (x:xs) = x `filterLogic` (filtero p xs)
where
filterLogic = (\curr acc -> if (p curr) then curr:acc else acc)
これもsummaやreversoと非常によく似た構造を持っています。したがって、foo を使用して書き換えることができるはずです。リスト [1,2,3,4] から偶数をフィルタリングしたいとしましょう。次に、再び foo に演算子 (この場合はfilterLogic
)、初期値、およびリスト自体を渡します。filterLogic
この例ではp
、述語と呼ばれる関数を受け取ります。これは、呼び出し用に定義する必要があります。
let p = even in foo (\curr acc -> if (p curr) then curr:acc else acc) [] [1,2,3,4]
Haskell の foo はfoldrと呼ばれます。そこで、foldr を使用してフィルターを書き直しました。
let p = even in foldr (\curr acc -> if (p curr) then curr:acc else acc) [] [1,2,3,4]
したがって、filterは、これまで見てきたように、 foldrで記述できます。
-- Solution 1: filter using foldr
filtero' :: (a -> Bool) -> [a] -> [a]
filtero' p xs = foldr (\curr acc -> if (p curr) then curr:acc else acc) [] xs
mapに関しては、次のように書くこともできます
-- Example 4: map
mapo :: (a -> b) -> [a] -> [b]
mapo f [] = []
mapo f (x:xs) = x `op` (mapo f xs)
where
op = (\curr acc -> (f curr) : acc)
したがって、これはfolderrを使用して書き換えることができます。たとえば、リスト内のすべての数値に 2 を掛けるには、次のようにします。
let f = (* 2) in foldr (\curr acc -> (f curr) : acc) [] [1,2,3,4]
したがって、これまで見てきたようにmapはfolderrで書くことができます:
-- Solution 2: map using foldr
mapo' :: (a -> b) -> [a] -> [b]
mapo' f xs = foldr (\curr acc -> (f curr) : acc) [] xs