Zipper для перебора списка в Scala

Это продолжение моей предыдущей question. Я могу использовать итератор, fold, zip, foreach и другие, чтобы перебирать список в Scala. Теперь мне интересно, существуют ли случаи, когда Zipper является наиболее подходящим. Предположим, мне нужен доступ только для чтения без concurrency.

Не могли бы вы привести такой пример и объяснить, почему Zipper - лучший выбор?

Ответ 1

Одна из многих опрятных вещей о молниях - это то, что у них есть экземпляр comonad, который позволяет нам очень легко решить определенный класс проблем.

Вот пример из верхней части головы. Предположим, что у нас есть последовательность чисел, и мы хотим сделать простую форму сглаживания с экспоненциальной скользящей средней, где новое значение для каждой позиции в списке является средним значением текущего значения и всеми другими значениями, но с более отдаленными соседями, делающими меньше.

Это не ужасно сложно вычислить императивно, но если мы используем застежку-молнию и комонадическое 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)

Это то, что мы хотим, учитывая, как мы определили проблему.