頻繁に発生する問題のためにScalaでどのような一般的なパターン/ソリューションが確立されていますか
-
28-09-2019 - |
質問
ボールを転がし始めます。
一連の要素を考えると、その一部は複数回含めることができますが、1つの典型的な要件は、それらをカウントすることです - 集計またはヒストグラムの形で。
しばしば引用されたソリューションは次のとおりです。
ss.groupBy(identity).mapValues(_.size)
それでは、SCALAで一般的に遭遇する同様の問題に対して他にどのようなソリューションが存在しますか?
解決
使用 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
'sと私はそれらを合計したい:
val turnover = trades.map(_.usdValue.abs).∑ //no implicit monoid :-(
しかし、これを行うには、暗黙的にする必要があります Monoid
. 。しかし、もちろん、a 零 Aの値 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 :-)
他のヒント
同じドラムを再び叩いているわけではありませんが...
成功した出力を生成したり、エラーメッセージで失敗したりする可能性のある多くのプロセスがある問題の解決策。目標は、成功した結果を集約することです。すべてのプロセスが成功をもたらし、1つ以上が失敗した場合、すべてのエラーメッセージを集約することです。
これはによって解決できます スカラズ 検証: まず、いくつかの輸入品をセットアップします
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
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)
あまりにも不器用です。
この質問に対するoxbow_lakesの答えから恥知らずに盗まれました: パラメーターのリストからケースクラスをインスタンス化します
タプルを使用してメソッド/関数を呼び出して引数を提供します。
case class Foo(a: Int, b: String, c: Double)
(Foo.apply _).tupled apply (1, "bar", 3.14)
これは、任意の機能に使用できます。
特定の条件の場合 cond
リターンを保持します Some(x)
, 、そうでなければ戻ってきます None
:
Some(x) filter cond
メーリングリストのポールフィリップスの投稿からピックアップ
時には、その代わりに使用する必要があり、多くの場合、結果を収集しています。
val buf = new Listbuffer[Int]
while(cond()) {
val x = fancyStuff()
buf += calculation(x)
}
持っていることは非常に役立つと思います while
何かを「生む」という同じ可能性 for
, 、命令的なスタイルと機能的スタイルの間の端にあるいくつかのburrを削除する:
val buf = while(cond()) {
val x = fancyStuff()
} yield calculation(x)