【问题标题】:Is it possible to have a map of {key -> function call} in Scala?是否有可能在 Scala 中有 {key -> function call} 的映射?
【发布时间】:2011-02-14 17:15:23
【问题描述】:

我正在尝试创建一个具有键映射的类 -> 函数调用,但以下代码的行为与我希望的不同。

class MyClass {
    val rnd = scala.util.Random

    def method1():Double = {
        rnd.nextDouble
    }

    def method2():Double = {
        rnd.nextDouble
    }

    def method3():Double = {
        rnd.nextDouble
    }

    def method4():Double = {
        rnd.nextDouble
    }

    def method5():Double = {
        rnd.nextDouble
    }

    var m = Map[String,Double]()    
    m += {"key1"-> method1}
    m += {"key2"-> method2}
    m += {"key3"-> method3}
    m += {"key4"-> method4}
    m += {"key5"-> method5}

    def computeValues(keyList:List[String]):Map[String,Double] = {
        var map = Map[String,Double]()
        keyList.foreach(factor => {
            val value = m(factor)
            map += {factor -> value}
        })
        map
    }

}

object Test {
    def main(args : Array[String]) {
        val b = new MyClass
        for(i<-0 until 3) {
            val computedValues = b.computeValues(List("key1","key4"))
            computedValues.foreach(element => println(element._2))
        }
    }
}

以下输出

0.022303440910331762
0.8557634244639081
0.022303440910331762
0.8557634244639081
0.022303440910331762
0.8557634244639081

表示一旦将函数放置在映射中,它就是一个冻结的实例(每个键为每次传递产生相同的值)。我希望看到的行为是键将引用函数调用,生成一个新的随机值,而不是仅仅返回映射中保存的实例。

【问题讨论】:

  • 为什么会有这么多相同的函数?
  • @Pablo。它们只是我将实际实现的功能的示例占位符。我希望有一个可以调用的潜在函数的宇宙,并且只调用它们的一个子集。

标签: scala map reference function-pointers


【解决方案1】:

问题在于您的地图签名m。您描述了您想将 functions 放在地图中;但是,您已将其声明为 Map[String, Double],这只是字符串到双精度的映射。正确的类型是Map[String, () =&gt; Double]

因为括号在无参数方法调用中是可选的,所以这里的类型差异非常重要。填充地图时,会在插入时调用方法以匹配类型签名(我相信这是通过隐式转换完成的,但我不是 100% 确定)。

只需更改地图的声明签名,即可根据需要插入函数,并且可以在 computeValues 期间评估(需要将第 35 行更改为 map += {factor -&gt; value()}),从而产生以下输出(在斯卡拉 2.8):

0.662682479130198
0.5106611727782306
0.6939805749938253
0.763581022199048
0.8785861039613938
0.9310533868752249

【讨论】:

  • 这些方法实际上是在插入时调用的,因此它们返回 Double,但由于可以在没有括号的情况下调用零方法参数,因此不需要为此目的进行隐式转换。见stackoverflow.com/q/6643030/1287856
【解决方案2】:

您需要将键映射到函数,而不是函数会给您的答案。试试这个:

var m = Map[String,() => Double]()
m += /* etc. */
m.values.foreach(println(x => x()))
m.values.foreach(println(x => x()))

【讨论】:

  • 谢谢雷克斯,非常感谢。
【解决方案3】:

我会使用 scala 的类型推断来定义映射。 如果它们仅通过地图使用,则无需单独定义方法。 您也可以使用不可变的 val,而不是可变的 var。

val m = Map( "key1" -> {() => rnd.nextDouble},
  "key2" -> {() => rnd.nextDouble},
  "key3" -> {() => rnd.nextDouble},
  "key4" -> {() => rnd.nextDouble},
  "key5" -> {() => rnd.nextDouble})

您还需要将第 35 行更改为 value()

【讨论】:

  • 谢谢,函数确实需要单独定义。它们只是我稍后要定义的实际函数的占位符。我只是使用随机数来确认正确的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-07
  • 1970-01-01
  • 2018-12-16
  • 2021-06-18
  • 1970-01-01
相关资源
最近更新 更多