问题
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