【问题标题】:flatMap on a sequence of tuples元组序列上的 flatMap
【发布时间】:2015-03-07 15:30:48
【问题描述】:

给定以下代码:

def flatMap2[A, B, C](s:Seq[(A, B)])(f: B => Seq[C]) : Seq[(A, C)] =
  s.flatMap { case (a, b) => f(b).map((a, _)) }

有没有更好的编码方式(可能在 scalaz 中退出)?

你能帮我找到一个更好的名字吗?

是否有更通用的抽象可供使用(IterableTraversableOnce)?

【问题讨论】:

    标签: scala collections


    【解决方案1】:

    这个怎么样:

    import scala.collection.GenTraversable
    import scala.collection.GenTraversableLike
    import scala.collection.GenTraversableOnce
    import scala.collection.generic.CanBuildFrom
    
    implicit class WithMapFlatMapValues[A, B, Repr <: GenTraversable[(A, B)]](val self: GenTraversableLike[(A, B), Repr]) extends AnyVal {
      def flatMapValues[C, That](f: B => GenTraversableOnce[C])(implicit bf: CanBuildFrom[Repr, (A, C), That]) = {
        self.flatMap { case (a, b) => f(b).toIterator.map(a -> _) } 
      }
    }
    

    所以你可以这样做:

    Vector("a"->1, "b"->2).flatMapValues(Seq.fill(_)("x"))
    // res1: Vector[(String, String)] = Vector((a,x), (b,x), (b,x))
    
    Set("a"->1, "b"->2).flatMapValues(Iterator.tabulate(_)(_+"x"))
    // res2: Set[(String, String)] = Set((a,0x), (b,0x), (b,1x))
    

    你也可以用 Iterator 来做(这样你就可以覆盖所有的 TraversableOnce):

    implicit class WithMapFlatMapValuesItr[A, B](val self: Iterator[(A, B)]) extends AnyVal {
      def flatMapValues[C](f: B => GenTraversableOnce[C]) = {
        for { (a, b) <- self; c <- f(b).toIterator } yield (a -> c)
      }
    }
    
    val i1 = Iterator("a"->1, "b"->2).flatMapValues(Seq.fill(_)("x"))
    // i1: Iterator[(String, String)] = non-empty iterator
    i1.toVector
    // res6: Vector[(String, String)] = Vector((a,x), (b,x), (b,x))
    

    【讨论】:

    • 有没有办法只有一个 impl ?
    • 为什么不只使用 Traversable 而不是 (GenTraversableOnce, GenTraversableLike) ?
    • 例如:隐式类 WithFlatMapValues[A, B](s: Traversable[(A, B)]) { def flatMapValues[C](f: B => TraversableOnce[C]): Traversable[ (A, C)] = s.flatMap { case (a, b) => f(b).map((a, _))} }
    • 使用 GenTraversable 意味着它可以处理比 Traversable 更多的东西。使用 GenTraversableLike/CanBuildFrom 使它像 Scala 的内置集合一样工作:生成与调用它相同的类型。您编写的小版本将始终返回 Traversable 类型的对象,而永远不会返回更具体的类型。
    【解决方案2】:

    看起来好一点:

    def flatMap2[A, B, C](List[(A, B)])(f: B => Seq[C]) : List[(A, C)] =
       for((a,b) <- s; bb <- f(b)) yield a -> bb
    

    但是您在这里尝试做的事情更像是多映射(保留顺序),而不仅仅是元组序列:

    def flatMap2[A, B, C](s: ListMap[A, List[B]])(f: B => List[C]) : Map[A, List[C]] = 
       s.mapValues(_.flatMap(f))
    
    scala> flatMap2(ListMap(2 -> List("a"), 1 -> List("c")))(x => List(x + "a"))
    res4: scala.collection.immutable.Map[Int,List[String]] = Map(2 -> List(aa), 1 -> List(ca))
    

    ListMap 在此处保存原始订单。 mapValues 返回ListMap 上的视图,这也将保留顺序。

    Scalaz 的幺半群可以帮助您将元素元素添加到此类 Map:

    yourListMap |+| ListMap(key -> List(value))
    
    scala>  val map = ListMap(2 -> List("a"), 1 -> List("c"))
    map: scala.collection.immutable.ListMap[Int,List[String]] = Map(2 -> List(a), 1 -> List(c))
    
    scala> (map: Map[Int,List[String]]) |+| ListMap(1 -> List("b"))
    res11: scala.collection.immutable.Map[Int,List[String]] = Map(2 -> List(a), 1 -> List(c, b))
    

    您可以使用Seq 代替List,但是您不会隐式转换为semigroup

    【讨论】:

      【解决方案3】:

      这个问题可能不清楚,在这一点上我会说:

      // s1: Seq[(A, B)], f: B => C
      val s2: Seq[(A, C)] = s1.map((a, b) => a -> f(b))
      

      【讨论】:

      • 这不会编译;即使修改为编译,它也没有实现期望的行为
      猜你喜欢
      • 1970-01-01
      • 2023-03-22
      • 2020-02-09
      • 2019-08-27
      • 1970-01-01
      • 2020-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多