【问题标题】:Variable multi-assign via unzip on List[( (A,B),(C,D) )], or List[(A,B,C,D)]通过解压缩 List[( (A,B),(C,D) )] 或 List[(A,B,C,D)] 进行变量多重赋值
【发布时间】:2019-02-15 06:29:03
【问题描述】:

关于this question,我可以通过unzipList[(A,B)] 上进行多重分配

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

我看到有一个 unzip 用于成对,一个 unzip3 用于三胞胎,但是如何解构一对 Tuple2 或单个 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,我正试图通过多重分配变量来避免这种符号

【问题讨论】:

  • 我不是反对者,但我同意这个问题需要一些工作。例如,List 只有一个类型参数,所以 List[(A, B), (C, D)] 的含义有点不清楚——foo 的静态类型也是如此,因为它显然不能同时是 List[(A, B, C, D)]List[((A, B), (C, D))] .
  • 谢谢,“或”表示“List[(A,B,C,D)]”或“List[((A,B),(C,D))]”之一会做。基本问题似乎是解压缩时您必须使用对或三元组,并且超出单个 Tuple2 或 Tuple3 的多重分配变得困难,如果不是不可能的话 AFAIK
  • 但是(A, B, C, D)((A, B), (C, D)) 的最小上限是Product,所以foo 必须是List[Product],这几乎没用。除非你的意思是你想要两者都使用相同的语法——不是说你真的有一个变量可以是任何一个?
  • 我可以将结果类型(来自数据库查询)设置为任何我想要的类型。 List[Product] 确实没用,因此是个问题。使用 List[Tuple2] 时,您可以解压缩、多重分配,并保留类型。只要您转到包含一对 Tuple2 的 Tuple4 列表或列表,类型就会丢失。我可以使用 List[Tuple3] 结果并对其进行 unzip3 解压缩,因为第三个参数变成了我不喜欢的 C&D 的散列,所以被黑了
  • +1 只是为了平衡无法解释的反对票,无论是谁投的。

标签: scala list tuples transform


【解决方案1】:

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

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 让我们可以在一行中完成所有操作。

【讨论】:

  • +1,是的,我已经采用了 2 步方法,只是 1-liners 的傻瓜。回复:bimap,在某些时候我需要深入了解 Scalaz,显然那里有很多有用的东西。
【解决方案2】:

这里实际上不需要任何隐式转换。诀窍是利用自定义提取器对象,如下所示:

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

}

然后你像这样使用它:

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

您实际上可以通过使用该类上的动态访问方法将其扩展为任意 Product,但在此过程中您会失去一些类型安全性。

【讨论】:

  • +1,你的手柄响了,我记得为什么,开箱即用的想法
  • ts map _._1 需要写成ts.map(_._1)ts map (_._1)
【解决方案3】:

既然只有unzipunzip3,你为什么不为此写一个扩展?这样的东西应该可以工作(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)
  }
}

【讨论】:

  • +1,很好,这显然比我使用 Tuple3/unzip3 方法的工作解决方案更优雅。在 2.9.2 上,除非在 2.10 上,否则听起来像这样不会飞
  • 你只需要自己创建所有隐式转换的东西
  • 这是一个一次性问题,生成一个游戏时间表,其中容器案例类具有 4 个 Seq 属性,其中 2 个是案例类,1 个是 Int,最后一个是 String。通常我可以通过 List[TupleN] 映射并创建容器案例类的列表(不喜欢“tuple._n”表示法),但在这种情况下并不容易,因此 unzip4 请求
【解决方案4】:

您可以添加自己的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))

【讨论】:

  • +1,好的,在控制台中验证,有效。希望可以利用现有的馆藏图书馆,但显然不是......
【解决方案5】:

除了我玩过的其他很棒的答案之外,我还考虑过嵌套和通用解压缩。我的方法使用类型类并失去了元组上的 productIterator 之类的元组和类型安全性。也许有人可以使用来自shapelessHList 来调整它以进行救援。还必须实现我的库的皮条客,以在集合上使用unzip 以返回正确的(相同)集合类型unzip 被调用并摆脱Iterable,但我在这里省略了这个只是为了展示这个想法嵌套的 arity-generic 解压缩。如果给定类型没有具体的隐式转换为 Unzippable,也许可以使用某种 LowerPriorityImplicits 将任何 A 隐式转换为 Unzippable[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))

【讨论】:

  • 基本上为所有答案 +1,因为至少到目前为止,他们都表现出很大的努力,更不用说创作者的专业知识了。至于我是否了解这些错综复杂的东西,此时,不;-) 有趣的是,一个简单的问题(开始时要减去 1)如何引起了几个 scala 重量级人物的兴趣......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-14
  • 2012-01-27
  • 2016-09-16
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 2011-03-26
相关资源
最近更新 更多