Какие общие шаблоны/решения были установлены в Scala для часто встречающихся проблем

Я начну.

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

Часто цитируемое решение:

ss.groupBy(identity).mapValues(_.size)

Итак, какие другие решения существуют для подобных часто встречающихся проблем в Scala?


person Community    schedule 25.11.2010    source источник


Ответы (6)


Использование Monoids или Numerics для определения разумных операций для расширенных классов, используя неявные при необходимости.

case class Money(ccy: Currency, amount : BigDecimal) {
  def +(that : Money) = { 
    require(this.currency == that.curency)
    copy(amount = this.amount + that.amount)
  }
  def abs = copy(amount = amount.abs)
}

Итак, допустим, у меня есть коллекция Money, и я хочу их суммировать:

val turnover = trades.map(_.usdValue.abs).∑ //no implicit monoid :-(

Но для этого мне нужно иметь неявный Monoid. Но, конечно же, нулевое значение для Money имеет смысл только в том случае, если у меня уже есть какая-то валюта!

implicit def CurrencyMonoid(implicit currency : Currency) = new Monoid[Currency] {
  def zero = Money(currency, 0)
  def append(m1 : Money, m2 : Money) = m1 + m2
}

Итак, теперь Scala будет использовать оба этих имплицита:

implicit val usd = Currency.USD
val turnover = trades.map(_.usdValue.abs).∑ //yay for monoids :-)
person Community    schedule 28.11.2010
comment
Мне нравится этот больше всего! Это мой вопрос, поэтому я могу быть субъективным :) - person Kevin Wright; 06.03.2011

Не то чтобы я снова бью в тот же барабан, но...

Решение проблемы, когда у нас есть ряд процессов, которые могут дать успешный результат или завершиться ошибкой с некоторым сообщением об ошибке. Цель состоит в том, чтобы агрегировать успешные результаты, если все процессы завершаются успешно, а если один или несколько завершаются неудачно, агрегировать все сообщения об ошибках.

Это можно решить с помощью проверки scalaz: сначала настройте импорт

scala>  import scalaz._; import Scalaz._
import scalaz._
import Scalaz._

Теперь давайте определим наши «процессы»

scala> def fooI(s : String) : ValidationNEL[Exception, Int] = s.parseInt.liftFailNel
fooI: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Int]

scala> def fooF(s : String) : ValidationNEL[Exception, Float] = s.parseFloat.liftFailNel
fooF: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Float]

scala> def fooB(s : String) : ValidationNEL[Exception, Boolean] = s.parseBoolean.liftFailNel
fooB: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Boolean]

Теперь используйте Applicative для агрегирования неудач/успехов:

scala> def attempt(ss : String*) = (fooI(ss(0)) <|**|> (fooF(ss(1)), fooB(ss(2)))) match {
 | case Success((i, f, b)) => println("Found " + i + " " + f + " " + b) 
 | case Failure(es)        => es foreach println
 | }
attempt: (ss: String*)Unit

Теперь давайте попробуем для некоторых отказов:

scala> attempt("a", "b", "true")
java.lang.NumberFormatException: For input string: "a"
java.lang.NumberFormatException: For input string: "b"

Теперь попробуем на успех:

scala> attempt("1", "2.3", "false")
Found 1 2.3 false
person Community    schedule 25.11.2010

Я несколько раз упускал способ генерировать декартово произведение для коллекций Scala. В Haskell вы можете написать

import Control.Applicative
(,) <$> [1,2,3] <*> ["a","b"]

-- [(1,"a"),(1,"b"),(2,"a"),(2,"b"),(3,"a"),(3,"b")]

Скала-решение

for(x <- List(1,2,3); y <- List("a","b")) yield (x,y)

это слишком неуклюже.

person Community    schedule 25.11.2010
comment
масштаб: (List(1, 2, 3) |@| List("a", "b"))((_,_)) - person Debilski; 25.11.2010
comment
Или scalaz: (xs ⊛ ys) { ×(_)(_) } если на то пошло - person oxbow_lakes; 25.11.2010

Бессовестно украденный из ответа oxbow_lakes на этот вопрос: из списка параметров

Вызов метода/функции с использованием кортежа для предоставления аргументов:

case class Foo(a: Int, b: String, c: Double)
(Foo.apply _).tupled apply (1, "bar", 3.14)

Это можно использовать для ЛЮБОЙ функции.

person Community    schedule 27.11.2010

Если выполняется определенное условие cond, верните Some(x), иначе верните None:

Some(x) filter cond

[взято из сообщения Пола Филлипса в списке рассылки]

person Community    schedule 26.11.2010

Иногда вам приходится использовать while вместо for, и часто вы собираете результаты:

val buf = new Listbuffer[Int]
while(cond()) {
  val x = fancyStuff() 
  buf += calculation(x)
}

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

val buf = while(cond()) {
  val x = fancyStuff() 
} yield calculation(x)
person Community    schedule 30.11.2010