суммирование преобразования списка чисел в scala

#scala

#scala

Вопрос:

Мне часто нужно суммировать преобразование списка чисел в Scala. Один из способов сделать это, конечно,:

 list.map(transform(_)).sum
  

Однако это создает память, когда создание памяти не требуется. Альтернативой является свертывание списка.

 list.foldLeft(0.0) { (total, x) => total   f(x) }
  

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

 list.mapSum(transform(_))
  

Комментарии:

1. Связано только по касательной, но будьте осторожны, если суммирование преобразований по множествам , например: Set(1,2,3).map(_ % 2).sum является 1 , не 2 . Недавно меня укусило что-то вроде setOfPlayers.map(_.salary).sum

Ответ №1:

Вы можете использовать представление, чтобы сделать ваши методы преобразования (map, filter …) ленивыми. Смотрите здесь для получения дополнительной информации.

Так, например, в вашем случае с методом, вызываемым transform , вы бы написали

 list.view.map(transform).sum
  

(обратите внимание, что вы можете опустить (_) )

Комментарии:

1. Я помнил об этом, но какое-то ошибочное тестирование убедило меня, что это замедляет работу. Сегодня, после еще нескольких попыток, view кажется отличным!

2. @schmmd в некоторых тривиальных случаях реализация view замедлит работу, что может быть причиной того, что она не применяется автоматически по умолчанию. Я думаю, что это зависит от таких факторов, как доля элементов, которые вы используете / отфильтровываете. Также скорость != память. Поэтому, вероятно, лучше всего попробовать это и сравнить, если скорость имеет решающее значение.

3. В этом примере я должен просто использовать итератор.

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

5. Хорошо понятно, но представления в scala считаются чрезвычайно проблематичными до такой степени, что многие люди хотят их удалить, поэтому я их избегаю.

Ответ №2:

Эта операция называется foldMap , и вы можете найти ее в Scalaz.

 list foldMap transform
  

Комментарии:

1. 1 когда я начну использовать scalaz, моя будущая замена действительно возненавидит меня.

2. Как указано, это выражение не суммирует элементы, не так ли?

3. Для дальнейшей разработки, foldMap имеет следующую сигнатуру типа: def foldMap[A, M: Monoid](t: F[A], f: A => M): M , поэтому ваше преобразование должно возвращать что-то, что является моноидом, и, следовательно, имеет «ноль» и | | операцию, которая foldMap будет использоваться для накопления результата.

4. @Zwirb в этой подписи есть два параметра, но я вижу только один аргумент в ответе. Что такое F[A] ?

5. @LuigiPlinge: вы правы! Я процитировал метод def из Foldable признака вместо метода из MA признака, который фактически используется здесь (и в конечном итоге вызывает метод в Foldable ): def foldMap[B](f: A => B)(implicit r: Foldable[M], m: Monoid[B]): B = r.foldMap(value, f) , где value ваш исходный список, который был преобразован во что-то с MA признаком. Это также сбивает меня с толку, кстати: Я едва начинаю ориентироваться в scalaz…