Zipper迭代Scala中的列表

时间:2014-06-01 20:27:42

标签: scala zipper

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

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

1 个答案:

答案 0 :(得分:17)

拉链的许多巧妙之处在于它们有一个comonad实例,它允许我们非常优雅地解决某类问题。

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

这绝对难以计算,但如果我们使用拉链和一个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)

考虑到我们如何定义问题,我们想要的是什么。