3

关于这个问题,我可以通过unzip一个List[(A,B)]

但是,现在我发现需要在 aList[( (A,B),(C,D) )]或 a上进行多重分配List[(A,B,C,D)]

我看到有一个 unzip 用于成对,一个 unzip3 用于三胞胎,但是如何解构一对Tuple2OR 一个Tuple4以便进行多重分配?我将相应地调整下面的集合类型,但无论哪一个适用于 1 步多分配都可以。

// foo can be a List[(A,B,C,D)] OR List[( (A,B),(C,D) )]
val(a,b,c,d) = foo.unzip

这有效,但被黑了

val(a,b,c_d) foo.unzip3 // foo is a List[(A,B,(C,D))]

因为我最终不得不c_d._1c_d._2,我试图通过多重分配变量来避免的符号

4

5 回答 5

6

也许这是不言而喻的,但如果你不介意多个步骤,有一种简单的方法可以做到这一点:

val foo = List((1 -> "w", 'x -> 2.0), (101 -> "Y", 'Z -> 3.0))
val (p, q) = foo.unzip
val (a, b) = p.unzip
val (c, d) = p.unzip

如果你真的想要一个单行,你将不得不求助于 Scalaz 之类的东西,它Bifunctor为元组提供了一个实例,可以让你编写这个,例如:

 import scalaz._, Scalaz._

 val ((a, b), (c, d)) = foo.unzip.bimap(_.unzip, _.unzip)

这与上面的版本基本相同,但是bimap让我们可以在一行中完成所有操作。

于 2012-10-22T12:50:37.150 回答
3

You don't actually need any implicit conversions here. The trick is to take advantage of custom extractor objects, like so:

object Unzipped4 {

  def unapply[A, B, C, D](ts: List[(A, B, C, D)]): Some[(List[A], List[B], List[C], List[D])] =
    Some((ts map _._1, ts map _._2, ts map _._3, ts map _._4))

}

You then use it like this:

val Unzipped4(as, bs, cs, ds) = foo

You could actually expand this to an arbitrary Product by using the dynamic access methods on that class, but you'd lose some type safety in the process.

于 2012-10-22T16:27:39.430 回答
2

除了我玩过的其他很棒的答案之外,我还考虑过嵌套和通用解压缩。我的方法使用类型类并且像元组一样失去了数量和类型安全性productIterator。也许有人可以使用HListfrom shapeless来调整它以进行救援。还必须实现我的库以unzip在集合上使用的 pimp 以返回正确的(相同的)集合类型unzip被调用并摆脱Iterable,但我在这里省略了这一点,仅显示嵌套的 arity-generic 解压缩的想法。如果给定类型没有具体的隐式转换为 Unzippable ,也许可以使用某种LowerPriorityImplicits来隐式转换AUnzippable[A,A]

trait Unzippable[T, +Super] {
  def unzip(t: T): Iterable[Super]
}

implicit object IntUnzippable extends Unzippable[Int, Int] { def unzip(i: Int) = Seq(i) }
implicit object BooleanUnzippable extends Unzippable[Boolean, Boolean] { def unzip(b: Boolean) = Seq(b) }
implicit object StringUnzippable extends Unzippable[String, String] { def unzip(s: String) = Seq(s) }

implicit def Tuple2Unzippable[Super, A <: Super, B <: Super, S, S1 <: S, S2 <: S](implicit ev1: Unzippable[A, S1], ev2: Unzippable[B, S2]) = new Unzippable[(A, B), S] {
  def unzip(t: (A, B)): Iterable[S] = ev1.unzip(t._1) ++ ev2.unzip(t._2)
}

def unzip[A, Super](i: Iterable[A])(implicit ev: Unzippable[A, Super]): Iterable[Iterable[Super]] = i.map(ev.unzip).transpose

object MyTuple3 {
  def unapply[X](i: Iterable[X]): Option[(X, X, X)] = if (i.size != 3) return None else Some((i.head, i.drop(1).head, i.last))
}

val list = (1, ("A", true)) :: (2, ("B", false)) :: (3, ("C", true)) :: Nil
val MyTuple3(nums, letters, bools) = unzip(list)
println((nums, letters, bools)) // (List(1, 2, 3),List(A, B, C),List(true, false, true))
于 2012-10-22T18:41:32.570 回答
2

因为只有unzipand unzip3,你为什么不为此写一个扩展?像这样的东西应该可以工作(2.10代码):

implicit class Unzip4[A,B,C,D](val xs: List[(A,B,C,D)]) extends AnyVal {
  def unzip4: (List[A], List[B], List[C], List[D]) = xs.foldRight[(List[A], List[B], List[C], List[D])]((Nil,Nil,Nil,Nil)) { (x, res) =>
    val (a,b,c,d) = x
    (a :: res._1, b :: res._2, c :: res._3, d :: res._4)
  }
}
于 2012-10-22T11:06:41.257 回答
2

您可以添加自己的unzip4方法。

import scala.collection._
import generic._

class Unzipper[A, CC[X] <: GenTraversable[X]](s: GenericTraversableTemplate[A, CC]) {
  def unzip4[A1, A2, A3, A4](implicit asQuad: A => (A1, A2, A3, A4)): (CC[A1], CC[A2], CC[A3], CC[A4]) = {
    val b1 = s.genericBuilder[A1]
    val b2 = s.genericBuilder[A2]
    val b3 = s.genericBuilder[A3]
    val b4 = s.genericBuilder[A4]
    for (e <- s) {
      val (a, b, c, d) = asQuad(e)
      b1 += a
      b2 += b
      b3 += c
      b4 += d
    }
    (b1.result, b2.result, b3.result, b4.result)
  }
}

implicit def toUnzipper[A, CC[X] <: GenTraversable[X]](s: GenericTraversableTemplate[A, CC]) = new Unzipper(s)
implicit def t2t2Tot4[A1, A2, A3, A4](tt: ((A1, A2), (A3, A4))) = tt match { case ((a, b), (c, d)) => (a, b, c, d) }
implicit def t1t3Tot4[A1, A2, A3, A4](tt: (A1, (A2, A3, A4))) = tt match { case (a, (b, c, d)) => (a, b, c, d) }
implicit def t3t1Tot4[A1, A2, A3, A4](tt: ((A1, A2, A3), A4)) = tt match { case ((a, b, c), d) => (a, b, c, d) }

用法:

scala> List((1, 2, 3, 4)).unzip4
res0: (List[Int], List[Int], List[Int], List[Int]) = (List(1),List(2),List(3),List(4))

scala> List((1, 2) -> (3, 4)).unzip4
res1: (List[Int], List[Int], List[Int], List[Int]) = (List(1),List(2),List(3),List(4))

scala> List(1 -> (2, 3, 4)).unzip4
res2: (List[Int], List[Int], List[Int], List[Int]) = (List(1),List(2),List(3),List(4))

scala> List((1, 2, 3) -> 4).unzip4
res3: (List[Int], List[Int], List[Int], List[Int]) = (List(1),List(2),List(3),List(4))
于 2012-10-22T11:17:51.423 回答