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!)