15

有时需要从小集合创建元组(例如 scalding 框架)。

def toTuple(list:List[Any]):scala.Product = ...
4

6 回答 6

15

你真的不希望你的方法返回Product,因为这是无用的模糊。如果您希望能够将返回的对象用作元组,那么您必须知道它的数量。所以你可以做的是toTupleN针对不同的arities有一系列的方法。为方便起见,您可以将这些作为隐式方法添加到Seq.

这个怎么样:

class EnrichedWithToTuple[A](elements: Seq[A]) {
  def toTuple2 = elements match { case Seq(a, b) => (a, b) }
  def toTuple3 = elements match { case Seq(a, b, c) => (a, b, c) }
  def toTuple4 = elements match { case Seq(a, b, c, d) => (a, b, c, d) }
  def toTuple5 = elements match { case Seq(a, b, c, d, e) => (a, b, c, d, e) }
}
implicit def enrichWithToTuple[A](elements: Seq[A]) = new EnrichedWithToTuple(elements)

并像这样使用它:

scala> List(1,2,3).toTuple3
res0: (Int, Int, Int) = (1,2,3)
于 2012-07-03T06:05:00.300 回答
14

如果正如@dhg 所观察到的,您预先知道预期的数量,您可以在这里做一些有用的事情。使用shapeless你可以写,

scala> import shapeless._
import shapeless._

scala> import Traversables._
import Traversables._

scala> import Tuples._
import Tuples._

scala> List(1, 2, 3).toHList[Int :: Int :: Int :: HNil] map tupled
res0: Option[(Int, Int, Int)] = Some((1,2,3))
于 2012-07-03T08:41:01.610 回答
12

如果你不知道前面的 arity 并想做一个可怕的骇客,你可以这样做:

def toTuple[A <: Object](as:List[A]):Product = {
  val tupleClass = Class.forName("scala.Tuple" + as.size)
  tupleClass.getConstructors.apply(0).newInstance(as:_*).asInstanceOf[Product]
}
toTuple: [A <: java.lang.Object](as: List[A])Product

scala> toTuple(List("hello", "world"))
res15: Product = (hello,world)
于 2012-07-03T13:41:33.257 回答
4

你想要一个Tuple还是只是一个Product. 因为对于后者:

case class SeqProduct[A](elems: A*) {
  override def productArity: Int = elems.size
  override def productElement(i: Int) = elems(i)
}

SeqProduct(List(1, 2, 3): _*)
于 2012-07-03T15:28:22.840 回答
1

基于@Kim Stebel 的想法,我编写了一个从 seq 创建元组的简单实用程序。

import java.lang.reflect.Constructor

/**
 * Created by Bowen Cai on 1/24/2015.
 */
sealed trait Product0 extends Any with Product {

  def productArity = 0
  def productElement(n: Int) = throw new IllegalStateException("No element")
  def canEqual(that: Any) = false
}
object Tuple0 extends Product0 {
  override def toString() = "()"
}

case class SeqProduct(elems: Any*) extends Product {
  override def productArity: Int = elems.size
  override def productElement(i: Int) = elems(i)
  override def toString() = elems.addString(new StringBuilder(elems.size * 8 + 10), "(" , ",", ")").toString()
}

object Tuples {

  private[this] val ctors = {
    val ab = Array.newBuilder[Constructor[_]]
    for (i <- 1 to 22) {
      val tupleClass = Class.forName("scala.Tuple" + i)
      ab += tupleClass.getConstructors.apply(0)
    }
    ab.result()
  }

  def toTuple(elems: Seq[AnyRef]): Product = elems.length match {
    case 0 => Tuple0
    case size if size <= 22 =>
      ctors(size - 1).newInstance(elems: _*).asInstanceOf[Product]
    case size if size > 22 => new SeqProduct(elems: _*)
  }

}
于 2015-01-24T17:32:52.530 回答
0
scala> val numbers = Seq(1,2,4)
numbers: Seq[Int] = List(1, 2, 4)

scala> val string = numbers.mkString("(",",",")")
string: String = (1,2,4)

*** mkString(start:String, sep: String, end: String)

我已经在我的 where-in-clause 中生成了它。

于 2020-01-09T10:03:10.273 回答