2011-02-14 14 views
5

Estoy tratando de crear una clase que tenga un mapa de teclas -> llamadas de función, y el siguiente código no se comporta como me gustaría.¿Es posible tener un mapa de {tecla -> llamada de función} en Scala?

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)) 
     } 
    } 
} 

La siguiente salida

0.022303440910331762 
0.8557634244639081 
0.022303440910331762 
0.8557634244639081 
0.022303440910331762 
0.8557634244639081 

indica que una vez que la función se coloca en el mapa, es un ejemplo congelado (cada tecla producir el mismo valor para cada pasada). El comportamiento que me gustaría ver es que la clave se referiría a una llamada a función, generando un nuevo valor aleatorio en lugar de simplemente devolver la instancia contenida en el mapa.

+0

por qué tantas funciones idénticas? –

+0

@Pablo. Son solo marcadores de posición de ejemplo para las funciones que REALMENTE implementaré. Quería tener un universo de funciones potenciales para llamar y solo llamar a un subconjunto de ellas. –

Respuesta

16

El problema está en la firma de su mapa m. Usted describió que quería poner funciones en el mapa; sin embargo, lo ha declarado como Map[String, Double] que es solo un mapa de cadenas para duplicar. El tipo correcto sería Map[String,() => Double].

Dado que los corchetes son opcionales en las invocaciones de métodos no-arg, la diferencia de tipos aquí es muy importante. Cuando se llena el mapa, los métodos se invocan en el momento de la inserción para que coincida con la firma del tipo (creo que esto se hace mediante una conversión implícita, pero no estoy 100% seguro).

Simplemente cambiando la firma declarada de su mapa, las funciones se insertan como lo desee y puede evaluarse durante computeValues (requiere un cambio en la línea 35 a map += {factor -> value()}), obteniéndose el siguiente resultado (probado en Scala 2.8) :

0.662682479130198 
0.5106611727782306 
0.6939805749938253 
0.763581022199048 
0.8785861039613938 
0.9310533868752249 
+0

Gracias por la explicación muy clara. Lo aprecio. –

+0

Los métodos se invocan realmente en el momento de la inserción, por lo que devuelven el doble, pero como los argumentos de método cero se pueden invocar sin paréntesis, no hubo necesidad de conversión implícita para ese fin. Ver http://stackoverflow.com/q/6643030/1287856 –

4

Debe asignar las teclas a las funciones, no a la respuesta que la función le daría. Pruebe esto:

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

Gracias Rex, muy apreciado. –

0

Usaría la inferencia de tipo de Scala para definir el mapa. No es necesario definir los métodos por separado, si solo se usan a través del mapa. También puede usar un valor inmutable val, en lugar de una variable mutable.

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

También es necesario cambiar la línea 35 a valor()

+0

Gracias, las funciones SI deben definirse por separado. Son meramente marcadores de posición para funciones reales que definiré más adelante. Acabo de usar números aleatorios para confirmar el comportamiento correcto. –

Cuestiones relacionadas