【问题标题】:How to make method return the same generic as the input?如何使方法返回与输入相同的泛型?
【发布时间】:2019-10-26 07:23:04
【问题描述】:

我想拆分以逗号分隔的字符串并将结果用作SeqSet

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)

但是,这是相当重复的。理想情况下,我希望有类似genericSplitByComma[Set](foods) 的东西,当我通过Set 时返回Set,当我通过Seq 时返回Seq

【问题讨论】:

    标签: scala generics type-conversion polymorphism scala-collections


    【解决方案1】:

    @KrzysztofAtłasik 的回答非常适合 Scala 2.12
    这是2.13 的解决方案。 (不完全确定这是否是最好的方法)

    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)
    

    你可以这样使用:

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

    【讨论】:

    • commaDelimited.split(",").to(f)
    • 2.13 快乐!我也想知道。 def splitByComma[C](s: String)(隐式 f: Factory[String, C]): C = s.split(",").to(f)
    • @DmytroMitin 哦,那是to[List]to(List) 的变化,因为它现在接收一个工厂,每个伴随对象现在都是一个工厂,我终于明白了:D
    【解决方案2】:

    Scala 中有一个名为 to 的方法,只要在范围内有名为 CanBuildFrom 的类型类,它就可以将任意集合转换为另一个集合。

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

    我们不需要约束S[_],因为如果范围内没有合适的CanBuildFrom,这个函数将无法编译。例如,这将失败:

    genericSplitByComma[Option]("Hello, hello")
    

    下面也会失败,因为我们的类型构造函数 S[_] 只接受一个类型参数,而映射需要两个:

    genericSplitByComma[Map]("Hello, hello")
    

    正如 Luis Miguel Mejía Suárez 和 Dmytro Mitin 所注意到的,在刚刚发布的 Scala 2.13 中的集合中有 major refactor,因此它适用于 Scala 2.12。

    【讨论】:

      【解决方案3】:

      有一个简单的解决方法。不完全是要求的语法,但同样简洁,它应该兼容 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, ?)
      

      【讨论】:

      • 现在我们必须拭目以待,看看 yhylord 将绿色支票放在哪里!
      • 我真的很喜欢这个并且可能会使用这样的东西,但我觉得我必须接受另一个与我所问的完全匹配的答案:)
      猜你喜欢
      • 2022-01-03
      • 2023-03-12
      • 2019-09-22
      • 2014-11-25
      • 2015-05-25
      • 1970-01-01
      • 2011-09-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多