3

假设我有一个函数列表,如下所示:

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

(其中func1,等人在别处定义)

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

有没有一种简单的方法可以做到这一点?

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

谢谢你。

4

3 回答 3

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没有类型参数,你可以对其进行匹配,并且可以轻松创建该类型的函数。说 bothATare Int,那么你可以这样做,例如:

F1(_ * 2)
于 2013-02-02T02:27:31.827 回答
2

我认为您误解了 aList的输入方式。List采用单个类型参数,它是列表中所有元素的类型。当你写

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

编译器将推断出类似funcList : List[A with B with C => T].

这意味着 in 中的每个函数都funcList接受一个参数,该参数是所有 AB和的成员C

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

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

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

(当然,A,BC必须在类型擦除后保持不同。)

如果你需要它是动态的,你基本上是在使用反射。不幸的是,Scala 的反射功能在不断变化,几周前发布了 2.10 版本,因此目前的实现方式的文档较少;请参阅新的 Scala TypeTags 如何改进(已弃用的)清单?.

于 2013-02-02T01:51:25.203 回答
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)
        ...
于 2013-02-02T13:54:53.267 回答