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

Я начну катиться.

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

Часто используемое решение:

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

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

Ответы [ 6 ]

4 голосов
/ 25 ноября 2010

Не то чтобы я снова грохнул по тому же барабану, но ...

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

Это может быть решено с помощью 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
3 голосов
/ 28 ноября 2010

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

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 :-)
3 голосов
/ 27 ноября 2010

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

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

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

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

3 голосов
/ 25 ноября 2010

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

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

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

Решение Scala

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

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

2 голосов
/ 26 ноября 2010

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

Some(x) filter cond

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

1 голос
/ 30 ноября 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)
...