scala - strings - string format interpolation java
Interpolación de cadenas en Scala 2.10-¿Cómo interpolar una variable String? (4)
La interpolación de cadenas está disponible en Scala a partir de Scala 2.10
Este es el ejemplo básico
val name = "World" //> name : String = World
val message = s"Hello $name" //> message : String = Hello World
Me preguntaba si hay una forma de hacer la interpolación dinámica, por ejemplo, la siguiente (no compila, solo para fines ilustrativos)
val name = "World" //> name : String = World
val template = "Hello $name" //> template : String = Hello $name
//just for illustration:
val message = s(template) //> doesn''t compile (not found: value s)
¿Hay alguna forma de evaluar "dinámicamente" una cadena así? (o es inherentemente incorrecto / no es posible)
Y que es exactamente?
no es un método def( aparentemente es un método enStringContext
), y no un objeto (si lo fuera, habría arrojado un error de compilación diferente al no encontrado , creo)
- La interpolación de cadenas ocurre en tiempo de compilación, por lo que el compilador generalmente no tiene suficiente información para interpolar
s(str)
. Espera una cadena literal, de acuerdo con el SIP . - En Uso avanzado en la documentación que vinculó, se explica que una expresión del
id"Hello $name ."
formularioid"Hello $name ."
se traduce en tiempo de compilación alnew StringContext("Hello", "."). id(name)
new StringContext("Hello", "."). id(name)
.
Tenga en cuenta que id
puede ser un interpolador definido por el usuario introducido a través de una clase implícita. La documentación proporciona un ejemplo para un interpolador json
,
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
def json(args: Any*): JSONObject = {
...
}
}
Aquí hay una posible solución al # 1 en el contexto de la pregunta original basada en la excelente respuesta de Rex
val name = "World" //> name: String = World
val template = name=>s"Hello $name" //> template: Seq[Any]=>String = <function1>
val message = template(name) //> message: String = Hello World
Esto es intrínsecamente imposible en la implementación actual: los nombres de las variables locales no están disponibles en el momento de la ejecución; pueden mantenerse como símbolos de depuración, pero también pueden haberse eliminado. (Los nombres de las variables de los miembros son, pero eso no es lo que está describiendo aquí).
s
es en realidad un método en StringContext
(o algo que se puede convertir implícitamente de StringContext
). Cuando escribes
whatever"Here is text $identifier and more text"
el compilador lo desengrasa en
StringContext("Here is text ", " and more text").whatever(identifier)
De forma predeterminada, StringContext
le brinda los StringContext
s
, f
y raw
*.
Como puede ver, el compilador elige el nombre y se lo da al método. Dado que esto sucede en tiempo de compilación, no puede hacerlo de forma sensata de forma dinámica: el compilador no tiene información sobre nombres de variables en tiempo de ejecución.
Sin embargo, puede usar vars para intercambiar los valores que desee. Y el método predeterminado s
simplemente llama a toString
(como era de esperar) para que pueda jugar juegos como
class PrintCounter {
var i = 0
override def toString = { val ans = i.toString; i += 1; ans }
}
val pc = new PrintCounter
def pr[A](a: A) { println(s"$pc: $a") }
scala> List("salmon","herring").foreach(pr)
1: salmon
2: herring
(0 ya fue llamado por el REPL en este ejemplo).
Eso es lo mejor que puedes hacer.
* raw
está roto y no está programado para ser reparado hasta 2.10.1; solo el texto antes de que una variable esté realmente en bruto (sin proceso de escape). Así que espere usando eso hasta que 2.10.1 esté fuera, o mire el código fuente y defina el suyo. Por defecto, no hay un proceso de escape, por lo que definir el tuyo es bastante fácil.