5

Option[Iterable[_]]2つを組み合わせて新しいものにしようとしていOption[Iterable[_]]ます。要素の一方(または両方)がSomeである場合はSomeを返し、それ以外の場合はNoneを返します。これを行うには慣用的な方法があるはずですが、私はそれを見つけることができないようです。以下は私が望んでいることをしているように見えますが、私が望んでいた巧妙な解決策ではありません。

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
}

ヒントをいただければ幸いです。ありがとう!

4

3 に答える 3

12

少しの抽象代数に我慢したいのであれば、ここに素晴らしい一般化があります:連結下Iterable[_]モノイドであり、モノイドは単なるセット(この場合は反復可能なコレクション)と加算のような操作(この場合は反復可能なコレクション)です(連結)いくつかの単純なプロパティとID要素(空のコレクション)を使用します。

同様に、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ライブラリは、これらすべての一般化をMonoid型クラスにキャプチャします。これにより、次のように記述できますmerge

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

(との有効なMonoidインスタンスを提供する他の操作がありますが、あなたの操作が最も一般的に使用され、Scalazがデフォルトで提供する操作であることに注意してください。)IterableOption

于 2012-08-05T23:37:17.170 に答える
3

これは機能します:

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

for/部分は、yield両方がである場合にのみ、オプションの内容を追加しますSome

必要に応じて、ドットと括弧の一部を削除することもできます。

  (for (a <- i1; b <- i2) yield a ++ b) orElse i1 orElse i2
于 2012-08-05T22:48:04.607 に答える
1

これを任意のアリティに使用できます。

def merge(xs: Option[Iterable[_]]*) = 
  if (xs.forall(_.isEmpty)) None else Some(xs.flatten.flatten)
于 2012-08-06T02:04:04.990 に答える