2012-08-05 18 views
5

Option[Iterable[_]]の2つを新しいOption[Iterable[_]]に結合しようとしています。私はいくつかの要素のいずれか(または両方)がSomeとNoneならばSomeを返したいと思います。これを行うための慣用的な方法があるはずですが、私はそれを見つけることができません。以下は私が欲しいものをするようですが、私が望んでいた非常に滑らかな解決策ではありません。スカラオプションを組み合わせる[Iterable [_]]

def merge(
    i1: Option[Iterable[_]], i2: Option[Iterable[_]] 
): Option[Iterable[_]] = (i1, i2) match { 
    case (Some(as), Some(bs)) => Some(as ++ bs) 
    case (a @ Some(as), None) => a 
    case (None, b @ Some(bs)) => b 
    case _ => None 
} 

いずれのヒントもありがとうございます。ありがとう!

+0

種類のほぼ同様の質問: http://stackoverflow.com/questions/10617979/binary-operator-with-option-arguments/10618340#10618340、役に立つかもしれません –

答えて

11

場合、素敵な一般化はここにあります:Iterable[_]monoidですモノノイドは単なる一連のもの(ここでは繰り返し可能なコレクション)であり、いくつかの単純なプロパティとアイデンティティ要素(空のコレクション)との加算的な操作(連結)です。 Aがモノイドである場合

は同様に、その後、Option[A]はまたあなたのmergeの少しより一般的なバージョンの下でモノイドです:

Some(xs) + Some(ys) == Some(xs + ys) 
Some(xs) + None  == Some(xs) 
None  + Some(ys) == Some(ys) 
None  + None  == None 

(私たちはAは何を知っているモノイドであることを必要とすることに注意してください。最初の行に行うには)

Scalaz libraryは、あなたがこのようなあなたのmerge書き込むことができますそのMonoid型クラス、これらのすべての一般化を捉え:

を期待どおりに動作
import scalaz._, Scalaz._ 

def merge(i1: Option[Iterable[_]], i2: Option[Iterable[_]]) = i1 |+| i2 

scala> merge(Some(1 to 5), None) 
res0: Option[Iterable[_]] = Some(Range(1, 2, 3, 4, 5)) 

scala> merge(Some(1 to 5), Some(4 :: 3 :: 2 :: 1 :: Nil)) 
res1: Option[Iterable[_]] = Some(Vector(1, 2, 3, 4, 5, 4, 3, 2, 1)) 

scala> merge(None, None) 
res2: Option[Iterable[_]] = None 

(そこIterableOptionのために有効なMonoidインスタンスを与える他の操作がありますが、あなたが最も一般的に使用されている、とScalazはで提供するものがありますデフォルト)あなたは、任意のアリティのためにこれを使用することができ

+0

偉大な答え。最初のコード断片の3行目にマイナーなタイプミスがあると思っています。== Some(ys)? –

+0

@BrianSmith:はい、もちろん、キャッチしてくれてありがとう! –

3

これは動作します:

def merge(i1: Option[Iterable[_]], i2: Option[Iterable[_]]): Option[Iterable[_]] = 
    (for (a <- i1; b <- i2) yield a ++ b).orElse(i1).orElse(i2) 

との両方がSomeある場合だけfor/yield部分はオプションの内容を追加します。

したい場合にも、ドットと括弧の一部をドロップすることができます:あなたは抽象代数学のビットを我慢して喜んでいる

(for (a <- i1; b <- i2) yield a ++ b) orElse i1 orElse i2 
+0

ああ、はい。それははるかに良いです - ありがとう。 – robo

1

:。

def merge(xs: Option[Iterable[_]]*) = 
    if (xs.forall(_.isEmpty)) None else Some(xs.flatten.flatten)