在 Scala 中迭代列表的拉链 [英] Zipper to iterate over list in Scala
问题描述
这是我之前的的后续问题.我可以使用迭代器、fold
、zip
、foreach
等来迭代 Scala 中的列表.现在我想知道是否存在 Zipper
最合适的用例.假设我需要没有并发的只读访问.
This is a follow-up to my previous question. I can use an iterator, fold
, zip
, foreach
and others to iterate over a list in Scala. Now I wonder if there are use cases where Zipper
is most appropriate. Suppose I need read-only access without concurrency.
你能举出这样的例子并解释为什么 Zipper
是最好的选择吗?
Could you give such an example and explain why the Zipper
is the best choice?
推荐答案
关于 zippers 的许多巧妙之处之一是它们有一个 comonad 实例,它允许我们非常优雅地解决某一类问题.
One of the many neat things about zippers is that they have a comonad instance, which allows us to solve a certain class of problems very elegantly.
这是我脑海中的一个简单例子.假设我们有一个数字序列,我们想用指数移动平均值做一个简单的平滑形式,其中列表中每个位置的新值是当前值和所有其他值的平均值,但是距离越远的邻居贡献越少.
Here's a quick example off the top of my head. Suppose that we've got a sequence of numbers and we want to do a simple form of smoothing with an exponential moving average, where the new value for each position in the list is an average of the current value and all the other values, but with more distant neighbors contributing less.
命令式计算这不是一件非常困难的事情,但如果我们使用拉链和共联组合,它离单行也不会太远:
This isn't a terribly hard thing to compute imperatively, but if we use a zipper and a comonadic cobind it's not too far from a one-liner:
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
我们将得到以下等价物:
We'll get the moral equivalent of:
List(1 / 24, 1 / 12, 1 / 6, 1 / 3, 1 / 6, 1 / 12, 1 / 24)
考虑到我们如何定义问题,这就是我们想要的.
Which is what we want, given how we defined the problem.
这篇关于在 Scala 中迭代列表的拉链的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!