volver unidades unidad todas sesion reiniciar puede pudieron problemas por mapear las iniciar desconecta desaparece conectar conecta compartida carpeta automaticamente scala collections functional-programming

scala - unidades - Evitar la eliminación accidental de duplicados al mapear un conjunto



problemas conectar unidad de red windows 10 (6)

Como no arrastrar dependencias adicionales a la imagen:

(0 /: students) { case (sum, s) => sum + s.age }

Me gustan mucho los conceptos de programación funcional, pero he sido mordido en dos ocasiones por el mismo gotcha, cuando mapeo a través de una colección que resulta ser un Set (es decir, elimina automáticamente los duplicados). El problema es que después de transformar los elementos de dicho conjunto, el contenedor de salida también es un conjunto, y así elimina cualquier duplicado de la salida transformada .

Una sesión REPL muy breve para ilustrar el problema:

scala> case class Person(name: String, age: Int) defined class Person scala> val students = Set(Person("Alice", 18), Person("Bob", 18), Person("Charles", 19)) students: scala.collection.immutable.Set[Person] = Set(Person(Alice,18), Person(Bob,18), Person(Charles,19)) scala> val totalAge = (students map (_.age)).sum totalAge: Int = 37

Por supuesto, esperaría que la edad total fuera 18 + 18 + 19 = 55, pero debido a que los estudiantes estaban almacenados en un Set , también lo fueron sus edades después del mapeo, por lo tanto, uno de los 18 s desapareció antes de que se sumaran las edades.

En código real, esto es a menudo más insidioso y más difícil de detectar, especialmente si escribe código de utilidad que simplemente toma un Traversable y / o utiliza el resultado de métodos que se declaran para devolver un Traversable (cuya implementación es un conjunto) . Me parece que estas situaciones son casi imposibles de detectar de manera confiable, hasta / a menos que se manifiesten como un error.

Entonces, ¿hay algunas mejores prácticas que reduzcan mi exposición a este problema ? ¿Me equivoco al pensar en map mapas sobre un Traversable general como la transformación conceptual de cada elemento en su lugar, en lugar de agregar los elementos transformados a su vez en una nueva colección? ¿Debo llamar a .toStream en todo antes del mapeo, si quiero mantener este modelo mental?

Cualquier consejo / recomendación sería muy apreciado.

Actualización : la mayoría de las respuestas hasta ahora se han centrado en la mecánica de incluir los duplicados en la suma. Estoy más interesado en las prácticas que intervienen al escribir el código en el caso general: ¿te has entrenado para llamar siempre a la toList de todas las colecciones antes de llamar al map ? ¿Comprueba minuciosamente las clases concretas de todas las colecciones en su aplicación antes de llamar a métodos sobre ellas? Etc.

Reparar algo que ya ha sido identificado como un problema es trivial : la parte difícil es evitar que estos errores se arraiguen en primer lugar.


Es posible que toIterable utilizar los métodos toIterable o toList para convertir primero el conjunto a otra estructura de datos. http://www.scala-lang.org/api/current/scala/collection/immutable/Set.html

(Tenga en cuenta que toIterable puede devolver cualquier Iterable, aunque la implementación de referencia no lo hará, de acuerdo con la documentación vinculada. @Debilski me informa en los comentarios que, no obstante, devuelve un conjunto).


Es posible que desee usar scalaz foldMap para este fin, ya que funciona en cualquier cosa para la que haya una clase de tipo Foldable disponible. El uso en su caso se verá así:

persons foldMap (_.age)

La firma de foldMap es la siguiente:

trait MA[M[_], A] { val value: M[A] def foldMap[B](f: A => B)(implicit f: Foldable[M], m: Monoid[B]) }

Asi que; siempre que tenga una colección CC[A] donde CC se puede plegar (es decir, atravesado ), una función de A => B donde B es un monoide, puede acumular un resultado.


Puede romper el tipo de colección

scala> import collection.breakOut import collection.breakOut scala> val ages = students.map(_.age)(breakOut): List[Int] ages: List[Int] = List(18, 18, 19)

Entonces puedes sumar como se esperaba

Con base en la actualización de la pregunta, la mejor práctica para evitar estos tipos de errores es una buena cobertura de prueba unitaria con datos representativos, junto con API razonables junto con el conocimiento de cómo el compilador scala mantiene los tipos de fuente a través de mapas / generadores, etc. Estás devolviendo un conjunto de algo que deberías hacer obvio, ya que devolver una colección / Traversable está ocultando un detalle de implementación relevante.


Si te das cuenta repetidamente del mismo error, tu primer problema no es el error, sino que te estás repitiendo a ti mismo. map().sum es un caso de uso bastante común (particularmente en contextos de análisis de datos) para merecer su propio método en Traversable. Desde mi clase de chulo Traversable personal, nunca voy a ninguna parte sin él.

implicit def traversable2RichTraversable[A](t: Traversable[A]) = new { ///many many methods deleted def sumOf[C: Numeric](g: A => C): C = t.view.toList.map(g).sum ///many many more methods deleted }

(Eso .view puede no ser necesario, pero no puede doler).


Una forma torpe pero posiblemente más rápida de transformarlo (en comparación con un explícito toList / toSeq ) sería usar collection.breakOut ( más información ) con un tipo de adscripción

(students map (_.age))(collection.breakOut) : Seq[Int]