2016-05-07 11 views
3

Ich versuche nur ein paar Hände auf Scala und versuchte, List.concat-Funktion auf eigene Faust zu implementieren. Hier ist der CodeScala Muster passend für Vararg

def concat[A](lists : Traversable[A]*):List[A]={ 
    println("concat called") 
    lists match { 
     case Nil => Nil 
     case x :: Nil => (x :\ List.empty[A])((elem,list)=> elem::list) 
     case x:: xs => (x :\ concat(xs:_*))((elem,list)=> elem :: list) 
    } 
    } 

Allerdings, wenn ich versuche, diese Methode zu nennen wie

concat(List(1,2,3),List(2,3,4),List(4,5,6),List(6,7,8)) 

ich Fehler

Exception in thread "main" scala.MatchError: WrappedArray(List(1, 2, 3), List(2, 3, 4), List(4, 5, 6), List(6, 7, 8)) (of class scala.collection.mutable.WrappedArray$ofRef) 

Kann jemand erklären, was ich hier falsch gemacht haben? Vielen Dank im Voraus

Antwort

6

Varags ist ein Seq und Sie können darauf wie auf einem Seq übereinstimmen, nicht wie auf einer Liste. Hier ein Beispiel:

@ a(1, 2, 3) 
res1: Seq[Int] = Array(1, 2, 3) 
@ def a(x: Int*) = x match { 
        case Seq() => "empty" 
        case Seq(a) => s"single $a" 
        case Seq(a, as @ _*) => s"multiple: $a, $as" 
       } 
defined function a 
@ a(1, 2, 3, 4) 
res3: String = "multiple: 1, WrappedArray(2, 3, 4)" 
@ a(1, 2) 
res4: String = "multiple: 1, WrappedArray(2)" 
@ a(1) 
res5: String = "single 1" 

tun solche Anpassung auf Nil und x :: xs in der Regel bedeutet, dass Sie einfach foldLeft verwenden können, das tut genau das.

def concat[A](lists: Traversable[A]*): List[A] = 
    lists.foldLeft(List.empty[A])(_ ++ _) 

Und beachten Sie, dass bei der Anpassung an Nil und x :: xs, wo xsNil sein kann, ist genug. Ihre zweite case kann einfach entfernt werden.

Gerade in denen:

case Nil => Nil 
case x :: Nil => (x :\ List.empty[A])(_ :: _) 
case x :: xs => (x :\ concat(xs:_*))(_ :: _) 

letzten beiden gleich sind. Wenn im dritten Fall xs == Nil dann anstelle von concat(xs:_*) erhalten Sie Ihre Nil, die die gleiche ist wie List.empty[A] (wenn der Typ ist falsch eingegeben).

3

Nil ist ein List:

scala> Nil 
res11: scala.collection.immutable.Nil.type = List() 

jedoch Scala wickelt alle variadische Parameter in eine Seq (WrappedArray implementiert Seq), das ist, warum Sie die MatchError bekommen. Sie könnten Ihre Funktion in der folgenden Weise umschreiben:

scala> def concat[A](lists : Traversable[A]*):List[A]={ 
    |  lists match { 
    |  case Seq() => Nil 
    |  case x +: Seq() => (x :\ List.empty[A])((elem,list)=> elem::list) 
    |  case x +: xs => (x :\ concat(xs:_*))((elem,list)=> elem :: list) 
    |  } 
    | } 
concat: [A](lists: Traversable[A]*)List[A] 

scala> concat(List(1), List(2), List(3)) 
res9: List[Int] = List(1, 2, 3) 

Sie können auch Ihre Funktion flatMap vereinfachen:

scala> def concat[A](lists: Traversable[A]*): List[A] = { 
    |  lists.flatMap(x => x).toList 
    | } 
concat: [A](lists: Traversable[A]*)List[A] 

scala> concat(List(1), List(2), List(3)) 
res16: List[Int] = List(1, 2, 3) 
+0

Er konnte es auch vereinfachen tun 'lists.toList.flatten' aber wahrscheinlich wasn 't der Punkt hier :) –

+0

Sicher, deshalb habe ich es am Ende des Beitrags erwähnt :) – soon

+1

'lists.flatMap (x => x)' == 'lists.flatten' – Dima