2012-08-05 19 views
5

İki Option[Iterable[_]] yeni Option[Iterable[_]] içine birleştirmeye çalışıyorum. Öğelerin bir (ya da her ikisi) Biri ve Aksi halde Hiçbiri ise Biri'ye dönmek istiyorum. Bunu yapmanın deyimsel bir yolu olmalı gibi görünüyor, ama bir tane bulamıyorum. Aşağıdaki, istediğimi yapıyor gibi görünüyor, ama umuyordum ki oldukça kaygan bir çözüm değil.Scala Seçeneği Birleştir [Değiştirilebilir [_]]

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 
} 

Herhangi bir ipucu kabul edilir. Teşekkürler!

+0

Tür neredeyse benzer soru: http://stackoverflow.com/questions/10617979/binary-operator-with-option-arguments/10618340#10618340, yararlı olabilir –

cevap

11

, burada güzel bir genelleme var bir monoidin bir dizi nesnenin (bu durumda yinelenen koleksiyonlar) ve bazı basit özellikleri ve bir kimlik unsuru (boş koleksiyon) ile ek benzeri bir işlem (birleştirme) olduğu birleştirme altında.

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

(biz A ne bilmek bir monoid olduğu gerçeğini gerektiğini unutmayın: A bir monoid ise

Benzer şekilde, daha sonra Option[A] senin merge biraz daha fazla genel sürümü altında bir monoid da . ilk satırdaki yapmak için)

Scalaz library sağlayan kendi Monoid tipi sınıfında, tüm bu genellemeler yakalar yazdığınız senin böyle merge:

beklendiği gibi çalıştığını
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 

(orada Iterable ve Option için geçerli Monoid örneklerini verecekti diğer işlemler vardır, ama seninki en sık kullanılan, ve Scalaz tarafından sağlamaktadır olanlar unutmayın varsayılan) Sen keyfi Arity için kullanabiliriz

+0

Harika cevap. İlk kod parçasının üçüncü satırında küçük bir yazım hatası olduğunu düşünün, == Bazıları (ys)? –

+0

@BrianSmith: Evet, elbette - yakalamak için teşekkürler! –

3

Bu işler: Her iki Some ve sadece eğer

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

for/yield porsiyon seçeneklerin içeriğini katacak.

İsterseniz de noktalar ve parantez bazı bırakabilirsiniz

: Iterable[_] bir monoid geçerli:

Eğer soyut cebir biraz tahammül istekli iseniz
(for (a <- i1; b <- i2) yield a ++ b) orElse i1 orElse i2 
+0

Ah, evet. Çok daha güzel - teşekkürler. – robo

1

.

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