回答:
traversefmapはと同じですが、データ構造を再構築しているときにエフェクトを実行することもできます。
Data.Traversableドキュメントの例をご覧ください。
data Tree a = Empty | Leaf a | Node (Tree a) a (Tree a)
のFunctorインスタンスは次のようにTreeなります。
instance Functor Tree where
fmap f Empty = Empty
fmap f (Leaf x) = Leaf (f x)
fmap f (Node l k r) = Node (fmap f l) (f k) (fmap f r)
fすべての値に適用して、ツリー全体を再構築します。
instance Traversable Tree where
traverse f Empty = pure Empty
traverse f (Leaf x) = Leaf <$> f x
traverse f (Node l k r) = Node <$> traverse f l <*> f k <*> traverse f r
Traversableコンストラクタは応用的なスタイルで呼ばれている以外のインスタンスは、ほぼ同じです。これは、ツリーの再構築中に(副次的な)影響がある可能性があることを意味します。適用はモナドとほとんど同じですが、効果が以前の結果に依存することはありません。この例では、たとえば、左のブランチを再構築した結果によっては、ノードの右のブランチとは異なる処理を実行できなかったことを意味します。
歴史的な理由により、このTraversableクラスには、のモナディックバージョンも含まtraverseれていmapMます。すべての意図と目的mapMは同じです。後でのみのスーパークラスになったtraverseため、別のメソッドとして存在します。ApplicativeMonad
不純な言語でこれを実装fmapする場合、traverse副作用を防ぐ方法がないため、と同じになります。データ構造を再帰的にトラバースする必要があるため、ループとして実装することはできません。これがJavaScriptでそれを行う方法の小さな例です:
Node.prototype.traverse = function (f) {
return new Node(this.l.traverse(f), f(this.k), this.r.traverse(f));
}
このように実装すると、言語が許可する効果に制限されます。非決定論(アプリケーションモデルのリストインスタンス)が必要で、言語に組み込まれていない場合は、運が悪いです。
Functorパラメトリックではないパーツの構造情報を意味します。状態の値Stateの故障、MaybeおよびEither、内の要素の数[]、及び内もちろん任意の外部の副作用IO。私はそれを一般的な用語としては気にしません(Monoid"空"と "追加"を使用する関数のように、概念はこの用語が最初に提案するよりも一般的です)。これはかなり一般的で、目的を十分に果たします。
ap以前の結果に依存させるのと同じくらい厄介です。私はそれに応じてその発言を言い換えました。
traverse内部に物事をオンTraversableにTraversable「内側」のもののApplicative、与えられた関数になり、そのApplicative事のsのうち。
Maybeas Applicativeとlistを使ってみましょうTraversable。まず、変換関数が必要です。
half x = if even x then Just (x `div` 2) else Nothing
そのため、数値が偶数の場合、その半分(a内Just)を取得し、それ以外の場合はを取得しNothingます。すべてがうまくいくと、次のようになります。
traverse half [2,4..10]
--Just [1,2,3,4,5]
だが...
traverse half [1..10]
-- Nothing
その理由は、<*>関数が結果を作成するために使用され、引数の1つがであるNothing場合にNothing戻るためです。
もう一つの例:
rep x = replicate x x
この関数xは、内容を含む長さのリストを生成しますx(例:rep 3=)[3,3,3]。の結果はtraverse rep [1..3]何ですか?
の部分的な結果を取得し[1]、[2,2]を[3,3,3]使用しrepます。リストのセマンティクスはそのままApplicativesで、「すべての組み合わせを取る」、たとえば(+) <$> [10,20] <*> [3,4]is [13,14,23,24]です。
「すべての組み合わせ」[1]とは、[2,2]2回です[1,2]。全2回の組み合わせ[1,2]と[3,3,3]6倍です[1,2,3]。だから私たちは:
traverse rep [1..3]
--[[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3]]
fac n = length $ traverse rep [1..n]
liftA2 (,)を使用したより一般的な形式よりも似ていtraverseます。
次のように定義できるのでsequenceA、の観点から理解するのが最も簡単だと思いtraverseます。
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
sequenceA 構造の要素を左から右に順番に並べ、結果を含む同じ形状の構造を返します。
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
sequenceA = traverse id
sequenceAアクションのリストから結果のリストを返すアクションに移動するなど、2つのファンクタの順序を逆にすることもできます。
したがってtraverse、いくつかの構造を取り、構造f内のすべての要素をいくつかのアプリケーションに変換するために適用され、それらのアプリケーションの効果を左から右に順序付け、結果を含む同じ形状の構造を返します。
Foldable関連する関数を定義すると比較することもできますtraverse_。
traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
あなたが主な違いことを見ることができるようにFoldableとは、Traversable前者が他の値に結果を畳むする必要があり、一方、後者は、あなたが構造物の形状を維持することを可能にするということです。
その使用法の簡単な例は、リストを通過可能な構造IOとして、およびアプリケーションとして使用することです。
λ> import Data.Traversable
λ> let qs = ["name", "quest", "favorite color"]
λ> traverse (\thing -> putStrLn ("What is your " ++ thing ++ "?") *> getLine) qs
What is your name?
Sir Lancelot
What is your quest?
to seek the holy grail
What is your favorite color?
blue
["Sir Lancelot","to seek the holy grail","blue"]
この例はややエキサイティングですが、traverse他のタイプのコンテナで使用したり、他のアプリケーションを使用したりすると、さらに興味深いものになります。
sequenceA . fmapリストはと同等でsequence . mapはありませんか?
IO タイプは副作用を表すために使用できます。(2)IOたまたまモナドになっているので、とても便利です。モナドは本質的に副作用にリンクされていません。また、通常の意味での「副作用」よりも広い「効果」の意味があることにも注意してください-純粋な計算を含むもの。この最後の点については、「効果的な」とは正確にはどういう意味かを参照してください。
fmap結果のタイプも変更するマッパー関数内でエフェクトを実行できることを除けば、のようなものです。
データベース内のユーザーIDを表す整数のリストを想像してください[1, 2, 3]。あなたがしたい場合はfmap、ユーザ名にこれらのユーザーIDには、伝統的に使用することはできませんfmap( -使用して、この場合の効果を必要とする関数の内部で使用すると、ユーザ名を読み取るためにデータベースにアクセスする必要があるため、IOモナドを)。
の署名traverseは次のとおりです。
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
を使用するとtraverse、効果を実行できるため、ユーザーIDをユーザー名にマッピングするためのコードは次のようになります。
mapUserIDsToUsernames :: (Num -> IO String) -> [Num] -> IO [String]
mapUserIDsToUsernames fn ids = traverse fn ids
と呼ばれる関数もありますmapM:
mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
の使用はmapMに置き換えることができますtraverseが、その逆はできません。mapMモナドでのみ機能しますが、traverseより一般的です。
効果を達成したいだけで有用な値を返さない場合はtraverse_、mapM_これらの関数のバージョンがあり、どちらも関数からの戻り値を無視し、わずかに高速です。
traverse あるループが。その実装は、トラバースされるデータ構造に依存します。これは、リスト、木、あるかもしれないMaybe、Seqforループや再帰関数のようなものを経由して横断しているの一般的な方法を持っている(uence)、または何か。配列には、forループ、whileループのリスト、再帰的なツリー、またはwhileループとスタックの組み合わせのいずれかが含まれます。しかし、関数型言語では、これらの面倒なループコマンドは必要ありません。ループの内部(関数の形)をデータ構造とより直接的かつ冗長ではなく結合します。
とTraversable型クラスは、おそらくより多くの独立した汎用性の高い、あなたのアルゴリズムを書くことができます。しかし、私の経験によれば、これTraversableは通常、アルゴリズムを既存のデータ構造に単に接着するためにのみ使用されます。修飾されたさまざまなデータ型に対して同様の関数を記述する必要がないことも非常に良いことです。