2016-08-29 24 views
5

Po prostu spojrzałem na deklarację List.flatMap i byłem trochę zaskoczony tym.Zrozumienie deklaracji flatMap na liście

final override def flatMap[B, That](f: A => GenTraversableOnce[B]) 
       (implicit bf: CanBuildFrom[List[A], B, That]): That 

Gdzie object List określa:

implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, List[A]] = 
    ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]] 

Jeśli więc powołać flatMap na List otrzymamy List i nie widzę żadnego sensu w That typu, jeżeli będzie to zawsze można wywnioskować do List[B] (z powodu implicit).

+0

że 'może być przesłonięta implicit' o bardziej lokalnym jednego, który opiera się na, powiedzmy, od' Vector' lub jawnie przekazany jako parametr – Chirlo

+0

Zobacz http: // stackoverflow .com/a/1728140/5344058 na przykład, kiedy przydatne jest posiadanie 'To' będącego innego typu w porównaniu do oryginalnej kolekcji –

+0

Czy widziałeś kiedyś' collection.breakout' z płaską mapą lub mapą ... to jest jeden przykład przesłaniania tego niejawnego. –

Odpowiedz

5

Jeśli więc powołać flatMap na List[A] otrzymamy List[A] a ja nie widzę sensu w That typu, jeżeli będzie to zawsze można wywnioskować do List[B]

Jedną rzeczą, którą” brakuje również tego, że flatMap nie jest faktycznie zdefiniowany na List[+A]. Jest dziedziczony od TraversableLike, co jest cechą używaną przez większość kolekcji Scalas. Każdy z nich może dostarczyć ukryty CanBuildFrom, który może zostać nadpisany, aby dostarczyć inny wynikowy zbiór.

Jeśli chcesz trochę smak tego, co można zrobić z niestandardowych CanBuildFrom:

scala> :pa 
// Entering paste mode (ctrl-D to finish) 

import scala.collection.generic.CanBuildFrom 
import scala.collection.immutable._ 
import scala.collection.mutable 
import scala.{List, Vector} 

implicit val listToVectorCBF = new CanBuildFrom[List[Int], Int, Vector[Int]] { 
    override def apply(from: List[Int]): mutable.Builder[Int, Vector[Int]] = this.apply() 
    override def apply(): mutable.Builder[Int, Vector[Int]] = Vector.newBuilder 
} 

// Exiting paste mode, now interpreting. 

scala> List(1,2,3).flatMap(List(_)) 
res6: Vector[Int] = Vector(1, 2, 3) 
+0

Ale czy mogę dostarczyć własny "CanBuildFrom', aby uzyskać coś, co nie jest nawet Seq? – user3663882

+0

@ user3663882 Nic nie powstrzymuje cię od dostarczania własnych. Ułatwia to klasom pochodnym proste dostarczanie własnych implantów CBF zamiast ponownego wdrażania metody. –

+0

@ user3663882 Dodałem mały przykład do wykazania. –

2

Cóż ... To implicit CanBuildFrom mogą być wykorzystane do bezpośredniego zbudować inny rodzaj konstrukcji zamiast List i oszczędzając w ten sposób jedną dodatkowy krok. Spójrzmy na poniższy przykład,

val list = List(List(1, 2, 3), List(4, 5, 6)) 

// now if we do a flatmap withtout overriding the implicit CanBuildFrom 
val newList = list.flatMap(l => l.map(i => (i,i))) 

// new list will be a List[(Int, Int)] 
// but what if you wanted a map 

val newMap = newList.toMap 

// But you actually needed to traverse the list twice in this case 

// But we can avoid the second traversal if we chose to override the implicit 
val newMap2 = list.flatMap(l => l.map(i => (i,i)))(collection.breakout)