2016-11-28 6 views
4

最近の課題では、リスト以外のタイプの折り畳み関数を定義するように求められています。私はまだこのコンセプトの周りを頭で覆うことはできません。これまでのところ、私はfoldlistの後続の要素に対して実行していると理解しています。 fold on Treeは、ルートのサブツリーに対して再帰的にいくつかの関数を適用できるので、直感的に理解できます。非リストに表示されますか?

しかし、のようなデータ型に:(それは私には思えるよう)の倍のアクションを実行するために

Maybe a :: Nothing | Just a 

listはありません。

ここでは基本的な概念を理解する上で問題があると確信しています。

+4

ヒント:ある意味では、「たぶん」は0または1の要素のリストと考えることができ、それらのそれぞれについて「fold」*が明確に定義されています。 –

+0

これをより明示的にするために、Foldableをリストに変換することができることに注意してください。 'toList <$> [Nothing、Just True] == [[]、[True]]' –

+3

実際に実装する必要がある「折りたたみ」の種類は不思議です。ハスケルでは、 'foldable'がデータ型を指定してリストに相当する' Foldable'クラスを持っています。そして、結果のリストに 'foldr'を使います。他の文脈では、 "fold"は 'recursion-schemes'の' cata'のように_eliminator_のための用語です。私の最初のCS試験では、口頭テストの際に「二分木の折り畳み」を作るように求められました。それは「カタ」のようなものでした。 – chi

答えて

6

Foldableは非常に混乱しやすいクラスです。正直なところ、多くの法律がないため、ほぼすべての種類の異なるFoldableインスタンスをかなり書くことが可能です。幸いなことに、同じタイプのがある場合はTraversableインスタンスに基づいて、Foldableインスタンスが純粋に機械的な方法で何をすべきかを理解することができます。私たちは、

class (Functor t, Foldable t) => Traversable t where 
    traverse :: Applicative f => (a -> f b) -> t a -> f (t b) 

Traversableを持って

は、いくつかの異なる法律がありますが、それは最も重要なものはtraverse Identity = Identityで判明します。のは、これがMaybeに適用される方法を見てみましょう:

traverse :: Applicative f => (a -> f b) -> Maybe a -> f (Maybe b) 
traverse g Nothing = _none 
traverse g (Just a) = _some 

は今最初のケースでは、あなたはf (Maybe b)を生成する必要がある、とあなたが持っているすべてがg :: a -> f bです。 fの値がなく、aの値がないため、生成できるのはpure Nothingです。

2番目のケースでは、f (Maybe b)を生成し、g :: a -> f baが必要です。だから興味深いのは、gaに適用してg a :: f bにすることです。これで、この値を捨ててNothingを返すか、Justにまとめて返すことができます。

同一性法により、traverse Identity (Just a) = Identity (Just a)。あなたはNothingを返すことができません。 のみ法的定義はMaybeためTraversableインスタンスが完全にTraversable法律やparametricityによって決定され

traverse _ Nothing = pure Nothing 
traverse g (Just a) = Just <$> g a 

です。これはMaybeに適用されるとして、

foldMapDefault :: (Traversable t, Monoid m) 
       => (a -> m) -> t a -> m 
foldMapDefault f xs = 
    getConst (traverse (Const . f) xs) 

私たちの定義を拡大
foldMapDefault f Nothing = 
    getConst (traverse (Const . f) Nothing) 
foldMapDefault f (Just a) = 
    getConst (traverse (Const . f) (Just a)) 

pure<$>の定義によって

foldMapDefault f Nothing = getConst (pure Nothing) 
foldMapDefault f (Just a) = getConst (Just <$> (Const (f a))) 

今ではtraverseを使用して折り畳むことが可能です0の場合は、これらのコンストラクタをアンラップ

foldMapDefault f Nothing = getConst (Const mempty) 
foldMapDefault f (Just a) = getConst (Const (f a)) 

foldMapDefault f Nothing = mempty 
foldMapDefault f (Just a) = f a 

されており、これはfoldMapMaybeに定義されている正確にどのように確かにあります。

+1

これは多分OPのための少し高いレベルのようです。とにかく良い答えですが、ちょっとしたフィードバックです。 – luqui

3

"基本的なコンセプト"として、これはかなり心地よい曲ですので、あまり気にしないでください。

それはさておき倍がリストに何をするかについてのあなたの直感を設定し、タイプ特定の折りたたみ機能(のはfoldrを使ってみましょうが)Maybeに適用された場合持っているべきかを考えるのに役立つことがあります。

foldr :: (a -> b -> b) -> b -> List a -> b 

もちろん、多分持っている必要がありますタイプに対応倍:

foldrMaybe :: (a -> b -> b) -> b -> Maybe a -> b 

は何の定義について考え[a]の代わりにList aを書くことは、それをより明確にするために、リスト上のfoldrの標準タイプがありますタイプについて他に何も知らずにすべてabに定義する必要があることを考えれば、これは可能性があります。さらにヒントとして、類似のタイプのData.Maybeに既に定義されている関数があるかどうかを確認してください。

+2

私は 'foldMaybe'がこのタイプを持つべきであることは明らかではないと思います - ' b->(a-> b) - >多分a-> b'は ' foldList'はリストの自然エリミネータに(ほぼ)対応し、 'b - >(a - > b) - >おそらくa - > b'は' Maybe 'の自然なエリミネータです - 実際このタイプの関数があります'Data.Maybe'(' maybe')。このタイプの 'foldMaybe'はありません。もっと一般的な型を持つ' Data.Foldable.foldr'を除いて(この関数の型は 'Maybe'や' List'とほとんど関係がありません) – user2407038

+0

Goodしかし、明確にするためには、 'foldr'がMaybesの型をリストの型と明白に平行にするべきであることは明らかでした。私はそれをより明確にする答えを編集しました。実際、 'Data.Foldable.foldr'はHaskellの現在のバージョンでは' Prelude.foldr'であり、 'foldr'がaの"自然な "折り畳みではないとしても、その型はリストやMaybesに特化します多分。 –

関連する問題