嵌套容器的分度镜

时间:2018-06-20 18:02:26

标签: haskell lens

如何使用镜头从多层嵌套中获取钥匙?

请考虑以下类型

data Outer = Outer { _outerMap :: Map String Inner }
data Inner = Inner { _innerMap :: Map Char Int }
makeLenses ''Outer
makeLenses ''Inner

并采用以下示例值

example :: Outer
example = Outer $ Map.fromList
  [ ("A", Inner $ Map.fromList [ ('a', 1), ('b', 2), ('c', 3) ])
  , ("B", Inner $ Map.fromList [ ('a', 4), ('b', 6), ('c', 8) ])
  , ("C", Inner $ Map.fromList [ ('a', 5), ('b', 7), ('c', 9) ])
  ]

使用透镜,我可以将example展平为[Int]并按如下所示过滤奇数:

>>> example^..outerMap.folded.innerMap.folded.filtered odd
[1,3,5,7,9]

我可以使用内键注释值,如下所示:

>>> example^@..outerMap.folded.innerMap.ifolded.filtered odd
[('a',1),('c',3),('a',5),('b',7),('c',9)]

但是我该如何使用镜头同时使用外键和内键注释值,以获得以下结果?

>>> _whatHere example
[(("A",'a'),1),(("A",'c'),3),(("C",'a'),5),(("C",'b'),7),(("C",'c'),9)]

以下尝试仍仅返回内部键:

>>> example^@..outerMap.ifolded.innerMap.ifolded.filtered odd
[('a',1),('c',3),('a',5),('b',7),('c',9)]

并且以下尝试未进行类型检查

>>> example^..outerMap.ifolded.withIndex.alongside id (innerMap.ifolded.filtered odd.withIndex)

error:
    • No instance for (Applicative
                         (Control.Lens.Internal.Getter.AlongsideRight
                            (Const (Data.Monoid.Endo [([Char], (Char, Int))])) [Char]))

没有镜头的实现可能看起来像这样:

nolens :: Outer -> [((String, Char), Int)]
nolens =
  filter (odd . snd)
  . foldMap (\(k, i) -> (map (first (k, )) . Map.toList . _innerMap) i)
  . Map.toList
  . _outerMap

1 个答案:

答案 0 :(得分:2)

使用(<.>)。就像(.)一样,除了它保留左右两边的索引。 (.)本身(及其别名(.>))仅保留RHS的索引,除非RHS本身保留索引,在这种情况下,索引来自LHS。助记符是箭头指向您要保存的索引。

>>> example^@..outerMap.ifolded<.>innerMap.ifolded.filtered odd
[(("A",'a'),1),(("A",'c'),3),(("C",'a'),5),(("C",'b'),7),(("C",'c'),9)]