2011-01-12 14 views
9

To jest problem, który rozwiązałem, ale będąc całkowitym imperatywem Scala noob, czuję, że znalazłem coś zupełnie nie eleganckiego. Wszelkie pomysły na ulepszenia docenione.Scala wstaw do listy w określonych lokalizacjach

val l1 = 4 :: 1 :: 2 :: 3 :: 4 :: Nil // original list 
val insert = List(88,99) // list I want to insert on certain places 

// method that finds all indexes of a particular element in a particular list 
def indexesOf(element:Any, inList:List[Any]) = { 
     var indexes = List[Int]() 
     for(i <- 0 until inList.length) { 
       if(inList(i) == element) indexes = indexes :+ i 
     } 
     indexes 
} 


var indexes = indexesOf(4, l1) // get indexes where 4 appears in the original list 

println(indexes) 

var result = List[Any]() 

// iterate through indexes and insert in front 
for(i <- 0 until indexes.length) { 
     var prev = if(i == 0) 0 else indexes(i-1) 
     result = result ::: l1.slice(prev, indexes(i)) ::: insert 
} 
result = result ::: l1.drop(indexes.last) // append the last bit from original list 

println(result) 

Myślałem, że bardziej eleganckie rozwiązanie można osiągnąć za pomocą czegoś takiego, ale to tylko czysta spekulacja.

var final:List[Any] = (0 /: indexes) {(final, i) => final ::: ins ::: l1.slice(i, indexes(i)) 

Odpowiedz

14
def insert[A](xs: List[A], extra: List[A])(p: A => Boolean) = { 
    xs.map(x => if (p(x)) extra ::: List(x) else List(x)).flatten 
} 

scala> insert(List(4,1,2,3,4),List(88,99)){_ == 4} 
res3: List[Int] = List(88, 99, 4, 1, 2, 3, 88, 99, 4) 

Edit: wyjaśnienie dodał.

Naszym celem jest, aby wstawić listę (zwaną extra) przed wybranych elementów w innej listy (tutaj nazwie xs --commonly wykorzystywane do list, jakby jedno jest x czym wiele z nich musi być liczba mnoga xs). Chcemy, aby działało to na dowolnej liście, którą możemy mieć, więc dodamy adnotację o typowym typie: [A].

Jakie elementy są kandydatami do wstawienia? Podczas pisania funkcji nie wiemy, więc podajemy funkcję, która mówi "prawda" lub "fałsz" dla każdego elementu (p: A => Boolean).

Teraz, dla każdego elementu na liście x, sprawdzamy - czy powinniśmy wprowadzić wstawienie (tj. Czy p(x) jest prawdziwe)? Jeśli tak, to po prostu go budujemy: extra ::: List(x) to tylko elementy extra, po których następuje pojedynczy element x. (Może lepiej napisać to jako extra :+ x - dodać pojedynczą pozycję na końcu.) Jeśli nie, mamy tylko pojedynczy przedmiot, ale robimy to jako List(x) zamiast tylko x, ponieważ chcemy, aby wszystko miało ten sam typ. Więc teraz, jeśli mamy coś podobnego

4 1 2 3 4 

a nasz stan jest to, że możemy wstawić 5 6 przed 4, generujemy

List(5 6 4) List(1) List(2) List(3) List(5 6 4) 

To jest dokładnie to, co chcemy, z wyjątkiem mamy listę list. Aby pozbyć się wewnętrznych list i spłaszczyć wszystko w jedną listę, po prostu zadzwonimy pod numer flatten.

+0

Dzieło sztuki;) Co oznacza pierwsze [A]? Co znaczy? – Murgh

+0

Pierwsze "[A]" oznacza, że ​​jest to metoda generyczna (działa na pewnym typie "A", późniejszy "A" odnosi się do tego samego). "_" oznacza "niezależnie od zmiennej"; jest to skrót do 'x => x == 4'. –

+2

'xs.map (..). Flatten' może być zapisany jako' xs.flatMap (..) '. – Landei

10

Sztuczka spłaszczania jest urocza, sam nie pomyślałabym o używaniu tutaj map. Z mojego punktu widzenia ten problem jest typową aplikacją dla zakładki, ponieważ chcesz przejrzeć listę i "zebrać" coś (lista wyników). Ponieważ nie chcemy naszą listę wyników tyłu, foldRight (a.k.a. :\) jest tu właściwa wersja:

def insert[A](xs: List[A], extra: List[A])(p: A => Boolean) = 
    xs.foldRight(List[A]())((x,xs) => if (p(x)) extra ::: (x :: xs) else x :: xs) 
3

Oto kolejna możliwość, używając Seq#patch do obsługi rzeczywistych wkładek. Musisz składaćRight tak, aby później indeksy były traktowane jako pierwsze (inserty modyfikują indeksy wszystkich elementów po wstawieniu, więc w przeciwnym razie byłoby to trudne).

def insert[A](xs: Seq[A], ys: Seq[A])(pred: A => Boolean) = { 
    val positions = xs.zipWithIndex filter(x => pred(x._1)) map(_._2) 
    positions.foldRight(xs) { (pos, xs) => xs patch (pos, ys, 0) } 
} 
Powiązane problemy