这是我之前question的后续内容。我可以使用迭代器fold
,zip
,foreach
和其他来迭代Scala中的列表。现在我想知道是否存在Zipper
最合适的用例。假设我需要没有并发性的只读访问。
你能举一个这样的例子来解释为什么Zipper
是最好的选择吗?
答案 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)
考虑到我们如何定义问题,我们想要的是什么。