【问题标题】:How would I use lens in Haskell to duplicate Python's enumerate?我将如何在 Haskell 中使用 lens 来复制 Python 的枚举?
【发布时间】:2015-03-18 23:42:20
【问题描述】:

Python 在列表中的enumerate 可以写成zip [0..]。我查看了 Control.Lens.Traversal 和 Control.Lens.Indexed,但我不知道如何使用镜头将其推广到任何合理的容器(我不敢说“Traversable”)。

我猜itraverseitraverseOf 是关键。

【问题讨论】:

  • 我相信lens 提供了索引地图,但我不确定。我知道如果你使用Data.Sequence,那么使用mapWithIndex (,) 比任何形式的遍历都要高效。
  • itraverse 当然可以满足您的需求。您使用它有什么困难?
  • @Carl 我对itraverse (,) "abcd" 的天真直觉抱怨缺乏 Int 的幺半群实例。看起来使用(,) 作为第一个参数给了我错误的输出类型,即(Int, t a) 而不是t (Int, a)
  • @AlexR 嗯,是的。 traverseitraverseApplicative 上下文中工作,您将忽略它。试试imap f = runIdentity . itraverse (\i a -> return (f i a))
  • 不知道镜头,但这与"How to decorate a Tree in Haskell" 中讨论的问题基本相同。 The answer by hammarbhelkir's response to your question 相同。

标签: haskell haskell-lens


【解决方案1】:

如果您使用的容器是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"))]

【讨论】:

  • 谢谢。这就是我一直在寻找的。​​span>
【解决方案2】:

一种解决方案是将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)

【讨论】:

  • 顺便说一句,Applicative 风格,go a = (\i -&gt; (i,a)) &lt;$&gt; get &lt;* modify (+1)。我可以推荐(Traversable t, Integral n) =&gt; t a -&gt; t (n, a)吗?
  • @dfeuer 当然,我只是懒惰。如果你真的想要,你甚至可以进一步放松到Num
  • 这就是我可能会正常使用的解决方案(好吧,看看我的代码,它更类似于@dfeuer 修改,除了使用元组部分和succ。我正在寻找一个镜头-不过,具体的答案部分是为了扩大我对包的理解。
  • @AlexR,我避免使用元组部分只是因为它们不是标准的;我也会用它们。你是对的,使用Enum 很有意义,但是Enum 是一个可怕的、可怕的、不好的、非常糟糕的课程:-/
【解决方案3】:

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

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

然后你会得到你想要的:

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

【讨论】:

  • 啊,还有更多方便的东西可用,比如 cchalmer 的回答中的那些。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-22
  • 2019-10-09
  • 1970-01-01
  • 1970-01-01
  • 2016-01-05
相关资源
最近更新 更多