假设我有一个函数列表:
val funcList = List(func1: A => T, func2: B => T, func2: C => T)
(func1
等人在其他地方定义)
我想编写一个方法,该方法将获取一个值并根据确切类型将其与正确的函数匹配(匹配a: A
与func1: A => T
),或者如果没有匹配函数则抛出异常。
有一种简单的方法吗?
这类似于PartialFunction
所做的,但我无法将funcList
中的函数列表更改为PartialFunctions。我想我必须对函数进行某种隐式转换,以便知道它可以处理的类型并且能够对它进行模式匹配的特殊类(基本上将这些函数提升为专用的PartialFunction
)。但是,我无法弄清楚如何识别每个功能的“域”。
谢谢。
答案 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
没有类型参数,因此您可以匹配它,并且可以轻松创建此类型的函数。假设A
和T
都是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
中的每个函数都会获取一个参数,该参数是所有 A
,B
和C
的成员。
除此之外,由于类型擦除,你不能(直接)匹配函数类型。
你可以做的是匹配a
本身,并为该类型调用适当的函数:
a match {
case x : A => func1(x)
case x : B => func2(x)
case x : C => func3(x)
case _ => throw new Exception
}
(当然,A
,B
和C
必须在类型删除后保持不同。)
如果你需要它是动态的,你基本上是使用反射。不幸的是,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)
...