我具有以下结构:
y = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20)])
]
我可以用它来更新每个“ c”:
y & mapped . at "c" . mapped %~ (+ 1)
-- [fromList [("c",2)], fromList [("c",6)], fromList [("d",20)]]
因此,基本上仅忽略第三项。但我要的是使操作失败。
仅更新,如果所有映射都包含键“ c”。
所以我想要:
y & mysteryOp
-- [fromList [("c",1)], fromList [("c",5)], fromList [("d",20)]]
-- fail because third entry does not contain "c" as key
我想我知道在这里使用哪些功能:
over
-- I want to map the content of the list
mapped
-- map over the structure and transform to [(Maybe Int)]
traverse
-- I need to apply the operation, which will avoid
at "c"
-- I need to index into the key "c"
我只是不知道如何组合它们
我看不出有一种简单的镜头组合器来编写它的方法,但这是您可以从头开始编写的遍历。如果每个映射都包含"c"
键的所有值,则它应遍历所有键,否则应不遍历任何值。
我们可以从一个辅助函数开始,以“也许”用新的键值更新地图,如果键不存在,则失败Maybe
monad。出于显而易见的原因,我们希望允许在任意函子中进行更新。也就是说,我们需要一个函数:
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
该签名清楚吗?我们检查密钥k
。 If找到键,我们将返回Just
一个更新的映射,并在v
函子中更新键的对应值f
。 否则,如果找不到密钥,则返回Nothing
。尽管我们只想使用ApplicativeDo
约束,但我们需要Functor f
扩展名,但我们可以用monad表示法清楚地写出这一点:
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v)) maybeUpdate k f m = do -- in Maybe monad v <- m ^. at k return $ do -- in "f" functor a <- f v return $ m & at k .~ Just a
或者,这些“单子动作”实际上只是函子动作,因此可以使用此定义:
maybeUpdate' k f m = m ^. at k <&> \v -> f v <&> \a -> m & at k .~ Just a
这是最困难的部分。现在,遍历非常简单。我们从签名开始:
traverseAll :: (Ord k) => k -> Traversal' [Map k v] v traverseAll k f maps =
[这个想法是通过使用
Maybe
助手在maybeUpdate
应用程序上遍历地图列表开始的:
traverse (maybeUpdate k f) maps :: Maybe [f (Map k v)]
如果此遍历成功(返回
Just
一个列表),则找到所有键,然后我们可以对f
应用动作进行排序:
sequenceA <$> traverse (maybeUpdate k f) maps :: Maybe (f [Map k v])
现在,如果遍历失败,我们仅使用
maybe
返回原始列表:
traverseAll k f maps = maybe (pure maps) id (sequenceA <$> traverse (maybeUpdate k f) maps)
现在,带有:
y :: [Map String Int] y = [ fromList([("c", 1 ::Int)]), fromList([("c", 5)]), fromList([("d", 20)]) ] y2 :: [Map String Int] y2 = [ fromList([("c", 1 ::Int)]), fromList([("c", 5)]), fromList([("d", 20),("c",6)]) ]
我们有:
> y & traverseAll "c" %~ (1000*) [fromList [("c",1)],fromList [("c",5)],fromList [("d",20)]] > y2 & traverseAll "c" %~ (1000*) [fromList [("c",1000)],fromList [("c",5000)],fromList [("c",6000),("d",20)]]
完全公开:我无法从头开始构建这样的
traverseAll
。我从隐式身份应用中的愚蠢的“遍历”开始:
traverseAllC' :: (Int -> Int) -> [Map String Int] -> [Map String Int] traverseAllC' f xall = maybe xall id (go xall) where go :: [Map String Int] -> Maybe [Map String Int] go (x:xs) = case x !? "c" of Just a -> (Map.insert "c" (f a) x:) <$> go xs Nothing -> Nothing go [] = Just []
并且一旦我启动并运行它,我就简化了它,使
Identity
变得明确:
traverseAllC_ :: (Int -> Identity Int) -> [Map String Int] -> Identity [Map String Int]
并将其转换为通用应用程序。
无论如何,这是代码:
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE RankNTypes #-}
import Data.Map (Map, fromList)
import Control.Lens
y :: [Map [Char] Int]
y = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20)])
]
y2 :: [Map [Char] Int]
y2 = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20),("c",6)])
]
traverseAll :: (Ord k) => k -> Traversal' [Map k v] v
traverseAll k f maps = maybe (pure maps) id (sequenceA <$> traverse (maybeUpdate k f) maps)
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
maybeUpdate k f m = do
v <- m ^. at k
return $ do
a <- f v
return $ m & at k .~ Just a
maybeUpdate' :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
maybeUpdate' k f m =
m ^. at k <&> \v -> f v <&> \a -> m & at k .~ Just a
main = do
print $ y & traverseAll "c" %~ (1000*)
print $ y2 & traverseAll "c" %~ (1000*)
[这里有两种替代方法,可以像您喜欢的镜头一样观看;
一种单线,尽管大多数情况下是非宽松的,但使用(<%~)
,即setting with passthrough operators之一: