2012-06-21 5 views
5

Mam List[Option[MyClass]] z None w losowych pozycjach i muszę "wypełnić" tę listę ponownie, od List[MyClass], zachowując kolejność.Jak wymienić (wypełnić) Brak wpisów na liście opcji z innej listy za pomocą idiomatic Scala?

Oto przykładowe listy i oczekiwany wynik:

val listA = List(Some(3),None,Some(5),None,None) 
val listB = List(7,8,9) 
val expectedList = List(Some(3), Some(7), Some(5), Some(8), Some(9)) 

Tak, jak byłoby idiomatyczne Scala przetworzyć tę listę?

Odpowiedz

13
def fillL[T](a:List[Option[T]], b:List[T]) = { 
    val iterB = b.iterator 
    a.map(_.orElse(Some(iterB.next))) 
} 
1

Rozwiązanie iterator jest zapewne idiomatyczne Scala, a to z pewnością zwięzłe i łatwe do zrozumienia, ale to nie jest funkcjonalny -Każdy czas zadzwonić next na iteracyjnej jesteś mocno w ziemi skutków ubocznych.

Podejście bardziej funkcjonalne byłoby użyć owczarni

def fillGaps[A](gappy: List[Option[A]], filler: List[A]) = 
    gappy.foldLeft((List.empty[Option[A]], filler)) { 
    case ((current, fs), Some(item)) => (current :+ Some(item), fs) 
    case ((current, f :: fs), None) => (current :+ Some(f), fs) 
    case ((current, Nil), None) => (current :+ None, Nil) 
    }._1 

Tu poruszać się po liście gappy utrzymując dwie inne listy: jeden dla przedmiotów mamy przetwarzane i drugi dla pozostałych elementy wypełniające.

Tego rodzaju rozwiązanie niekoniecznie jest lepsze niż inne - Scala została zaprojektowana tak, aby w ten sposób mieszać funkcjonalne i imperatywne konstrukcje - ale ma potencjalne zalety.

+0

"za każdym razem, gdy zadzwonisz dalej w iteratorze, jesteś mocno w krainie efektów ubocznych." To prawda, ale w tym przypadku są one starannie enapuslowane w metodzie, która pozostaje referencyjna przejrzysta. –

+0

@Paul: Racja, myślę, że inne rozwiązanie jest świetne i to podejście, które wybrałbym, aby rozwiązać ten problem w moim własnym kodzie. Ale wiąże się to z efektami ubocznymi iw niektórych podobnych sytuacjach, które mogą nie być idealne. –

0

bym po prostu napisz to w prosty sposób, pasujące na głowach listach i obchodzić każdy przypadek odpowiednio:

def fill[A](l1: List[Option[A]], l2: List[A]) = (l1, l2) match { 
    case (Nil, _) => Nil 
    case (_, Nil) => l1 
    case (Some(x) :: xs, _) => Some(x) :: fill(xs, l2) 
    case (None :: xs, y :: ys) => Some(y) :: fill(xs, ys) 
} 

Przypuszczalnie kiedyś zabraknie rzeczy do napełnić go, po prostu zostawić reszta None jest tam.