Is it possible to have a map of {key -> function call} in Scala?
I'm trying to create a class that has a map of keys -> function calls, and the following code is not behaving as I would like it to.
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,D开发者_C百科ouble] = {
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))
}
}
}
The following output
0.022303440910331762
0.8557634244639081
0.022303440910331762
0.8557634244639081
0.022303440910331762
0.8557634244639081
indicates that once the function is placed in the map, it's a frozen instance (each key producing the same value for each pass). The behavior I would like to see is that the key would refer to a function call, generating a new random value rather than just returning the instance held in the map.
The problem is with the signature of your map m
. You described that you wanted to put functions in the map; however you've declared it as Map[String, Double]
which is just a map of strings to doubles. The correct type would be Map[String, () => Double]
.
Because brackets are optional on no-arg method invocations, the difference in types here is very important. When the map is being populated, the methods are invoked at insertion time in order to match the type signature (I believe this is done by an implicit conversion but I'm not 100% sure).
By simply changing the declared signature of your map, the functions are inserted as you desire, and can be evaluated during computeValues
(requires a change on line 35 to map += {factor -> value()}
), resulting in the following output (tested on Scala 2.8):
0.662682479130198
0.5106611727782306
0.6939805749938253
0.763581022199048
0.8785861039613938
0.9310533868752249
You need to map the keys to functions, not to the answer that the function would give you. Try this:
var m = Map[String,() => Double]()
m += /* etc. */
m.values.foreach(println(x => x()))
m.values.foreach(println(x => x()))
I would use scala's type inference to define the map. There's no need to define the methods separately, if they're only used via the map. Also you can use an immutable val, instead of a mutable var.
val m = Map( "key1" -> {() => rnd.nextDouble},
"key2" -> {() => rnd.nextDouble},
"key3" -> {() => rnd.nextDouble},
"key4" -> {() => rnd.nextDouble},
"key5" -> {() => rnd.nextDouble})
You also need to change line 35 to value()
精彩评论