繁体   English   中英

返回指定类型的通用Traversable

[英]Return a generic Traversable of a specified type

我希望能够通用地操纵T[_] <: Traversable类的类型,以便我可以执行诸如map和filter之类的事情,但是我想推迟尽可能长地选择哪种Traversable的决定。

我希望能够针对返回T[Int]而不是Traversable[Int]的通用T[Int]编写函数。 因此,例如,我想将一个函数应用于Set[Int]Vector[Int]或任何扩展Traversable并返回该类型的函数。

我首先尝试以一种简单的方式执行此操作,例如:

trait CollectionHolder[T[_] <: Traversable[_]] {

  def easyLessThanTen(xs: T[Int]): T[Int] = {
    xs.filter(_ < 10)
  }
}

但这不会编译:扩展功能缺少参数类型。 但是,如果函数采用Traversable[Int]而不是T[Int] ,它将进行编译,因此以为我可以使用Traversable并将其转换为T 这导致我转向CanBuildFrom

object DoingThingsWithTypes {    

  trait CollectionHolder[T[_] <: Traversable[_]] {

    def lessThanTen(xs: T[Int])(implicit cbf: CanBuildFrom[Traversable[Int], Int, T[Int]]): T[Int] = {

      val filteredTraversable = xs.asInstanceOf[Traversable[Int]].filter(_ < 10)

      (cbf() ++= filteredTraversable).result
}

编译。 但是然后在我的测试中:

val xs = Set(1, 2, 3, 4, 1000)

object withSet extends CollectionHolder[Set]

withSet.lessThanTen(xs) shouldBe Set(1, 2, 3, 4)

我收到以下编译器错误:

无法基于Traversable [Int]类型的集合构造具有Int类型元素的Set [Int]类型集合。 方法lessThanTen没有足够的参数:(隐式cbf:scala.collection.generic.CanBuildFrom [Traversable [Int],Int,Set [Int]])Set [Int]。 未指定的值参数cbf。

在哪里可以获得CanBuildFrom进行转换? 或者更好的是,如何修改所需结果的简单方法? 还是我需要使用一个类型类为每个我感兴趣的Traversable(一个用于Set,一个用于Vector等)编写隐式实现? 如果可能的话,我宁愿避免使用最后一种方法。

使用(Scala 2.12.8)标准库而不是cats / scalaz / etc。 您需要查看GenericTraversableTemplate filter未在此处定义,但可以轻松定义为:

import scala.collection.GenTraversable
import scala.collection.generic.GenericTraversableTemplate

trait CollectionHolder[T[A] <: GenTraversable[A] with GenericTraversableTemplate[A, T]] {

  def lessThanTen(xs: T[Int]): T[Int] = {
    filter(xs)(_ < 10)
  }

  def filter[A](xs: T[A])(pred: A => Boolean) = {
    val builder = xs.genericBuilder[A]
    xs.foreach(x => if (pred(x)) { builder += x })
    builder.result()
  }
}

在评论中,您提到nonEmpty并且exists 由于GenTraversable类型绑定,它们可用。 真正的filter也是,问题在于它返回GenTraversable[A]而不是T[A]

Scala 2.13重做了集合,因此那里的方法可能略有不同,但是我还没有足够地了解它。

另外: T[_] <: Traversable[_]可能不是您想要的,而不是T[A] <: Traversable[A] ; 例如,如果您有T[Int] <: Traversable[String]则不违反第一个约束。

是的,我是说您应该使用typeclasss
但是,您不必实现它们,也不必提供所需类型的实例。 因为,这些非常常见,可以在catsscalaz类的库中找到。

例如,使用cats

import cats.{Traverse, TraverseFilter}
import cats.syntax.all._ // Provides the nonEmpty, filter & map extension methods to C.

import scala.language.higherKinds

def algorithm[C[_]: TraverseFilter: Traverse](col: C[Int]): C[Int] =
  if (col.nonEmpty)
    col.filter(x => x < 10)
  else
    col.map(x => x * 2) // nonsense, but just to show that you can use map too.

您可以这样使用:

import cats.instances.list._

algorithm(List(1, 200, 3, 100))
// res: List[Int] = List(1, 3)

这可能是值得加入,有很多其他的方法,如existsfoldLeftsize等。
看一下文档 而且,如果这是您第一次使用catsscalaz或一般概念,那么您可能会发现scala-with-cats非常有启发性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM