Scala中不同函数类型的映射

时间:2012-02-20 23:47:25

标签: scala

我有两个具有不同类型签名的函数,我想将它们存储在Map中:

val add = ((a: Int, b: Int) => a + b)
val odd = ((a: Int) => a % 2 == 0)

var funcs = Map[String, Any]("add" -> add, "odd" -> odd)

然后我想在以后访问这些功能,而不必知道他们的类型。我该怎么做?我可以写:

funcs("add").asInstanceOf[(Int, Int) => Int](1, 2)

但这需要我

  1. 提前知道函数的类型是什么
  2. 进行某种模式匹配,以匹配每种可能的类型
  3. 有什么方法可以找出存储为Any的对象类型并将其转换为该类型?

3 个答案:

答案 0 :(得分:6)

虽然这当然不是一个好主意,如果你真的必须这样做,你可以使用反射来做。

使用PaulP的Invocation实用程序:

scala> val add: (Int, Int) => Int = _ + _
add: (Int, Int) => Int = <function2>

scala> val isOdd: Int => Boolean = _ % 2 != 0
isOdd: Int => Boolean = <function1>

scala> import Invocation._
import Invocation._

scala> val funcs = Map("add" -> add, "isOdd" -> isOdd)
funcs: scala.collection.immutable.Map[java.lang.String,ScalaObject] = Map(add -> <function2>, isOdd -> <function1>)

scala> funcs("add") o 'apply(3, 4)
res18: Any = 7

scala> funcs("isOdd") o 'apply(11)
res19: Any = true

答案 1 :(得分:4)

  

有什么方法可以找出存储为Any的对象类型并将其转换为该类型?

这实际上没有意义。让我们假设我能做到,它看起来像这样:

val func = funcs(func_name).toAppropriateType

现在是什么?

func现在拥有某种未知类型的值。我可以称它为...但我不知道它的签名,那么我通过什么参数呢?并且每次调用时类型/签名都会有所不同(取决于从地图中拉出的内容),因此调用它的代码对于每次调用都必须是不同的;我需要某种方法为func的每种可能性执行不同的代码...但这将是模式匹配!它必须考虑到所有可能的类型,就像在Any上匹配一样。

事实上,神奇的toAppropriateType方法,即使它可以存在,也会让我在使用Any之上 。我仍然可以使用func做的唯一事情就是你可以用所有可能的类型(即很少)的所有值来做。

一般情况下,当您从集合中取出某些内容时,您必须使用对该类型允许放入集合的任何内容有效的代码来处理它。唯一的选择是使用诸如asInstanceOf之类的功能,它会抛弃类型系统的保证,并负责确保您不使用错误的类型;这意味着你必须有其他方式来了解类型系统之外的预期类型。

如果你想拥有一组预先确定的签名函数,那么最好的方法是构造一个只允许这些签名的类型,并让你分辨出它们之间的区别(比如Rex)克尔建议使用Either)。一旦发现需要在集合中组合不同类型的东西,切换到使用Any通常会导致比解决的问题更多的问题。

答案 2 :(得分:2)

您可以将它们存储在Either

val funcs = Map[String, Either[Int=>Boolean, (Int,Int)=>Int]](
  "add" -> Right(add),
  "odd" -> Left(odd)
)

现在你可以再次将它们拆开,虽然模式匹配在这里很挑剔(Right(f)会起作用,但是不要试图在f上设置条件)。我建议使用foldrightleft(以及isRightisLeft,如果你真的必须),就像这样:

def doSomething(s: String) =
  funcs(s).fold(
    f => { f(4) },      // Left is Int=>Boolean
    g => { g(3,5)==8 }  // Right is (Int,Int)=>Int
  )

据推测,你想要一些方法来加载适当的参数,而不是我输入的虚拟值。

如果你有很多函数变体,你可以定义自己的类似层次结构,除了更多不同的情况,或者你可以嵌套Either