2014-11-09 5 views
0

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

Простой пример:

val bob = "Bob" 
val alice = "Alice" 
val message = "${b} < ${a} and ${a} > ${b}" 
message.format(a = alice, b = bob) 

Определение сообщения как отдельное значение имеет решающее значение, так как я хочу, чтобы загрузить его из файла ресурсов и не указать в коде напрямую. Есть много похожих вопросов, на которые ответила новая функция scala, называемая String Interpolation. Но это не касается моего случая: я не мог позволить компилятору выполнять всю работу, так как файл ресурсов загружается во время выполнения.

ответ

3

Это не ясно «позиционные арги» вы имеете в виду ли на обычный смысл или смысл «индекс аргумента», как используемый Formatter.

scala> val bob = "Bob" 
bob: String = Bob 

scala> val alice = "Alice" 
alice: String = Alice 

scala> val message = "%2$s likes %1$s and %1$s likes %2$s" format (bob, alice) 
message: String = Alice likes Bob and Bob likes Alice 

Вы хотели бы:

scala> def f(a: String, b: String) = s"$b likes $a and $a likes $b" 
f: (a: String, b: String)String 

scala> f(b = bob, a = alice) 
res2: String = Bob likes Alice and Alice likes Bob 

Вы можете скомпилировать интерполяции с отражающей панели инструментов или сценариев.

scala> import scala.tools.reflect._ 
import scala.tools.reflect._ 

scala> val tb = reflect.runtime.currentMirror.mkToolBox() 
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = [email protected] 

scala> val defs = s"""val a = "$alice" ; val b = "$bob"""" 
defs: String = val a = "Alice" ; val b = "Bob" 

scala> val message = "${b} < ${a} and ${a} > ${b}" 
message: String = ${b} < ${a} and ${a} > ${b} 

scala> val msg = s"""s"$message"""" 
msg: String = s"${b} < ${a} and ${a} > ${b}" 

scala> tb eval (tb parse s"$defs ; $msg") 
res3: Any = Bob < Alice and Alice > Bob 

или

scala> def f(a: String, b: String) = tb eval (tb parse s"""val a = "$a"; val b = "$b"; s"$message"""") 
f: (a: String, b: String)Any 

scala> f(a = alice, b = bob) 
res4: Any = Bob < Alice and Alice > Bob 

Это немного слишком много.

Но учтите:

https://www.playframework.com/documentation/2.0/ScalaTemplates

0

Один из подходов может иметь список замен (без ограничений на количество переменных), а затем идти с чем-то вроде:

def substituteVar(s:String,subs:Seq[(String,String)]):String= 
    subs.foldLeft(s)((soFar,item) => soFar replaceAll("\\$\\{"+item._1+"}", item._2)) 

substituteVar(message,Seq(("a",alice),("b",bob))) 
res12: String = Bob < Alice and Alice > Bob 

Может быть расширен, а также ...

+0

Высматривайте, что это делает, когда 'Алис = "$ {Ь}"'. –

+0

Хорошая точка @ChrisMartin, чтобы сделать ее более надежной, можно было бы вставить \\ для выхода из $ и \ (... и других?) В значения переменных перед выполнением подстановки. – wwkudu

+0

Он также случайно приводит к тому, что некоторые из подстановок являются транзитивными. '$ {a}' изменяется на '$ {b}', а затем '$ {b}' изменяется на 'Bob'. –

1

Я не Не знаю, можно ли использовать тот же синтаксический анализатор, который использует компилятор.

Но Regex может обрабатывать пример, который вы указали.

val values = Map("a" -> "Alice", "b" -> "Bob") 
val message = "${b} < ${a} and ${a} > ${b}" 

def escapeReplacement(s: String): String = 
    s.replace("\\", "\\\\").replace("$", "\\$") 

"\\$\\{([^\\}]*)\\}".r.replaceAllIn(message, 
    m => escapeReplacement(values(m.group(1)))) 
+0

Вы должны любить регулярное выражение. с утиной лентой, WD40 и регулярным выражением, вы можете покорить мир. О, и кофе. ну, а любовь и кабельные связи тоже я думаю ... – wwkudu