2

我想将以下代码从 Scala 2.12 迁移到 2.13

给定任何元组集合Coll[(A, B)]和一个方法f: B => IterableOnce[C],我想Coll[(A, C)]通过应用f元组的第二个元素来产生一个。

implicit class TuplesOps[A, B, Repr <: Traversable[(A, B)]](val s: TraversableLike[(A, B), Repr]) extends AnyVal {
  def flatMapValues[C, That](f: B => TraversableOnce[C])(implicit bf: CanBuildFrom[Repr, (A, C), That]) =
    s.flatMap { case (a, b) => f(b).map((a, _))}
}

我知道集合 API 在 2.13 中发生了变化,我读到:https ://docs.scala-lang.org/overviews/core/custom-collection-operations.html

我尝试了 2 个实现

第一的

import scala.collection.{ AbstractView, BuildFrom }
import scala.collection.generic.IsSeq

object Evidence extends App {
  class TuplesOps[Repr, S <: IsSeq[Repr]](coll: Repr, seq: S) {
    def flatMapValues[B, C, D, That](f: C => IterableOnce[D])(implicit bf: BuildFrom[Repr, (B, D), That], ev: seq.A =:= (B, C)): That = {
      val seqOps = seq(coll)
      bf.fromSpecific(coll)(new AbstractView[(B, D)] {
        override def iterator: Iterator[(B, D)] = {
          seqOps.flatMap { x => f(ev(x)._2).map((ev(x)._1, _))}.iterator
        }
      })
    }
  }

  implicit def TuplesOps[Repr](coll: Repr)(implicit seq: IsSeq[Repr]): TuplesOps[Repr, seq.type] =
    new TuplesOps(coll, seq)

  List("a"->1, "b"->2).flatMapValues{(x:Int) => Seq.fill(x)("x")}
}

我有这个编译错误:

[error] /home/yamo/projects/perso/coll213/src/main/scala/example/Evidence.scala:22:37: diverging implicit expansion for type scala.collection.BuildFrom[List[(String, Int)],(B, String),That]
[error] starting with method Tuple9 in object Ordering
[error]   List("a"->1, "b"->2).flatMapValues{(x:Int) => Seq.fill(x)("x")}

第二

import scala.collection.{ AbstractView, BuildFrom }
import scala.collection.generic.IsSeq

object Aux extends App {
  type Aux[Repr, B, C] = IsSeq[Repr] { type A = (B, C)}

  class TuplesOps[Repr, B, C, S <: Aux[Repr, B, C]](coll: Repr, seq: S) {
    def flatMapValues[D, That](f: C => IterableOnce[D])(implicit bf: BuildFrom[Repr, (B, D), That]): That = {
      val seqOps = seq(coll)
      bf.fromSpecific(coll)(new AbstractView[(B, D)] {
        // same as before
        override def iterator: Iterator[(B, D)] = {
          seqOps.flatMap { case (b, c) => f(c).map((b, _))}.iterator
        }
      })
    }
  }

  implicit def TuplesOps[Repr, B, C](coll: Repr)(implicit seq: Aux[Repr, B, C]): TuplesOps[Repr, B, C, seq.type] =
    new TuplesOps(coll, seq)

  List("a"->1, "b"->2).flatMapValues(Seq.fill(_)("x"))
}

我有这个编译错误

[error] /home/yamo/projects/perso/coll213/src/main/scala/example/Aux.scala:24:24: value flatMapValues is not a member of List[(String, Int)]
[error]   List("a"->1, "b"->2).flatMapValues(Seq.fill(_)("x"))

如果可能的话,你能帮我让这两种解决方案都能奏效吗?

4

1 回答 1

3

我同意,自定义集合说明不是他们应该的。

我发现该Factory方法比 . 更容易一些BuildFrom,但我仍然习惯了它们中的一个/两个。

import scala.collection.Factory

implicit
class TuplesOps[A,B,CC[x] <: Iterable[x]](val ts: CC[(A,B)]) {
  def flatMapValues[C](f: B => IterableOnce[C]
                      )(implicit fac: Factory[(A,C), CC[(A,C)]]
                       ): CC[(A,C)] =
    ts.flatMap{case (a,b) => f(b).iterator.map(x => (a,x))}
      .to[CC[(A,C)]](fac)
}

这通过了我所有的简单(头脑)测试。它不会在 anArray或 an上工作,Iterator但你原来的 Scala 2.12.x 版本也没有(对我来说)所以我认为没关系。

于 2021-03-12T10:42:23.650 回答