2012-11-06 6 views
39

String interpolazione viene available in Scala iniziando Scala 2.10Interpolazione stringa in Scala 2.10 - Come interpolare una variabile String?

Questo è l'esempio di base

val name = "World"   //> name : String = World 
val message = s"Hello $name" //> message : String = Hello World 

mi chiedevo se esiste un modo per fare interpolazione dinamico, ad esempio il seguente (non viene compilato, solo a scopo illustrativo)

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) 
  1. C'è un modo per "dinamicamente" valutare una stringa del genere? (o è intrinsecamente sbagliato/non possibile)

  2. E che cos'è esattamente s? non è un metodo def (apparently it is a method on StringContext), e non un oggetto (se fosse, avrebbe gettato un errore di compilazione diverso rispetto non trovato credo)


risposta

29

s è in realtà un metodo su StringContext (o qualcosa che può essere convertito in modo implicito da StringContext). Quando si scrive

whatever"Here is text $identifier and more text" 

il compilatore desugars in

StringContext("Here is text ", " and more text").whatever(identifier) 

Per impostazione predefinita, StringContext ti dà s, f, e raw * metodi.

Come si può vedere, il compilatore stesso preleva il nome e lo dà al metodo. Dato che questo accade al momento della compilazione, non è possibile farlo in modo dinamico in modo dinamico - il compilatore non ha informazioni sui nomi delle variabili in fase di runtime.

È possibile utilizzare vars, tuttavia, in modo da poter scambiare i valori desiderati. E il metodo di default s chiama semplicemente toString (come ci si aspetterebbe) in modo da poter giocare a giochi come

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 è già stata chiesta dal REPL in questo esempio).

Questo è il meglio che puoi fare.

* raw è rotto e non è programmato per essere risolto fino a 2.10.1; solo testo prima che una variabile sia effettivamente grezza (nessuna elaborazione di escape). Quindi, tieni duro a usare quello fino a quando 2.10.1 non è disponibile, o guarda il codice sorgente e definisci il tuo. Per impostazione predefinita, non vi è alcuna elaborazione di escape, quindi definire la tua è abbastanza semplice.

+1

Una piccola aggiunta. Non si può usare solo l'identificatore. Qualsiasi espressione di scala valida può essere inserita tra $ {}. – pedrofurla

7
  1. String interpolazione avviene in fase di compilazione, quindi il compilatore in genere non dispone di informazioni sufficienti per interpolare s(str). Si aspetta una stringa letterale, according to the SIP.
  2. Sotto Uso avanzato nella documentazione collegata, viene spiegato che un'espressione del modulo id"Hello $name ." viene convertita in fase di compilazione a new StringContext("Hello", "."). id(name).

noti che id può essere un interpolatore definito dall'utente introdotto attraverso una classe implicita. La documentazione fornisce un esempio per un json interpolatore,

implicit class JsonHelper(val sc: StringContext) extends AnyVal { 
    def json(args: Any*): JSONObject = { 
    ... 
    } 
} 
1

Questo è intrinsecamente impossibile nell'implementazione corrente: i nomi delle variabili locali non sono disponibili al momento dell'esecuzione - possono essere tenuti in giro come simboli di debug, ma possono anche essere stati rimossi. (I nomi delle variabili dei membri sono, ma non è quello che stai descrivendo qui).

9

Ecco una possibile soluzione a # 1 nel contesto della domanda iniziale sulla base di eccellente risposta di 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 
+2

Con Scala 2.11.5 la seconda riga restituisce l'errore "nome del tipo di parametro mancante". Penso che debba essere scritto come 'val template = (name: Any) => s" Hello $ name "' o 'def template (name: Any) = s" Hello $ name "' – Suma

+0

hai aggiunto il 'val nome = "Mondo" 'prima? Penso che aiuti l'inferenza del tipo, e senza di essa dovrai effettivamente includere il tipo di nome ... in altre parole, non so se $ name stia ombreggiato o meno il nome del parametro ... –

+0

Ho incollato tutte e tre le linee in una sorgente Scala. Non riesco a compilarlo a meno che non aggiungo l'annotazione del tipo al parametro name. – Suma