匹配值与基于类型的函数

时间:2013-02-02 01:32:19

标签: scala

假设我有一个函数列表:

val funcList = List(func1: A => T, func2: B => T, func2: C => T)

func1等人在其他地方定义)

我想编写一个方法,该方法将获取一个值并根据确切类型将其与正确的函数匹配(匹配a: Afunc1: A => T),或者如果没有匹配函数则抛出异常。

有一种简单的方法吗?

这类似于PartialFunction所做的,但我无法将funcList中的函数列表更改为PartialFunctions。我想我必须对函数进行某种隐式转换,以便知道它可以处理的类型并且能够对它进行模式匹配的特殊类(基本上将这些函数提升为专用的PartialFunction)。但是,我无法弄清楚如何识别每个功能的“域”。

谢谢。

3 个答案:

答案 0 :(得分:4)

您无法识别每个函数的域,因为它们在运行时被擦除。如果你想要更多的信息,请查找删除,但缺少的是你想要的信息不存在。

有关类型擦除的方法,你会发现很多关于Stack Overflow本身的讨论。其中一些归结为将类型信息存储在某处作为值,以便您可以匹配。

另一种可能的解决方案是简单地放弃使用参数化类型(Java术语中的泛型)用于您自己的自定义类型。就是这样做:

abstract class F1 extends (A => T)
object F1 { 
  def apply(f: A => T): F1 = new F1 { 
    def apply(n: A): T = f(n) 
  } 
}

等等。由于F1没有类型参数,因此您可以匹配它,并且可以轻松创建此类型的函数。假设AT都是Int,那么您可以这样做,例如:

F1(_ * 2)

答案 1 :(得分:2)

我认为你误解了List是如何打字的。 List采用单个类型参数,即 all 列表元素的类型。当你写

val funcList = List(func1: A => T, func2: B => T, func2: C => T)

编译器会推断类似funcList : List[A with B with C => T]的类型。

这意味着funcList中的每个函数都会获取一个参数,该参数是所有 ABC的成员。

除此之外,由于类型擦除,你不能(直接)匹配函数类型。

你可以做的是匹配a本身,并为该类型调用适当的函数:

a match {
    case x : A => func1(x)
    case x : B => func2(x)
    case x : C => func3(x)
    case _ => throw new Exception
}

(当然,ABC必须在类型删除后保持不同。)

如果你需要它是动态的,你基本上是使用反射。不幸的是,Scala的反射设备不断变化,几周前发布了2.10版本,所以目前的方法文档记录较少;见How do the new Scala TypeTags improve the (deprecated) Manifests?

答案 2 :(得分:2)

解决类型擦除的常用答案是使用清单的帮助。在您的情况下,您可以执行以下操作:

abstract class TypedFunc[-A:Manifest,+R:Manifest] extends (A => R) {
  val retType: Manifest[_] = manifest[R]
  val argType: Manifest[_] = manifest[A]
}
object TypedFunc {
  implicit def apply[A:Manifest, R:Manifest]( f: A => R ): TypedFunc[A, R] = {
    f match {
      case tf: TypedFunc[A, R]  => tf
      case _ => new TypedFunc[A, R] { final def apply( arg: A ): R = f( arg ) }
    }
  }
}

def applyFunc[A, R, T >: A : Manifest]( funcs: Traversable[TypedFunc[A,R]] )( arg: T ): R = {
  funcs.find{ f => f.argType <:< manifest[T] } match {
    case Some( f ) => f( arg.asInstanceOf[A] )
    case _ => sys.error("Could not find function with argument matching type " + manifest[T])
  }
}

val func1 = { s: String => s.length }
val func2 = { l: Long => l.toInt }
val func3 = { s: Symbol => s.name.length }
val funcList = List(func1: TypedFunc[String,Int], func2: TypedFunc[Long, Int], func3: TypedFunc[Symbol, Int])

在REPL中进行测试:

scala> applyFunc( funcList )( 'hello )
res22: Int = 5
scala> applyFunc( funcList )( "azerty" )
res23: Int = 6
scala> applyFunc( funcList )( 123L )
res24: Int = 123
scala> applyFunc( funcList )( 123 )
java.lang.RuntimeException: Could not find function with argument matching type Int
        at scala.sys.package$.error(package.scala:27)
        at .applyFunc(<console>:27)
        at .<init>(<console>:14)
        ...