Скала Вальс против Варс - PullRequest
4 голосов
/ 24 ноября 2010

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

val total = items.filter(_.itemType == CHECK).map(._amount).sum

Это дало бы мне то, что мне нужно, сумму всех проверок в неизменяемой переменной.Но он делает это с тем, что похоже на 3 итерации.Один раз для фильтрации чеков, снова для сопоставления сумм, а затем суммы.Другим способом было бы сделать что-то вроде:

var total = new BigDecimal(0)
for (
    item <- items
    if item.itemType == CHECK
) total += item.amount

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

var numOfChecks = 0
var total = new BigDecimal(0)
items.foreach { item =>
    if (item.itemType == CHECK) {
        numOfChecks += 1
        total += item.amount
    }
}

Так что, если вам понадобится куча счетчиков или итогов в списке, лучше оставить изменяемые переменные или не беспокоиться об этом, сделайте что-то вроде:

val checks = items.filter(_.itemType == CHECK)
val total = checks.map(_.amount).sum
return (checks.size, total)

, который кажется более простым для чтения и использует только vals

Ответы [ 3 ]

8 голосов
/ 24 ноября 2010

Другим способом решения вашей проблемы за одну итерацию будет использование представлений или итераторов:

items.iterator.filter(_.itemType == CHECK).map(._amount).sum

или

items.view.filter(_.itemType == CHECK).map(._amount).sum

Таким образом, оценка выражения будет отложена до тех пор, покавызов sum.

Если ваши предметы относятся к классам кейсов, вы также можете написать это так:

items.iterator collect { case Item(amount, CHECK) => amount } sum
7 голосов
/ 24 ноября 2010

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

Создание временных объектов, теперь , что вызывает беспокойство, потому что вы попадете в память (даже если она кэширована), что не относится к одной итерации. В этих случаях view поможет, хотя он добавляет больше вызовов методов для выполнения той же работы. Надеюсь, JVM оптимизирует это. См. Мориц ответ для получения дополнительной информации о просмотрах.

6 голосов
/ 24 ноября 2010

Для этого вы можете использовать foldLeft:

(0 /: items) ((total, item) => 
   if(item.itemType == CHECK) 
      total + item.amount 
   else 
      total
)

Следующий код вернет кортеж (количество чеков -> сумма сумм):

((0, 0) /: items) ((total, item) => 
   if(item.itemType == CHECK) 
      (total._1 + 1, total._2 + item.amount) 
   else 
      total
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...