Карта flatten и flatmap не эквивалентная
Я думал, что Scala construct map(f).flatten
эквивалентен flatMap(f)
. Но с этим примером это не так. Интересно, какова роль класса case в нем. Если я использую целые числа, то они эквивалентны. Но в моем случае я не могу.
case class CTest(v: Int)
val s = Set(Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2)))
val possibilities = s flatMap { m =>
val mapping = m flatMap {
case (label, destNodes) => destNodes map {
case nodes => (label, nodes) }
}
mapping
}
possibilities
Урожайность
Set((CTest(0),3), (CTest(1), 2))
тогда
case class CTest(v: Int)
val s = Set(Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2)))
val possibilities = s flatMap { m =>
val mapping = m map {
case (label, destNodes) => destNodes map {
case nodes => (label, nodes) }
}
mapping.flatten
}
possibilities
дает
Set((CTest(0),0), (CTest(0),3), (CTest(1),0), (CTest(1),2))
Любая идея, почему?
Ответы
Ответ 1
Взгляните на реализацию flatMap
:
def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
def builder = bf(repr) // extracted to keep method size under 35 bytes, so that it can be JIT-inlined
val b = builder
for (x <- this) b ++= f(x).seq
b.result
}
Результат flatMap
зависит от исходного типа коллекции и типа результата функции f
. В первом примере вы генерируете последовательность кортежей из карты, так что компилятор выбирает реализацию типа CanBuildFrom[Map[A, B], (C, D), Map[C, D]]
, которая предоставляет построитель для Map
, который вызывает перезапись одних и тех же ключей.
Вы можете напрямую преобразовать карту в обычную итерабельную, что даст результат, который вы хотите:
case class CTest(v: Int)
val s = Set(Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2)))
val possibilities = s flatMap { m =>
val mapping = m.toIterable.flatMap {
case (label, destNodes) => destNodes map {
case nodes => (label, nodes) }
}
mapping
}
possibilities
Ответ 2
Это происходит из-за промежуточных структур данных.
Я возьму простую версию вашего примера.
val m = Map(CTest(0) -> List(0, 3), CTest(1) -> List(0, 2))
При использовании flatMap
вы непосредственно создаете Map[CTest, Int]
scala> m flatMap {
| case (label, destNodes) => destNodes map {
| case nodes => (label, nodes) }
| }
res3: scala.collection.immutable.Map[CTest,Int] = Map(CTest(0) -> 3, CTest(1) -> 2)
Здесь, из-за уникальности клавиш Map
, (CTest(0), 0)
и (CTest(1), 0)
будут удалены из результата. когда вы flatMap
по множеству, вы получите Set
из Tuples
, которые были в Map
.
В вашем втором примере вы отображаете и сглаживаете.
val mapping = m map {
| case (label, destNodes) => destNodes map {
| case nodes => (label, nodes) }
| }
mapping: scala.collection.immutable.Iterable[List[(CTest, Int)]] = List(List((CTest(0),0), (CTest(0),3)), List((CTest(1),0), (CTest(1),2)))
mapping.flatten
res4: scala.collection.immutable.Iterable[(CTest, Int)] = List((CTest(0),0), (CTest(0),3), (CTest(1),0), (CTest(1),2))
Нет никакой Map
или другой уникальной сохраненной структуры данных, созданной в середине процесса. Поэтому значения не отбрасываются.