我如何使用 Haskell 中的镜头来复制 Python 的枚举?
How would I use lens in Haskell to duplicate Python's enumerate?
Python在列表中的enumerate可以写成zip [0..]
。我查看了 Control.Lens.Traversal 和 Control.Lens.Indexed,但我无法弄清楚如何使用镜头将其推广到任何合理的容器(我犹豫说 "Traversable")。
我猜 itraverse
或 itraverseOf
是关键。
一个解决方案是将 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)
您忽略了 itraverse
上的 Applicative
上下文。您需要 something 才能使用它。但是有些东西可能很无聊,比如 Identity
.
imap f = runIdentity . itraverse (\i a -> return (f i a))
然后你就会得到你要找的东西:
> imap (,) [1,2,3]
[(0,1),(1,2),(2,3)]
如果您使用的容器是 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"))]
Python在列表中的enumerate可以写成zip [0..]
。我查看了 Control.Lens.Traversal 和 Control.Lens.Indexed,但我无法弄清楚如何使用镜头将其推广到任何合理的容器(我犹豫说 "Traversable")。
我猜 itraverse
或 itraverseOf
是关键。
一个解决方案是将 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)
您忽略了 itraverse
上的 Applicative
上下文。您需要 something 才能使用它。但是有些东西可能很无聊,比如 Identity
.
imap f = runIdentity . itraverse (\i a -> return (f i a))
然后你就会得到你要找的东西:
> imap (,) [1,2,3]
[(0,1),(1,2),(2,3)]
如果您使用的容器是 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"))]