Может ли seq / par / view / force коллекции Scala рассматриваться как нарушение принципа унифицированного типа возврата? - PullRequest
8 голосов
/ 17 июня 2011

Большая часть сложности реализации инфраструктуры сбора происходит из-за того, что Scala может - в отличие от CQ LINQ или других структур сбора - возвращать «лучший» тип коллекции для функций более высокого порядка:

val numbers = List(1,2,3,4,5)
numbers map (2*) // returns a List[Int] = List(2, 4, 6, 8)

val doubles = Array(1.0, 2.0, 3.0)
doubles filter (_ < 3) // returns Array[Double] = Array(1.0, 2.0)

Почему этот принцип не выполняется для таких методов, как seq, par, view, force?

numbers.view.map(2*).force 
// returns Seq[Int] = List(2, 4, 6, 8)

numbers.seq 
// returns scala.collection.immutable.Seq[Int] = List(1, 2, 3, 4)

doubles.par.seq 
// returns scala.collection.mutable.ArraySeq[Double] = ArraySeq(1.0, 2.0, 3.0)

Существует ли техническое ограничение, которое мешает его работе?Или это по замыслу / намерению?Учитывая, что LINQ в основном ленив, эквивалент Scala (view, force) не является более безопасным для типов (только при использовании строгих методов), верно?

Ответы [ 2 ]

8 голосов
/ 18 июня 2011

Можно встраивать больше информации о типах в классы параллельной коллекции, чтобы вы вернули коллекцию, из которой вы начали, это правда.Это будет означать, что после превращения List в ParVector путем вызова par (в O (n), потому что элементы копируются в вектор) и затем вызова seq, вы снова получите List,Чтобы получить список с seq, вам нужно будет скопировать все элементы из вектора обратно в список.Вместо этого происходит следующее:

  • ParVector преобразуется обратно в Vector при вызове seq - он конвертируется в O (1)
  • , вызывая parснова в этом векторе вы получите ParVector в O (1), так как оба вектора и параллельный вектор имеют одни и те же базовые данные

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

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

1 голос
/ 20 июня 2011

Что касается статического возвращаемого типа метода сбора, C # поддерживает перегрузку методов расширения LINQ (Select, Where и т. Д.) И автоматически выберет наиболее конкретный из возможных. Таким образом, Seq.Select может возвращать Seq, Enumerable.Select может возвращать Enumerable и т. Д. Это очень похоже на то, как в Scala выбирается наиболее конкретная неявная реализация.

Что касается динамического типа, операции LINQ реализованы как методы расширения, поэтому динамическая диспетчеризация не выполняется. Таким образом, (Seq as Enumerable).Select вернет Enumerable, а не Seq. В Scala вызовы методов сбора отправляются динамически, поэтому динамический тип остается неизменным для map, filter и т. Д. Оба подхода имеют свои преимущества / недостатки. Единственное чистое решение для такого рода проблем - это IMHO с мультиметодами, и ни один язык / среда выполнения не поддерживают их эффективно.

Что касается поведения во время выполнения, LINQ всегда возвращает лениво оцененное представление коллекции. В представлении нет метода, который волшебным образом возвращает коллекцию исходного типа, вам нужно вручную указать, что вы хотите массив, например, используя метод toArray. ИМХО, это более чистый и простой подход, чем в Scala, и ленивые операции по сбору сочетаются лучше, чем строгие, но это происходит за счет дополнительного вызова метода, чтобы получить строгую коллекцию для отдельных операций по сбору.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...