Возможно ли иметь карту {key -> вызов функции} в Scala? - PullRequest
6 голосов
/ 14 февраля 2011

Я пытаюсь создать класс, у которого есть карта ключей -> вызовов функций, и следующий код не ведет себя так, как мне бы хотелось.

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

указывает, что после размещения функции на карте это замороженный экземпляр (каждый ключ выдает одно и то же значение для каждого прохода). Поведение, которое я хотел бы видеть, заключается в том, что ключ будет ссылаться на вызов функции, генерируя новое случайное значение, а не просто возвращая экземпляр, хранящийся на карте.

Ответы [ 3 ]

17 голосов
/ 14 февраля 2011

Проблема с подписью вашей карты m.Вы описали, что хотите поместить функции в карту;однако вы объявили его как Map[String, Double], который является просто картой строк в двойных разрядах.Правильный тип будет Map[String, () => Double].

. Поскольку скобки являются необязательными при вызове метода без аргументов, разница в типах здесь очень важна.Когда карта заполняется, методы вызываются во время вставки, чтобы соответствовать сигнатуре типа (я считаю, что это делается неявным преобразованием, но я не уверен на 100%).

Простым изменениемобъявленная подпись вашей карты, функции вставляются по вашему желанию и могут быть оценены в течение computeValues (требуется изменение в строке 35 на map += {factor -> value()}), что приводит к следующему выводу (проверено в Scala 2.8):

0.662682479130198
0.5106611727782306
0.6939805749938253
0.763581022199048
0.8785861039613938
0.9310533868752249
5 голосов
/ 14 февраля 2011

Вам необходимо сопоставить ключи с функциями, а не с ответом, который даст вам функция.Попробуйте это:

var m = Map[String,() => Double]()
m += /* etc. */
m.values.foreach(println(x => x()))
m.values.foreach(println(x => x()))
0 голосов
/ 15 февраля 2011

Я бы использовал вывод типа scala для определения карты. Нет необходимости определять методы отдельно, если они используются только через карту. Также вы можете использовать неизменяемый val вместо изменяемого var.

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

Вам также нужно изменить строку 35 на значение ()

...