Scala: fold vs foldLeft

Я пытаюсь понять, как складываются и foldLeft, а также уменьшают и уменьшают работу. Я использовал fold и foldLeft в качестве примера

scala> val r = List((ArrayBuffer(1, 2, 3, 4),10))
scala> r.foldLeft(ArrayBuffer(1,2,4,5))((x,y) => x -- y._1)

scala> res28: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(5)

scala> r.fold(ArrayBuffer(1,2,4,5))((x,y) => x -- y._1)
<console>:11: error: value _1 is not a member of Serializable with Equals
              r.fold(ArrayBuffer(1,2,4,5))((x,y) => x -- y._1)

Почему fold не работал как foldLeft? Что такое Serializable with Equals? Я понимаю, что fold и foldLeft имеет небольшую различную подпись API в терминах типов параметров. Пожалуйста, порекомендуйте. Спасибо.

Ответ 1

Метод fold (первоначально добавленный для параллельного вычисления) менее мощный, чем foldLeft в терминах типов, к которым он может быть применен. Его подпись:

def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1

Это означает, что тип, над которым выполняется свертывание, должен быть супертипом типа элемента коллекции.

def foldLeft[B](z: B)(op: (B, A) => B): B

Причина в том, что fold может быть реализована параллельно, а foldLeft не может. Это происходит не только из-за части *Left, которая подразумевает, что foldLeft идет слева направо последовательно, но также и потому, что оператор op не может комбинировать результаты, вычисляемые параллельно - он определяет только, как объединить тип агрегации B с типом элемента A, но не как объединить две агрегации типа B. Метод fold, в свою очередь, определяет это, потому что тип агрегации A1 должен быть супертипом типа элемента A, то есть A1 >: A. Это отношение супертипа позволяет одновременно сгибать агрегацию и элементы и комбинировать агрегаты - как с одним оператором.

Но это отношение супертипа между агрегацией и типом элемента также означает, что тип агрегации A1 в вашем примере должен быть супертипом (ArrayBuffer[Int], Int). Поскольку нулевой элемент вашего агрегации ArrayBuffer(1, 2, 4, 5) типа ArrayBuffer[Int], тип агрегации определяется как супертип обоих из них - и это Serializable with Equals, единственная наименее верхняя граница кортежа и массива буфер.

В общем случае, если вы хотите разрешить параллельное свертывание для произвольных типов (что делается не по порядку), вы должны использовать метод aggregate, который требует определения того, как объединяются две совокупности. В вашем случае:

r.aggregate(ArrayBuffer(1, 2, 4, 5))({ (x, y) => x -- y._1 }, (x, y) => x intersect y)

Btw, попробуйте написать свой пример с помощью reduce/reduceLeft - из-за отношений супертипа между типом элемента и типом агрегации, которые оба эти методы имеют, вы обнаружите, что это приводит к аналогичной ошибке, который вы описали.