2012-06-21 3 views
5

У меня есть List[Option[MyClass]] с None в случайных позициях, и мне нужно «заполнить» этот список еще раз, начиная с List[MyClass], сохраняя заказ.Как заменить (заполнить) Нет записей в списке опций из другого списка, используя идиоматическую Scala?

Здесь приведены примеры списков и ожидаемый результат:

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)) 

Итак, как бы идиоматических Scala обработать этот список?

ответ

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

Итератора решение, возможно, идиоматическое Scala, и, безусловно, краткие и легко понять, но это не функциональных -любы вызова next итератора вы находитесь твердо на земле побочных эффектов.

Более функциональный подход будет использовать складку:

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 

Здесь мы двигаемся по списку Gappy при сохранении двух других списков: один для элементов мы обработали, а другую для остальные наполнители.

Такое решение не обязательно лучше, чем другое - Scala спроектирована таким образом, чтобы вы могли сочетать функциональные и императивные конструкции таким образом, но у этого есть потенциальные преимущества.

+0

«В любое время, когда вы звоните на итераторе, вы твердо находитесь в стране побочных эффектов». Правда, но в этом случае они аккуратно инкапсулируются в методе, который остается ссылочно прозрачным. –

+0

@Paul: Верно, я думаю, что другое решение отлично, и это подход, который я решил решить эту проблему в моем собственном коде. Но это связано с побочными эффектами и в некоторых подобных ситуациях, которые могут быть не идеальными. –

0

Я бы просто написать его в простой способ, соответствующий по головам списков и обработки каждого отдельного случая:

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) 
} 

Предположительно, как только вы бежите из вещей, чтобы заполнить его, вы просто оставить остальная часть None s там.