scala generics type-conversion polymorphism scala-collections

scala - ¿Cómo hacer que el método devuelva el mismo genérico que la entrada?



generics type-conversion (3)

Existe un método en Scala to que se puede transformar una colección arbitraria en otra, siempre que exista una clase de tipos llamada CanBuildFrom en el alcance.

import scala.collection.generic.CanBuildFrom import scala.languageFeature.higherKinds def genericSplitByComma[S[_]](s: String)(implicit cbf: CanBuildFrom[Nothing, String, S[String]]): S[String] = { s.split(",").to[S] } genericSplitByComma[Set]("Hello, hello") //Set(Hello, hello) genericSplitByComma[List]("Hello, hello") //List(Hello, hello) genericSplitByComma[Array]("Hello, hello") //Array(hello, world!)

No necesitamos restringir S[_] porque esta función no se compilará si no hay un objeto CanBuildFrom adecuado. Por ejemplo, esto fallará:

genericSplitByComma[Option]("Hello, hello")

A continuación también fallará porque nuestro constructor de tipo S[_] acepta solo un argumento de tipo y el mapa espera dos:

genericSplitByComma[Map]("Hello, hello")

Como notaron Luis Miguel Mejía Suárez y Dmytro Mitin, hubo un gran refactor en las colecciones en la Scala 2.13 recién lanzada, por lo que funcionará hasta la Scala 2.12.

Quiero dividir una cadena delimitada por comas y usar el resultado como Seq o Set :

def splitByComma(commaDelimited: String): Array[String] = commaDelimited.trim.split('','') def splitByCommaAsSet(commaDelimited: String): Set[String] = splitByComma(commaDelimited).toSet def splitByCommaAsSeq(commaDelimited: String): Seq[String] = splitByComma(commaDelimited).toSeq val foods = "sushi,taco,burrito" val foodSet = splitByCommaAsSet(foods) // foodSet: scala.collection.immutable.Set[String] = Set(sushi, taco, burrito) val foodSeq = splitByCommaAsSeq(foods) // foodSeq: Seq[String] = List(sushi, taco, burrito)

Sin embargo, esto es bastante repetitivo. Idealmente, me gustaría tener algo como genericSplitByComma[Set](foods) que solo devuelve un Set cuando paso Set , y devuelve un Seq cuando paso Seq .


Hay una solución simple para esto. No es exactamente la sintaxis solicitada, pero es igual de concisa y debería ser compatible con 2.13.

def simpleSplitByComma(coll :Iterable[String]) = coll.flatMap(_.trim.split(",")) simpleSplitByComma(Set("hello,world")) //res0: Set(hello, world) simpleSplitByComma(Seq("bellow,world")) //res1: List(bellow, world) simpleSplitByComma(Array("fellow,old")) //res2: ArrayBuffer(fellow, old) simpleSplitByComma(Stream("end,of,the,world")) //res3: Stream(end, ?)


La respuesta de @ KrzysztofAtłasik funciona muy bien para Scala 2.12 .
Esta es una solución para 2.13 . (No estoy completamente seguro de si esta es la mejor manera) .

import scala.collection.Factory import scala.language.higherKinds def splitByComma[C[_]](commaDelimited: String)(implicit f: Factory[String, C[String]]): C[String] = f.fromSpecific(commaDelimited.split(",")) // Or, as Dmytro stated, which I have to agree looks better. commaDelimited.split(",").to(f)

Que puedes usar así:

splitByComma[Array]("hello,world!") // res: Array[String] = Array(hello, world!) splitByComma[Set]("hello,world!") // res: Set[String] = Set(hello, world!) splitByComma[List]("hello,world!") // res: List[String] = List(hello, world!)