我如何在Haskell中使用镜头来复制Python的枚举?

时间:2015-03-18 14:47:07

标签: haskell lens

列表上的Python enumerate可以写为zip [0..]。我查看了Control.Lens.Traversal和Control.Lens.Indexed,但我无法弄清楚如何使用镜头将其概括为任何合理的容器(我犹豫说" Traversable")。

我猜测itraverseitraverseOf是关键。

3 个答案:

答案 0 :(得分:8)

如果您使用的容器是FunctorWithIndex的实例,那么您只需使用imap (,)

> imap (,) "abc"
[(0,'a'),(1,'b'),(2,'c')]

但如果指数不是这个位置就不会起作用:

> let m = Map.fromList [('a', "foo"), ('b', "bar"), ('c', "foobar")])
> imap (,) m
fromList [('a',('a',"foo")),('b',('b',"bar")),('c',('c',"foobar"))]

相反,您可以使用traversed,这是一个索引遍历,其中索引是元素出现的顺序。这可用于Traversable的所有内容。而不是imap使用iover traversed(与imapOf相同但已被弃用):

> iover traversed (,) "abc"
[(0,'a'),(1,'b'),(2,'c')]

> iover traversed (,) m
fromList [('a',(0,"foo")),('b',(1,"bar")),('c',(2,"foobar"))]

答案 1 :(得分:6)

一种解决方案是将State monad与traverse一起使用,因为它也是Applicative

enumerate :: (Integral n, Traversable t) => t a -> t (n, a)
enumerate t = evalState (traverse go t) 0
    where
        go a = do
            i <- get
            modify (+1)
            return (i, a)

答案 2 :(得分:3)

您忽略了Applicative上的itraverse上下文。你需要某些东西才能使用它。但事情可能很无聊,比如Identity

imap f = runIdentity . itraverse (\i a -> return (f i a))

然后你得到了你正在寻找的东西:

> imap (,) [1,2,3]
[(0,1),(1,2),(2,3)]