Scala для понимания и промежуточных результатов flatMap / map

С первого раза прочитал это:

for {
  harpo<-list1 if harpo.length>0
  groucho<-list2
  chico<-list3
} yield (harpo, groucho, chico)

переводится на:

list1.filter(_.length>0).flatMap(harpo =>      
      list2.flatMap(groucho=>list3.map((harpo,groucho,_)))
)

Меня беспокоили ненужные промежуточные коллекции, возвращаемые filter, flatMap & map. Первый был исправлен в Scala 2.8 (?) Путем добавления метода withFilter, и я подозреваю, что происходит какая-то магия, которая изменяет тип возвращаемого значения этих методов в зависимости от использования, поэтому при использовании в качестве параметра для flatMap они возвращают не- строгий сбор, но я не могу найти никаких доказательств. Верны ли мои подозрения и не так ли неэффективно, как кажется на первый взгляд?


person Turin    schedule 26.05.2013    source источник


Ответы (1)


Это относится к этому вопросу. В частности, ответ от @ IODEV показывает, как смотреть на обессахаренную форму:

$ scala -Xprint:typer -e
'val list1 = List("foo", "bar"); val list2 = list1; val list3 = list1;
for (harpo<-list1 if harpo.length>0; groucho <- list2; chico <- list3) 
yield (harpo, groucho, chico)'

(без разрывов строк)

list1.withFilter(_.length() > 0)
  .flatMap(harpo =>
    list2.flatMap(groucho =>
      list3.map(chico => (harpo, groucho, chico))
    )
  )

Я не вижу потраченных впустую промежуточных коллекций, которые вы могли бы сохранить, если только вы не выберете изменяемый конструктор и while или foreach вызовы для заполнения этого конструктора:

val b = List.newBuilder[(String, String, String)]
for(harpo <- list1 if harpo.length() > 0; groucho <- list2; chico <- list3) {
  b += ((harpo, groucho, chico))
}
b.result()

Вопрос в том, демонстрирует ли ваш конкретный код значительную проблему с производительностью. Например. ваши коллекции невероятно велики. Если нет, используйте более идиоматическую форму (for ... yield). Оптимизируйте конструктор с for ... {} только тогда, когда вы действительно что-то от этого получите.

person 0__    schedule 26.05.2013
comment
Может, мне следовало быть более ясным. Мой вопрос: являются ли коллекции, возвращаемые list3.map и list2.flatMap, строгими или нет? Если они строгие, они создаются повторно, чтобы их отбросить после помещения их элементов в родительскую коллекцию. Если я просто сделаю val l = list3.map("brother "+_), я получу строгий сбор. Типичным шаблоном C ++ в таких случаях является использование ленивого промежуточного типа, который неявно преобразуется в строгий тип при присвоении переменной ожидаемого возвращаемого типа. Я не знаю, возможно ли это в scala, но его вывод типа все еще имеет для меня секреты. - person Turin; 27.05.2013
comment
list2 и list3, вероятно, являются строгими (вы не указываете их типы, но предполагаете, что они относятся к типу List), поэтому внутренние циклы создают строгие промежуточные результаты. Я не знаю коллекций C ++, но сомневаюсь, что вы можете вложить flatMaps ленивым способом, исключающим любые промежуточные коллекции - map и filter да, но не flatMap . Если вы этого хотите, используйте итерационный подход с Builder и foreach. Как я уже сказал, не попадайтесь в ловушку преждевременных оптимизаций. - person 0__; 27.05.2013