2010-11-25 1 views
7

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

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

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

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

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

ответ

3

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

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 будет использовать оба этих implicits:

implicit val usd = Currency.USD 
val turnover = trades.map(_.usdValue.abs).∑ //yay for monoids :-) 
3

Я пропустил несколько раз способ создания декартова продукта для коллекций 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) 

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

+4

scalaz: `(Список (1, 2, 3) | @ | Список («a», «b»)) ((_, _)) ` – Debilski 2010-11-25 13:18:44

+1

Или скалаз:` (xs ⊛ ys) {× (_) (_)} `, если дело доходит до этого – 2010-11-25 17:22:28

4

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

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

Это может быть решена с помощью 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 
2

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

Some(x) filter cond 

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

3

бессовестно украдены от ответа oxbow_lakes на данный вопрос: Instantiating a case class from a list of parameters

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

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

Это может быть использовано для любой функции.

1

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

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) 
Смежные вопросы