13

这是我之前的问题的后续。我可以使用迭代器、、、foldzip其他迭代器foreach来迭代 Scala 中的列表。现在我想知道是否有Zipper最合适的用例。假设我需要没有并发的只读访问。

你能举一个这样的例子并解释为什么Zipper是最好的选择吗?

4

1 回答 1

17

关于 zippers 的许多巧妙之处之一是它们有一个共单子实例,这使我们能够非常优雅地解决某一类问题。

这是我脑海中的一个快速示例。假设我们有一个数字序列,我们想用指数移动平均线做一个简单的平滑形式,其中列表中每个位置的新值是当前值和所有其他值的平均值,但是更远的邻居贡献更少。

用命令式计算这并不是一件非常困难的事情,但是如果我们使用 zipper 和 comonadic cobind ,那么它离单行也不会太远:

import scalaz._, Scalaz._

val weights = Stream.from(1).map(1.0 / math.pow(2, _))

def sumNeighborWeights(neighbors: Stream[Double]) =
  neighbors.fzipWith(weights)(_ * _).sum

def smooth(data: NonEmptyList[Double]) = data.toZipper.cobind { z =>
  (z.focus + sumNeighborWeights(z.lefts) + sumNeighborWeights(z.rights)) / 3
}

现在如果我们写:

val result = smooth(NonEmptyList[Double](0, 0, 0, 1, 0, 0, 0)).toList

我们将得到以下道德等价物:

List(1 / 24, 1 / 12, 1 / 6, 1 / 3, 1 / 6, 1 / 12, 1 / 24)

鉴于我们如何定义问题,这就是我们想要的。

于 2014-06-01T21:23:49.200 回答