よくやった、foldr
あなたは自分で発見した!(嘲笑などに聞こえないことを願っています。そのような意味ではありません。ほとんどの人は折り目が不自然であり、それを理解するのに非常に一生懸命考えなければなりません!)
このような状況に対処する方法として、自分で必要な関数を作成し、その型を見つけてから、 Hoogleでその型を検索して、そのような関数が既に存在するかどうかを確認することをお勧めします。
この場合、この方法で関数を記述してみてください。私たちはそれを呼びますfoo
:
-- If we see an empty list the result should be u
foo u f [] = u
-- If we're given a a non-empty list we recurse down the list to get a partial
-- result, then "add on" to it:
foo u f (x:xs) = f x (foo u f xs)
この関数を定義したら、それを にロードし、そのコマンドをghci
使用してその型を見つけることができます。:t
*Main> :load "../src/scratch.hs"
[1 of 1] Compiling Main ( ../src/scratch.hs, interpreted )
Ok, modules loaded: Main.
*Main> :t foo
foo :: t1 -> (t -> t1 -> t1) -> [t] -> t1
これで、Hoogle で type を検索t1 -> (t -> t1 -> t1) -> [t] -> t1
できます。一番上の結果はfoldr
で、型は(a -> b -> b) -> b -> [a] -> b
と同じfoo
ですが、変数の名前が変更され、引数の順序が反転しています。検索結果は、関数がPrelude
モジュール (デフォルトで Haskell によって読み込まれるモジュール) 内にあることも示しています。結果をクリックすると、ドキュメンテーションでその定義を見つけることができます。ドキュメントでは、次の式で説明されています。
foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)
彼らはf
関数を中置演算子として使用していますが、混乱しないことを願っていますが、念のためこれを次のように書き換えることができます。
foldr f z [x1, x2, ..., xn] == f x1 (f x2 ... (f xn z) ...)
これはまさにあなたが望む動作です。
なぜ私は上からそんなに大したことをしたのfoldr
ですか?foldr
実際には、リストを分解するための最も基本的な機能だからです。このようにタイプを見てください:
foldr :: (a -> b -> b) -- ^ What to do with a list node
-> b -- ^ What to do with the empty list
-> [a] -- ^ A list
-> b -- ^ The final result of "taking the list apart."
多くのリスト関数は、次のように簡単に記述できることがわかりましたfoldr
。
map f = foldr step []
where step x rest = (f x):rest
-- | Append two lists
xs (++) ys = foldr (:) ys xs
-- | Filter a list, keeping only elements that satisfy the predicate.
filter pred = foldr step []
where step x rest | pred x = x:rest
| otherwise = rest
-- | Find the first element of a list that satisfies the predicate.
find pred = foldr step Nothing
where step x r | pred x = Just x
| otherwise = r