How would I use lens in Haskell to duplicate Python's enumerate?

自古美人都是妖i 提交于 2019-12-30 10:06:31

问题


Python's enumerate on lists can be written as zip [0..]. I looked at Control.Lens.Traversal and Control.Lens.Indexed, but I couldn't figure out how to use lenses to generalize this to any reasonable container (I hesitate to say "Traversable").

I'm guessing itraverse or itraverseOf is key.


回答1:


If you're using a container that is an instance of FunctorWithIndex then you can simply use imap (,):

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

But if the index isn't the position this won't work:

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

Instead you can use traversed, which is an indexed traversal where the index is the order the elements appear. This can be used for anything that's Traversable. Instead of imap use iover traversed (which is the same as imapOf but that's been deprecated):

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

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



回答2:


One solution would be to use the State monad with traverse, since it is also 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)



回答3:


You're ignoring the Applicative context on itraverse. You need something for it to be working with. But the something can be boring, like Identity.

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

And then you get what you're looking for:

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


来源:https://stackoverflow.com/questions/29125149/how-would-i-use-lens-in-haskell-to-duplicate-pythons-enumerate

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!