6

我希望编写一个 Scala 方法,该方法接收任意大小和类型的元组以及索引,并返回该索引处的元组中的元素。我知道如何做所有事情,但保留类型。我还没有找到使返回值成为元组项的动态类型的方法。

这是我到目前为止的功能:

def subscript_get(tup: Product, index:Int): Any={
    return tup.productElement(index)    
}

例如,用法是:

subscript_get((0,1,2,3),0) --> Int = 0

subscript_get((0,1,"asdf",3),2) --> java.lang.String = asdf

我知道我可以在之后将结果转换回我正在寻找的内容,但这对我不起作用,因为我并不总是知道我应该转换为什么类型。

这样的事情甚至可能吗?谢谢!

4

2 回答 2

11

我不确定你是否想要一个使用宏的解决方案,但为了记录(因为我之前已经准确地编写过这个方法),这里是你如何在 2.10 中使用宏系统来实现它。

正如我在上面的评论中指出的那样,这种方法需要index是一个整数文字,并且依赖于 2.10 中的“未指定但预期的”行为。它还提出了一些关于文档的棘手问题。

import scala.language.experimental.macros
import scala.reflect.macros.Context

object ProductIndexer {
  def at[T <: Product](t: T)(index: Int) = macro at_impl[T]
  def at_impl[T <: Product: c.WeakTypeTag](c: Context)
    (t: c.Expr[T])(index: c.Expr[Int]) = {
    import c.universe._

    index.tree match {
      case Literal(Constant(n: Int)) if
        n >= 0 && 
        weakTypeOf[T].members.exists {
          case m: MethodSymbol => m.name.decoded == "_" + (n + 1).toString
          case _ => false
        } => c.Expr[Any](Select(t.tree, newTermName("_" + (n + 1).toString)))
      case Literal(Constant(_: Int)) => c.abort(
        c.enclosingPosition,
        "There is no element at the specified index!"
      )
      case _ => c.abort(
        c.enclosingPosition,
        "You must provide an integer literal!"
      )
    }
  }
}

接着:

scala> import ProductIndexer._
import ProductIndexer._

scala> val triple = (1, 'a, "a")
triple: (Int, Symbol, String) = (1,'a,a)

scala> at(triple)(0)
res0: Int = 1

scala> at(triple)(1)
res1: Symbol = 'a

scala> at(triple)(2)
res2: String = a

所有都按预期静态键入,如果你给它一个超出范围的索引(或不是文字),你会得到一个很好的编译时错误。

于 2013-04-30T23:55:01.613 回答
0

你不能这样做。如果使用Product,元组中值的(编译时)类型将丢失。此外,方法无法根据您传入的值调整其返回类型(不完全正确,请参见依赖方法类型,但对于 an 则为正确Int)。

如果您不知道要转换为哪种类型,则可以使用模式匹配:

subscript_get(..., 1) match {
  case v: Int    => // do something with Int
  case v: String => // do something with String
  // snip
  case _ => sys.error("don't know how to handle this")
}
于 2013-04-30T23:00:58.600 回答