以下をどのように見つけminValue
ますか?私には独自の解決策がありますが、他の人がそれをどのように行うかを見たいと思っています。
val i1: Option[Int] = ...
val i2: Option[Int] = ...
val defaultValue: Int = ...
val minValue = ?
更新:以下の解決策と回答の解決策の動作が異なることに気づきました。2つの値がある場合に2つの値の最小値を求める質問を読みましたが、回答ではNone
、それが含まれているように効果的に扱っています。他の何よりも大きい(のmin
)または小さい(の)値。max
具体的には、 i1
isSome(1)
とi2
isの場合None
、私のソリューションはデフォルト値を返しますが、あなたのソリューションは1を返します。
後者の動作が必要な場合は、のデフォルトの半群インスタンスOption[A]
との熱帯半群を使用できますInt
。たとえば、Scalaz 7では、次のように記述します。
import scalaz._, Scalaz._
optionMonoid(Semigroup.minSemigroup[Int]).append(i1, i2) getOrElse defaultValue
または次の速記:
Tags.Min(i1) |+| Tags.Min(i2) getOrElse defaultValue
以下のアプリケーションファンクターソリューションほどクリーンではありませんが、それがあなたの問題である場合、それはあなたの問題です。
追加のリストを作成する必要のない、より慣用的な方法は次のとおりです。
(for { x <- i1; y <- i2 } yield math.min(x, y)) getOrElse defaultValue
または、同等に:
i1.flatMap(x => i2.map(math.min(x, _))) getOrElse defaultValue
あなたがしているのは、2つの場所の関数(min
)を適用可能なファンクター(Option
)に「持ち上げる」ことです。Scalazは、その適用可能なビルダー構文でこれを簡単にします。
import scalaz._, Scalaz._
(i1 |@| i2)(math.min) getOrElse defaultValue
この場合、標準ライブラリソリューションはそれほど洗練されていませんが、これは知っておくと便利な抽象化です。
私は次のアプローチを使用して同様の問題を解決しました。両方のオプションに値がある場合は特殊なケースを処理し、それ以外の場合はAPIメソッドを使用しますOption.orElse
。
val a: Option[Int] = Some(10)
val b: Option[Int] = Some(20)
val c: Option[Int] = (a, b) match {
case (Some(x), Some(y)) => Some(x min y)
case (x, y) => x orElse y
}
val minValue: Int = List(i1, i2).flatten.sorted.headOption getOrElse defaultValue
私はこれがあなたが求めているものだと思います:
val minValue = List(i1, i2).flatten match {
case Nil => defaultValue
case xs => xs.min
}
並べ替えには、単に最大値または最小値を見つけるよりもはるかに多くの処理が必要になるため、避けたいと思いsorted
ます(ただし、この場合はおそらくそれほど違いはありません)。
式にパターンを使用できます。パターンと一致しない値は破棄されます。
(for (Some(x) <- List(None, Some(3))) yield x) max
ただし、List.flattenアプローチほど良くはありません。
scalazとmap/for / getOrElseの使用を避けたい場合は、次のようにすることができます。
val minValue = (i1, i2) match {
case (Some(x), Some(y)) => math.min(x, y)
case _ => defaultValue
}
言及されていない別のオプションは、reduceLeftOption
(交換math.max
およびmath.min
必要に応じて)使用することです。
val min = (first ++ second).reduceLeftOption(math.min).getOrElse(defaultValue)
scala> val first = Some(10)
first: Some[Int] = Some(10)
scala> val second: Option[Int] = None
second: Option[Int] = None
scala> val defaultMin = -1
defaultMin: Int = -1
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res7: Int = 10
scala> val first: Option[Int] = None
first: Option[Int] = None
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res8: Int = -1
scala> val first = Some(10)
first: Some[Int] = Some(10)
scala> val second = Some(42)
second: Some[Int] = Some(42)
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res9: Int = 10
Option
2つのsをIterable
with Option
's++
演算子として組み合わせることができます。これによりminOption
、(caseによって形成される空のiterableのcaseを適切に処理するためにNone/None
)使用し、必要に応じてデフォルト値にフォールバックできますgetOrElse
。
(optionA ++ optionB).minOption.getOrElse(-1)
// None and None => -1
// Some(5) and None => 5
// None and Some(5) => 5
// Some(5) and Some(3) => 3
カスタム猫 Semigroup
インスタンスを使用して、エレガントに必要なことを行うことができます。
import cats.kernel.Semigroup
import cats.instances.option._ // this import is for cats std option combiner
import cats.syntax.semigroup._
object Implicits {
implicit val intMinSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.min(x, y)
implicit val intMaxSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.max(x, y)
}
import Implicits.intMinSemigroup
// these are results for minSemigroup
// List((Some(1),Some(1),Some(2)), (Some(1),Some(1),None), (None,Some(2),Some(2)), (None,None,None))
//import Implicits.intMaxSemigroup
// these are results for maxSemigroup
// List((Some(1),Some(2),Some(2)), (Some(1),Some(1),None), (None,Some(2),Some(2)), (None,None,None))
for {
maybeA <- Seq(Some(1), None)
maybeB <- Seq(Some(2), None)
} yield (maybeA, maybeA |+| maybeB, maybeB)
デフォルト値で置換None
する場合は、combineを2回使用できます。
val defaultValue: Int = 3
val optionMin = for {
maybeA <- Seq(Some(1), None)
maybeB <- Seq(Some(2), None)
} yield (maybeA |+| maybeB) |+| Some(defaultValue)
// List(Some(1), Some(1), Some(2), Some(3))
簡単に言うと、同じ型の2つの値を型の1つの値に結合するためのtypeclassSemigroup[A]
です。ここでは、std cats (拡張)をここで使用します。ソースコード:A
A
OptionMonoid
Semigroup[Option[A]]
class OptionMonoid[A](implicit A: Semigroup[A]) extends Monoid[Option[A]] {
def empty: Option[A] = None
def combine(x: Option[A], y: Option[A]): Option[A] =
x match {
case None => y
case Some(a) =>
y match {
case None => x
case Some(b) => Some(A.combine(a, b))
}
}
}
彼自身でオプションのマッチングが必要であり、私たちが彼に仕事をさせるために与えるべきものはすべてですimplicit A: Semigroup[A]
。min
私たちの場合、max
ケース用に2つの異なるコンバイナーを記述します。
object Implicits {
implicit val intMinSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.min(x, y)
implicit val intMaxSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.max(x, y)
}
したがって、コンバイナ(つまりimport Implicits.intMinSemigroup
)をインポートし、combine関数を演算子として使用するcats.syntax.semigroup
ために使用します。|+|
maybeA |+| maybeB
。
結論として、 (だけでなく)任意のタイプのカスタムセミグループを定義し、いくつかのcats構文とインスタンスをインポートした後にこのタイプのオプションを組み合わせることができます。Int