Haskell Language
traversable
खोज…
परिचय
Traversable
वर्ग generalises समारोह पूर्व के रूप में जाना mapM :: Monad m => (a -> mb) -> [a] -> m [b]
के साथ काम करने के लिए Applicative
सूचियों के अलावा अन्य संरचनाओं पर प्रभाव।
ट्रैवर्सिबल संरचना के लिए तत्काल फंक्शनल और फोल्डेबल
import Data.Traversable as Traversable
data MyType a = -- ...
instance Traversable MyType where
traverse = -- ...
हर Traversable
संरचना एक बनाया जा सकता है Foldable
Functor
का उपयोग कर fmapDefault
और foldMapDefault
में पाया कार्यों Data.Traversable
।
instance Functor MyType where
fmap = Traversable.fmapDefault
instance Foldable MyType where
foldMap = Traversable.foldMapDefault
fmapDefault
Identity
एप्लिकेशन fmapDefault
में traverse
चलाकर परिभाषित किया गया है।
newtype Identity a = Identity { runIdentity :: a }
instance Applicative Identity where
pure = Identity
Identity f <*> Identity x = Identity (f x)
fmapDefault :: Traversable t => (a -> b) -> t a -> t b
fmapDefault f = runIdentity . traverse (Identity . f)
foldMapDefault
का उपयोग कर परिभाषित किया गया है Const
अनुप्रयोगी functor है, जबकि एक monoidal मूल्य जमा है जो अपनी पैरामीटर ध्यान नहीं देता।
newtype Const c a = Const { getConst :: c }
instance Monoid m => Applicative (Const m) where
pure _ = Const mempty
Const x <*> Const y = Const (x `mappend` y)
foldMapDefault :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
foldMapDefault f = getConst . traverse (Const . f)
बाइनरी ट्री के लिए ट्रैवर्सेबल का एक उदाहरण
के क्रियान्वयन traverse
आमतौर पर के एक कार्यान्वयन की तरह लग रहे fmap
एक में पहुंचाया Applicative
संदर्भ।
data Tree a = Leaf
| Node (Tree a) a (Tree a)
instance Traversable Tree where
traverse f Leaf = pure Leaf
traverse f (Node l x r) = Node <$> traverse f l <*> f x <*> traverse f r
यह कार्यान्वयन पेड़ के इन-ऑर्डर ट्रैवर्सल को निष्पादित करता है।
ghci> let myTree = Node (Node Leaf 'a' Leaf) 'b' (Node Leaf 'c' Leaf)
-- +--'b'--+
-- | |
-- +-'a'-+ +-'c'-+
-- | | | |
-- * * * *
ghci> traverse print myTree
'a'
'b'
'c'
DeriveTraversable
एक्सटेंशन GHC को टाइप की संरचना के आधार पर Traversable
इंस्टेंस उत्पन्न करने की अनुमति देता है। हम Node
कंस्ट्रक्टर के लेआउट को समायोजित करके मशीन-लिखित ट्रैवर्सल के क्रम को भिन्न कर सकते हैं।
data Inorder a = ILeaf
| INode (Inorder a) a (Inorder a) -- as before
deriving (Functor, Foldable, Traversable) -- also using DeriveFunctor and DeriveFoldable
data Preorder a = PrLeaf
| PrNode a (Preorder a) (Preorder a)
deriving (Functor, Foldable, Traversable)
data Postorder a = PoLeaf
| PoNode (Postorder a) (Postorder a) a
deriving (Functor, Foldable, Traversable)
-- injections from the earlier Tree type
inorder :: Tree a -> Inorder a
inorder Leaf = ILeaf
inorder (Node l x r) = INode (inorder l) x (inorder r)
preorder :: Tree a -> Preorder a
preorder Leaf = PrLeaf
preorder (Node l x r) = PrNode x (preorder l) (preorder r)
postorder :: Tree a -> Postorder a
postorder Leaf = PoLeaf
postorder (Node l x r) = PoNode (postorder l) (postorder r) x
ghci> traverse print (inorder myTree)
'a'
'b'
'c'
ghci> traverse print (preorder myTree)
'b'
'a'
'c'
ghci> traverse print (postorder myTree)
'a'
'c'
'b'
रिवर्स में एक संरचना का पता लगाना
Backwards
एप्लिकेटर फन्नेटर की मदद से एक विपरीत दिशा में एक ट्रावर्सल चलाया जा सकता है, जो एक मौजूदा ऐप्लिकेटर को फ़्लिप करता है ताकि उल्टे क्रम में संयोजित प्रभाव उत्पन्न हो।
newtype Backwards f a = Backwards { forwards :: f a }
instance Applicative f => Applicative (Backwards f) where
pure = Backwards . pure
Backwards ff <*> Backwards fx = Backwards ((\x f -> f x) <$> fx <*> ff)
Backwards
एक "उल्टा traverse
" में उपयोग करने के लिए रखा जा सकता है। जब किसी traverse
कॉल के अंतर्निहित एप्लिकेशन को Backwards
से फ़्लिप किया जाता है, तो परिणामस्वरूप प्रभाव रिवर्स ऑर्डर में होता है।
newtype Reverse t a = Reverse { getReverse :: t a }
instance Traversable t => Traversable (Reverse t) where
traverse f = fmap Reverse . forwards . traverse (Backwards . f) . getReverse
ghci> traverse print (Reverse "abc")
'c'
'b'
'a'
Reverse
newtype Data.Functor.Reverse के अंतर्गत पाया जाता है।
ट्रैवर्सेबल की परिभाषा
class (Functor t, Foldable t) => Traversable t where
{-# MINIMAL traverse | sequenceA #-}
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
sequenceA :: Applicative f => t (f a) -> f (t a)
sequenceA = traverse id
mapM :: Monad m => (a -> m b) -> t a -> m (t b)
mapM = traverse
sequence :: Monad m => t (m a) -> m (t a)
sequence = sequenceA
Traversable
संरचनाओं t
हैं finitary कंटेनर तत्वों के a
है जो एक effectful "आगंतुक" ऑपरेशन के साथ पर संचालित किया जा सकता। आगंतुक समारोह f :: a -> fb
प्रदर्शन एक संरचना और के प्रत्येक तत्व पर दुष्प्रभाव के traverse
का उपयोग करने वालों दुष्प्रभाव तैयार करता Applicative
। यह देखने का एक और तरीका है कि है sequenceA
कहते Traversable
संरचनाओं के साथ निकल Applicative
रों।
एक संचित पैरामीटर की सहायता से एक ट्रैवर्सेबल संरचना को बदलना
दो mapAccum
फ़ंक्शंस फोल्डिंग और मैपिंग के संचालन को जोड़ती है।
-- A Traversable structure
-- |
-- A seed value |
-- | |
-- |-| |---|
mapAccumL, mapAccumR :: Traversable t => (a -> b -> (a, c)) -> a -> t b -> (a, t c)
-- |------------------| |--------|
-- | |
-- A folding function which produces a new mapped |
-- element 'c' and a new accumulator value 'a' |
-- |
-- Final accumulator value
-- and mapped structure
ये फ़ंक्शंस सामान्यीकृत fmap
हैं कि वे मैप किए गए मानों को इस बात पर निर्भर करने की अनुमति देते हैं कि गुना में पहले क्या हुआ है। वे foldl
/ foldr
को सामान्यीकृत foldl
कि वे संरचना में जगह बनाने के साथ-साथ इसे एक मूल्य तक कम कर देते हैं।
उदाहरण के लिए, tails
को mapAccumR
का उपयोग करके लागू किया जा सकता है और इसकी बहन inits
को mapAccumL
का उपयोग करके लागू किया जा सकता है।
tails, inits :: [a] -> [[a]]
tails = uncurry (:) . mapAccumR (\xs x -> (x:xs, xs)) []
inits = uncurry snoc . mapAccumL (\xs x -> (x `snoc` xs, xs)) []
where snoc x xs = xs ++ [x]
ghci> tails "abc"
["abc", "bc", "c", ""]
ghci> inits "abc"
["", "a", "ab", "abc"]
mapAccumL
को State
mapAccumL
में ट्रैवर्सिंग द्वारा कार्यान्वित किया जाता है।
{-# LANGUAGE DeriveFunctor #-}
newtype State s a = State { runState :: s -> (s, a) } deriving Functor
instance Applicative (State s) where
pure x = State $ \s -> (s, x)
State ff <*> State fx = State $ \s -> let (t, f) = ff s
(u, x) = fx t
in (u, f x)
mapAccumL f z t = runState (traverse (State . flip f) t) z
mapAccumR
रिवर्स में mapAccumL
चलाकर काम करता है।
mapAccumR f z = fmap getReverse . mapAccumL f z . Reverse
सामग्री के साथ आकृतियों के रूप में ट्रैवर्सेबल संरचनाएं
यदि टाइप t
Traversable
तो ta
मानों को दो टुकड़ों में विभाजित किया जा सकता है: उनका "आकार" और उनका "कंटेंट":
data Traversed t a = Traversed { shape :: t (), contents :: [a] }
जहाँ "सामग्री" वही है जो आप Foldable
उदाहरण का उपयोग करके "विज़िट" करते हैं।
एक ही दिशा जा रहे हैं, से ta
करने के लिए Traversed ta
कुछ भी लेकिन आवश्यकता नहीं है Functor
और Foldable
break :: (Functor t, Foldable t) => t a -> Traversed t a
break ta = Traversed (fmap (const ()) ta) (toList ta)
लेकिन पीछे जाने से महत्वपूर्ण रूप से traverse
फ़ंक्शन का उपयोग किया traverse
है
import Control.Monad.State
-- invariant: state is non-empty
pop :: State [a] a
pop = state $ \(a:as) -> (a, as)
recombine :: Traversable t => Traversed t a -> t a
recombine (Traversed s c) = evalState (traverse (const pop) s) c
Traversable
कानूनों को break . recombine
आवश्यकता होती है break . recombine
और recombine . break
दोनों पहचान हैं। विशेष रूप से, इसका मतलब यह है कि contents
में सही संख्या तत्व हैं जो पूरी तरह से बिना किसी बाएं ओवर के shape
भरने के लिए contents
।
Traversed t
Traversable
ही है। के कार्यान्वयन के traverse
की सूची के उदाहरण का उपयोग करते हुए तत्वों पर जाकर काम करता है Traversable
और उसके बाद परिणाम को निष्क्रिय आकार reattaching।
instance Traversable (Traversed t) where
traverse f (Traversed s c) = fmap (Traversed s) (traverse f c)
सूचियों की एक सूची को प्रेषित करना
यह देखते हुए कि zip
टपल की सूची में एक सूची का tuple स्थानांतरित करता है,
ghci> uncurry zip ([1,2],[3,4])
[(1,3), (2,4)]
और transpose
और sequenceA
के प्रकारों के बीच समानता,
-- transpose exchanges the inner list with the outer list
-- +---+-->--+-+
-- | | | |
transpose :: [[a]] -> [[a]]
-- | | | |
-- +-+-->--+---+
-- sequenceA exchanges the inner Applicative with the outer Traversable
-- +------>------+
-- | |
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
-- | |
-- +--->---+
विचार उपयोग करने के लिए है []
के Traversable
और Applicative
तैनात करने के लिए संरचना sequenceA
n-ary का एक प्रकार के रूप में zip
, एक साथ सभी आंतरिक सूचियों को एक साथ ज़िप करने pointwise।
[]
's डिफ़ॉल्ट "प्राथमिकता वाला विकल्प" Applicative
उदाहरण हमारे उपयोग के लिए उपयुक्त नहीं है - हमें "zippy" Applicative
। इसके लिए हम Control.Applicative
में पाए जाने वाले ZipList
newtype का उपयोग करते हैं।
newtype ZipList a = ZipList { getZipList :: [a] }
instance Applicative ZipList where
pure x = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith ($) fs xs)
अब हम मिल transpose
मुक्त करने के लिए, में traversing द्वारा ZipList
Applicative
।
transpose :: [[a]] -> [[a]]
transpose = getZipList . traverse ZipList
ghci> let myMatrix = [[1,2,3],[4,5,6],[7,8,9]]
ghci> transpose myMatrix
[[1,4,7],[2,5,8],[3,6,9]]