Last active
May 18, 2017 15:14
-
-
Save j5ik2o/963781232ee55f1b6f7024722ff83041 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package example | |
/** | |
* 値が存在する・しないの両状態を表す型を表すトレイト。 | |
* | |
* @tparam A 値の型 | |
*/ | |
sealed trait MyOption[+A] { | |
/** | |
* 格納された値を返す。 | |
* | |
* @return 値 | |
* @throws NoSuchElementException 値が存在しない場合 | |
*/ | |
def get: A | |
/** | |
* 値がないかどうかを返す。 | |
* | |
* @return 値が存在しない場合はtrue。 | |
*/ | |
def isEmpty: Boolean | |
/** | |
* 値が存在する場合に、値の変換を行う。 | |
* | |
* @param f 値を変換するための関数 | |
* @tparam B 新しい型 | |
* @return 新しい [[MyOption]] | |
*/ | |
def map[B](f: A => B): MyOption[B] = this match { | |
case MyNone => MyNone | |
case MySome(v) => MySome(f(v)) | |
case MyWithFilter(op, p) => | |
if (op.isEmpty) | |
MyNone | |
else { | |
val x = op.get | |
if (p(x)) | |
MySome(f(x)) | |
else | |
MyNone | |
} | |
} | |
/** | |
* 値が存在する場合に、値の変換を行う。 | |
* | |
* @param f 値を変数するための関数 | |
* @tparam B 新しい型 | |
* @return 新しい [[MyOption]] | |
*/ | |
def flatMap[B](f: A => MyOption[B]): MyOption[B] = map(f).getOrElse(MyNone) | |
/** | |
* 値が存在する場合に、値をフィルタリングする。 | |
* | |
* @param f フィルターのための述語関数 | |
* @return 新しい [[MyOption]] | |
*/ | |
def filter(f: A => Boolean): MyOption[A] = this match { | |
case MySome(v) if f(v) => this | |
case MyWithFilter(op, p) => | |
if (op.isEmpty) | |
MyNone | |
else { | |
val x = op.get | |
if (p(x) && f(x)) | |
op | |
else | |
MyNone | |
} | |
case _ => MyNone | |
} | |
/** | |
* [[filter]]の遅延評価版。難易度高 | |
* | |
* 関数fはすぐには評価できません。`withFiler`以外のメソッドを利用した際に、評価してください。 | |
* 難しい場合はfilterメソッドと同じ実装でも構いません。 | |
* | |
* @param f フィルターのための述語関数 | |
* @return 新しい [[MyOption]] | |
*/ | |
def withFilter(f: A => Boolean): MyOption[A] = MyWithFilter(this, f) | |
/** | |
* 格納された値を返す。値がない場合は指定された値を返す。 | |
* | |
* @param elseValue 値がない場合に返す値 | |
* @tparam B 新しい要素型 | |
* @return 値 | |
*/ | |
def getOrElse[B >: A](elseValue: B): B = this match { | |
case MyNone => elseValue | |
case MySome(v) => v | |
case MyWithFilter(op, p) => | |
if (op.isEmpty) | |
elseValue | |
else { | |
val x = op.get | |
if (p(x)) | |
x | |
else | |
elseValue | |
} | |
} | |
/** | |
* 値が存在しない場合に、指定した式を評価し返す。 | |
* | |
* @param elseValue 値が存在しない場合に返す式 | |
* @tparam B 新しい要素型 | |
* @return elseValueを評価した値 | |
*/ | |
def orElse[B >: A](elseValue: => MyOption[B]): MyOption[B] = this match { | |
case MyNone => elseValue | |
case MySome(_) => this | |
case MyWithFilter(op, p) => | |
if (op.isEmpty) | |
elseValue | |
else { | |
val x = op.get | |
if (p(x)) | |
op | |
else | |
elseValue | |
} | |
} | |
} | |
private case class MyWithFilter[A](op: MyOption[A], p: A => Boolean) extends MyOption[A] { | |
override def get: A = op.filter(p).get | |
override def isEmpty: Boolean = op.filter(p).isEmpty | |
} | |
/** | |
* 値が存在ない場合の[[MyOption]]。 | |
*/ | |
case object MyNone extends MyOption[Nothing] { | |
override def get: Nothing = throw new NoSuchElementException | |
override def isEmpty: Boolean = true | |
} | |
/** | |
* 値が存在する場合の[[MyOption]]。 | |
* | |
* @param value 値 | |
* @tparam A 値の型 | |
*/ | |
case class MySome[+A](value: A) extends MyOption[A] { | |
override def get: A = value | |
override def isEmpty: Boolean = false | |
} | |
/** | |
* [[MyOption]]のコンパニオンオブジェクト。 | |
*/ | |
object MyOption { | |
/** | |
* ファクトリメソッド。 | |
* | |
* `value`が`null`以外であれば`MySome`にラップして返す。 | |
* それ以外は`MyNone`を返す。 | |
* | |
* @param value 値 | |
* @tparam A 値の型 | |
* @return [[MyOption]] | |
*/ | |
def apply[A](value: A): MyOption[A] = if (value == null) MySome(value) else MyNone | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package example | |
object MyOptionMain extends App { | |
def f(x: Int): MyOption[Int] = MySome(x * 2) | |
def g(x: Int): MyOption[Int] = MySome(x + 3) | |
assert(MyNone.isEmpty) | |
assert(!MySome(100).isEmpty) | |
assert(MySome(100).get == 100) | |
try { | |
MyNone.get | |
assert(false) | |
} catch { | |
case _: NoSuchElementException => | |
} | |
assert(MySome(100).getOrElse(20) == 100) | |
assert((MyNone: MyOption[Int]).getOrElse(20) == 20) | |
assert(MySome(100).orElse(MySome(20)) == MySome(100)) | |
assert(MyNone.orElse(MySome(20)) == MySome(20)) | |
assert(MySome(100).filter(_ > 0) == MySome(100)) | |
assert(MySome(100).filter(_ < 0) == MyNone) | |
assert(MySome(100).map(_ * 2) == MySome(200)) | |
assert((MyNone: MyOption[Int]).map(_ * 2) == MyNone) | |
assert(MySome(100).flatMap(f) == f(100)) | |
assert(MySome(100).flatMap(MySome(_)) == MySome(100)) | |
assert((MyNone: MyOption[Int]).flatMap(MySome(_)) == MyNone) | |
assert(MySome(100).flatMap(f).flatMap(g) == MySome(100).flatMap(x => f(x).flatMap(g))) | |
assert((MyNone: MyOption[Int]).flatMap(f).flatMap(g) == (MyNone: MyOption[Int]).flatMap(x => | |
f(x).flatMap(g))) | |
// --- withFilterのテスト | |
assert(MySome(100).withFilter(_ > 0).getOrElse(20) == 100) | |
assert(MySome(100).withFilter(_ < 0).getOrElse(20) == 20) | |
assert((MyNone: MyOption[Int]).withFilter(_ > 0).getOrElse(20) == 20) | |
assert((MyNone: MyOption[Int]).withFilter(_ < 0).getOrElse(20) == 20) | |
assert(MySome(100).withFilter(_ > 0).orElse(MySome(20)) == MySome(100)) | |
assert(MySome(100).withFilter(_ < 0).orElse(MySome(20)) == MySome(20)) | |
assert((MyNone: MyOption[Int]).withFilter(_ > 0).orElse(MySome(20)) == MySome(20)) | |
assert((MyNone: MyOption[Int]).withFilter(_ < 0).orElse(MySome(20)) == MySome(20)) | |
assert(MySome(100).withFilter(_ > 0).filter(_ > 0) == MySome(100)) | |
assert(MySome(100).withFilter(_ < 0).filter(_ > 0) == MyNone) | |
assert(MySome(100).withFilter(_ > 0).filter(_ < 0) == MyNone) | |
assert(MySome(100).withFilter(_ < 0).filter(_ < 0) == MyNone) | |
assert(MySome(100).withFilter(_ > 0).map(_ * 2) == MySome(200)) | |
assert(MySome(100).withFilter(_ < 0).map(_ * 2) == MyNone) | |
assert((MyNone: MyOption[Int]).withFilter(_ > 0).map(_ * 2) == MyNone) | |
assert((MyNone: MyOption[Int]).withFilter(_ < 0).map(_ * 2) == MyNone) | |
assert(MySome(100).withFilter(_ > 0).flatMap(f) == f(100)) | |
assert(MySome(100).withFilter(_ > 0).flatMap(MySome(_)) == MySome(100)) | |
assert((MyNone: MyOption[Int]).withFilter(_ > 0).flatMap(MySome(_)) == MyNone) | |
assert( | |
MySome(100).withFilter(_ > 0).flatMap(f).flatMap(g) == MySome(100) | |
.withFilter(_ > 0) | |
.flatMap(x => f(x).flatMap(g))) | |
assert( | |
(MyNone: MyOption[Int]).withFilter(_ > 0).flatMap(f).flatMap(g) == (MyNone: MyOption[Int]) | |
.withFilter(_ > 0) | |
.flatMap(x => f(x).flatMap(g))) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment