Estoy tratando de hacer una función que ejecute otras funciones (que devuelven Strings) dependiendo de una entrada.
Por ejemplo:

def execute(input: String) = {
    val actions = Map("foo" -> someObject.doStuff(), "bar" -> someObject.doThing())
    actions.get(input) }

¿Hay alguna manera de hacer que las funciones en Map se invoquen solo si existe la clave correspondiente?

4
Kah 17 nov. 2018 a las 21:31

3 respuestas

La mejor respuesta
object someObject {
  def doStuff() = "foo"
  def doThing() = "bar"

}

//if you need a string as a return value.
//there will be empty string for missing keys
def execute(input: String):String = {
  val actions = Map( "foo" -> someObject.doStuff _ ,
                     "bar" -> someObject.doThing _ )

  actions.getOrElse(input,()=>"")()
}


//if you need an Option[String] as a return value.
//there will be None value for missing keys
def executeOption(input: String):Option[String] = {
  val actions = Map( "foo" -> someObject.doStuff _ ,
                     "bar" -> someObject.doThing _ )

  actions.get(input).map(_())
}
4
Bogdan Vakulenko 18 nov. 2018 a las 10:39

Sí, hay una forma de ejecutar métodos de objetos dentro de un Map. Pero primero tienes que poner funciones en el mapa. Lo que hiciste fue poner resultados de métodos de objetos en tu mapa. Por lo tanto, sus métodos se ejecutaron y sus resultados se colocaron como valores en el mapa.

Supongamos que sus métodos están definidos en esta clase:

class SomeObject {
  def doStuff(): String = "I did stuff"
  def doThing(): String = "I did a thing"
}

Y tienes una instancia de la clase:

val someObject = new SomeObject

La primera forma, cómo retrasar la ejecución de estos métodos es crear una nueva función usando lambdas y ejecutar métodos dentro de estas lambdas:

val actions = Map(
  "foo" -> (() => someObject.doStuff()),
  "bar" -> (() => someObject.doThing()),
)

Puede usar una sintaxis especial para convertir métodos de objetos directamente en lambdas:

val actions = Map(
  "foo" -> someObject.doStuff _,
  "bar" -> someObject.doThing _
)

Si especifica el tipo de mapa explícitamente, también puede eliminar el guión bajo:

val actions: Map[String, () => String] = Map(
  "foo" -> someObject.doStuff,
  "bar" -> someObject.doThing
)

A continuación, debe obtener la función del mapa. Si no hay una función para la tecla, utilice una función predeterminada, una, que siempre devuelva None

val action = actions.getOrElse(input, () => None)

Y finalmente, invocas la acción:

action()
1
ygor 17 nov. 2018 a las 20:37

Por lo que puedo decir, realmente no estás buscando lazy map. Estás tratando de construir algún tipo de motor de ejecución, que hace algo basado en la entrada de cadena.

Ahora, el truco simple para lograr esto es tener un functions real en su Map y ejecutar la función correspondiente para la cadena de entrada.

Entonces, digamos que tienes un objeto A,

object A {
  var i = 1

  def doFirstThing(): Unit = {
    i = i + 1
    println(s"first thing :: $i")
  }

  def doSecondThing(): Unit = {
    i = i - 1
    println(s"second thing :: $i")
  }
}

Y desea crear un ejecutor para ejecutar doFirstThing para la entrada "first" y doSecondThing para la entrada "second".

Entonces, ahora necesita crear functions f1 y f2 que ejecutará A.doFirstThing() y A.doSecondThing().

Es bastante simple en realidad,

scala> val f1 = () => A.doFirstThing()
// f1: () => Unit = $$Lambda$1235/1187572055@7e192338

scala> val f2 = () => A.doSecondThing()
// f2: () => Unit = $$Lambda$1236/1696761182@47df3efe

O bien, puede usar _ para convertir def (methods) a funciones,

scala> val f1 = A.doFirstThing _
// f1: () => Unit = $$Lambda$1317/1501701470@7306836f

scala> val f2 = A.doSecondThing _
// f2: () => Unit = $$Lambda$1318/545144829@2a3251ad

Ahora, puedes definir tu mapa usando estos,

scala> def execute(input: String): Unit = {
     |   val map = Map(
     |     "first" -> f1,
     |     "second" -> f2
     |   )
     | 
     |   map.get(input).foreach(f => f())
     | }
// execute: (input: String)Unit

scala> execute("first")
// first thing :: 2

scala> execute("second")
// second thing :: 1

scala> execute("second")
// second thing :: 0

scala> execute("first")
// first thing :: 1

scala> execute("first")
// first thing :: 2

Y simplemente ignorará todas las demás entradas,

scala> execute("abc")
1
Sarvesh Kumar Singh 17 nov. 2018 a las 20:19