Scala のオプションを理解する
この記事では、Scala でオプションのデータ要素を操作する方法を学びます。Scala の Option
は、堅牢なコードを書くのに役立ちます。
Scala の Option
型
Scala の Option[T]
は、指定されたタイプの 0 個または 1 個の値を格納する container
です。Scala の Option
を使用してオプションの値を管理することで、次の 2つのことを保証できます。
- タイプセーフティ:オプションの値をパラメータ化できます。
- 機能認識:
Option
タイプは、プログラムで作成されるバグを少なくするための多くの機能機能を提供します。
Scala では、Option[T]
は Some[T]
または None[T]
オブジェクトのいずれかで表されます。ここで、scala.Option
は抽象的で拡張された基本クラスであり、Scala の Option
をコンテナーのように動作させます。
Option
クラスとそのサブクラスには具象型が必要であることに注意してください。これらは、明示的にすることも、次の構文のように推測することもできます。
例:
val obj1: Option[Int] = None
val obj2 = Some(100)
ここで、obj1
と obj2
は Option[Int]
のオブジェクトです。よくある質問の 1つは、Option
が一部
かなし
かを確認する方法です。
この問題を解決するために、以下の機能があります。
isEmpty
:オブジェクトがNone
の場合、このメソッドはtrue
を返します。nonEmpty
:オブジェクトがSome
の場合、このメソッドはtrue
を返します。isDefined
:オブジェクトがSome
の場合、このメソッドはtrue
を返します。
Scala で Option
の値を取得する
get
メソッドを使用して、Options
値を取得できます。get
メソッドが None
のオブジェクトで呼び出されると、NoSuchElementException
がスローされます。これは成功バイアス
とも呼ばれます。
サンプルコード:
val obj1: Option[Int]= ...
val v1 = if (obj1.isDefined)
{
obj1.get
}
else
{
0
}
上記のコードは、パターンマッチングを使用して記述することもできます。
サンプルコード:
val obj1: Option[Int]= ...
val v1 = obj1 match {
case Some(temp) =>
temp
case None =>
0
}
値を取得するための getOrElse
や orElse
などの他のメソッドもあります。
getOrElse
:このメソッドは、オブジェクトがSome
の場合に値を取得します。それ以外の場合は、デフォルト値を返します。orElse
:このメソッドは、オブジェクトがSome
の場合に値を取得します。それ以外の場合は、alternate
オプションを返します。
オプションの値が設定されていない場合、デフォルト値を返すことができるため、getOrElse
メソッドは非常に便利です。
構文:
val v1 = obj1.getOrElse(0)
Scala のコンテナとしての Option
Scala では、Option
が別の値を格納するコンテナであることを見てきました。ちょうど List
をトラバースするように、Option
をトラバースすることができます。
このコンテナのいくつかのメソッドを見てみましょう。Scala の Option.map
メソッドの構文:
final def map[Y](f: (X) => Y): Option[Y]
サンプルコード:
val obj1: Option[Int] = Some(100)
assert(obj1.map(_.toString).contains("100"))
assert(obj1.map(_ * 2.0).contains(200))
val obj2: Option[Int] = None
assert(obj2.map(_.toString).isEmpty)
上記の例では、Option.map
メソッドを使用して、含まれている値を別のタイプに変換しました。Option.map
メソッドを使用して、プログラムのフロー制御
に影響を与えることもできます。
サンプルコード:
val obj1: Option[Int] = Some(10)
val obj2: Option[Int] = None
def mul(n: Int): Int = n * 2
assert(obj1.map(mul).contains(20))
assert(obj2.map(mul).isEmpty)
ここでは、Option[Int]
の 2つのオブジェクトを作成しました。最初のオブジェクトの値は 10
で、2 番目のオブジェクトは空です。次に、入力に 2
を掛けるメソッド mul
を定義しました。
最後に、mul
メソッドを使用して obj1
と obj2
をマップします。obj1
をマッピングすると Some(20)
が得られ、obj2 をマッピングすると None
が得られます。
ここで注意すべきことの 1つは、2 番目の呼び出し obj2.map(mul)
では、mul
メソッドがまったく呼び出されないことです。フィルタリングによるフロー制御も可能です。
これを行うには多くの方法があります。
filter
:このメソッドは、Options
値がSome
であり、提供された関数がtrue
を返す場合、Option
を返します。exists
:Option
が設定され、提供された関数がtrue
を返す場合、このメソッドはtrue
を返します。forall
:exists
メソッドと同じように動作します。
これらの関数を使用すると、コードを非常に簡潔に記述できます。
最高得点のチームを見つける例を見てみましょう。
サンプルコード:
def highScoringTeam(playerA: Player, playerB: Player, tournament: Tournament): Option[(Player, Int)] =
{ getTopScore(playerA, tournament).foldRight(getTopScore(playerB, tournament)) {
case (playerAInfo, playerBInfo) => playerBInfo.filter {
case (_, scoreB) => scoreB > playerAInfo._2
}.orElse(Some(playerAInfo))
}
}
上記のコードでは、Option
をコレクションとして使用できることを利用しています。唯一の違いは、オプションは最大で 1つの値を持つことができるということです。