2012-08-05 16 views
5

Estoy tratando de combinar dos Option[Iterable[_]] en un nuevo Option[Iterable[_]]. Me gustaría devolver un Some si uno (o ambos) de los elementos es un Some y a None en caso contrario. Parece que debería haber una forma idiomática de hacer esto, pero parece que no puedo encontrar uno. Lo siguiente parece hacer lo que quiero, pero no es la solución perfecta que esperaba.Combinación de la opción Scala [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 
} 

Cualquier consejo es apreciado. ¡Gracias!

+0

pregunta Tipo de casi similar: http://stackoverflow.com/questions/10617979/binary-operator-with-option-arguments/10618340#10618340, puede ser útil –

Respuesta

11

Si usted está dispuesto a soportar un poco de álgebra abstracta, hay una buena generalización aquí: Iterable[_] es una monoid bajo concatenación, donde un monoide es solo un conjunto de cosas (colecciones iterables, en este caso) y una operación de adición (concatenación) con algunas propiedades simples y un elemento de identidad (la colección vacía).

mismo modo, si A es un monoide, a continuación, Option[A] es también un monoide bajo una versión ligeramente más general de su merge:

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

(Tenga en cuenta que tenemos el hecho de que A es un monoide saber qué . hacer en la primera línea)

el Scalaz library capta todas estas generalizaciones de su clase Monoid tipo, lo que le permite escribir su merge así:

import scalaz._, Scalaz._ 

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

que funciona como se esperaba:

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 

(Tenga en cuenta que hay otras operaciones que darían válidos Monoid casos para Iterable y Option, pero los suyos son los más comúnmente utilizados, y los que Scalaz presta, predeterminado)

+0

Gran respuesta. ¿Crees que hay un pequeño error tipográfico en la tercera línea del primer fragmento de código, debería ser == Some (ys)? –

+0

@BrianSmith: Sí, por supuesto, ¡gracias por la captura! –

3

Esto funciona:

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

La parte for/yield añadirá el contenido de las opciones si y sólo si ambos son Some.

También puede dejar algunos de los puntos y paréntesis, si se quiere:

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

Ah, sí. Eso es mucho mejor, gracias. – robo

1

Usted podría utilizar esto para aridad arbitraria:.

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