Stream
ダニエルの答えに加えて、それは短絡評価に役立つことを覚えておいてください。たとえば、を受け取ってString
返す関数の膨大なセットがありOption[String]
、そのうちの1つが機能するまでそれらを実行し続けたいとします。
val stringOps = List(
(s:String) => if (s.length>10) Some(s.length.toString) else None ,
(s:String) => if (s.length==0) Some("empty") else None ,
(s:String) => if (s.indexOf(" ")>=0) Some(s.trim) else None
);
確かに、リスト全体を実行したくはありませんList
。「これらを関数として扱い、いずれかが以外のものを返すまで実行する」という便利な方法はありませんNone
。何をすべきか?おそらくこれ:
def transform(input: String, ops: List[String=>Option[String]]) = {
ops.toStream.map( _(input) ).find(_ isDefined).getOrElse(None)
}
Stream
これはリストを取得し、それを(実際には何も評価しない)として扱いStream
、関数を適用した結果である新しいものを定義し(ただし、まだ何も評価しません)、最初のリストを検索します。が定義されています-そしてここで、魔法のように、マップを適用し、元のリストから適切なデータを取得する必要があることを振り返って認識します-次に、Option[Option[String]]
をOption[String]
使用してからラップを解除しgetOrElse
ます。
次に例を示します。
scala> transform("This is a really long string",stringOps)
res0: Option[String] = Some(28)
scala> transform("",stringOps)
res1: Option[String] = Some(empty)
scala> transform(" hi ",stringOps)
res2: Option[String] = Some(hi)
scala> transform("no-match",stringOps)
res3: Option[String] = None
しかし、それは機能しますか?println
関数にを入れて、それらが呼び出されているかどうかを判断できるようにすると、次のようになります。
val stringOps = List(
(s:String) => {println("1"); if (s.length>10) Some(s.length.toString) else None },
(s:String) => {println("2"); if (s.length==0) Some("empty") else None },
(s:String) => {println("3"); if (s.indexOf(" ")>=0) Some(s.trim) else None }
);
// (transform is the same)
scala> transform("This is a really long string",stringOps)
1
res0: Option[String] = Some(28)
scala> transform("no-match",stringOps)
1
2
3
res1: Option[String] = None
(これはScala 2.8の場合です。残念ながら、2.7の実装は1つオーバーシュートすることがあります。失敗が発生すると、リストが長く蓄積されることに注意してください。ただし、ここでの実際の計算と比較すると、おそらくこれは安価です。)None