2012-07-03 31 views

Trả lời

9

Nếu bạn không biết arity lên phía trước và muốn làm một khủng khiếp Hack khủng khiếp, bạn có thể làm điều này:

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

1 Rất hữu ích để dọn dẹp chuyển nhượng từ chuỗi. Nó không hoạt động với var (a, b, c) = toTuple (myIter.toList) - Bất kỳ ý tưởng nào? –

+1

Rubistro: vì mục đích đó bạn có thể sử dụng 'var List (a, b, c) = myIter.toList' –

14

Bạn thực sự không muốn phương thức trả lại Product vì điều này vô nghĩa mơ hồ. Nếu bạn muốn có thể sử dụng đối tượng được trả về như một tuple, thì bạn sẽ phải biết tính chất của nó. Vì vậy, những gì bạn có thể làm là có một loạt các phương pháp toTupleN cho các vị thần khác nhau. Để thuận tiện, bạn có thể thêm chúng dưới dạng phương thức ngầm trên Seq.

Làm thế nào về điều này:

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) 

và sử dụng nó như:

scala> List(1,2,3).toTuple3 
res0: (Int, Int, Int) = (1,2,3) 
14

Nếu, như @dhg quan sát, bạn biết arity dự kiến ​​lên phía trước bạn có thể làm điều gì đó hữu ích ở đây. Sử dụng shapeless bạn có thể viết,

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

Nhưng 'Shapeless' không cung cấp cách để tạo ra một' Tuple' từ 'List' nếu chiều dài của danh sách là undefined trong thời gian biên dịch, nó có thể dẫn đến sai sót runtime –

3

Bạn có muốn một Tuple hoặc chỉ là Product. Vì lý do thứ hai:

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

SeqProduct(List(1, 2, 3): _*) 
+0

Cũng mong muốn' Tuple', vì vậy tôi có thể sử dụng 'FunctionX.tupled', thực sự yêu cầu' TupleX', không phải 'Sản phẩm' (lưu ý ngay cả' ProductX'). – metasim

1

Dựa trên ý tưởng của @Kim Stebel, tôi đã viết một tiện ích đơn giản tạo tuple từ 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: _*) 
    } 

} 
+0

Cảm ơn bạn, @xKommando. Đây chỉ là những gì tôi cần. Bởi vì tôi muốn _toTuple_ áp dụng cho một chuỗi _Any_ thay vì _AnyRef_, tôi đã thay thế kiểu elems bằng _Seq [Any] _ và thay thế biểu thức cho trường hợp 'size <= 22' bằng' val refs = for (e <- elems) yield e.asInstanceOf [AnyRef] ctors (size - 1) .newInstance (refs: _ *). asInstanceOf [Product] '(dựa trên câu trả lời ở đây: [link] (http://stackoverflow.com/questions/16751484/scala-how-to-force-gói-một-số nguyên-như-một-đối tượng)) – Phasmid