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

后端 未结 3 1692
孤独总比滥情好
孤独总比滥情好 2021-01-13 19:37

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 len

相关标签:
3条回答
  • 2021-01-13 20:02

    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)
    
    0 讨论(0)
  • 2021-01-13 20:08

    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"))]
    
    0 讨论(0)
  • 2021-01-13 20:27

    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)]
    
    0 讨论(0)
提交回复
热议问题