Подтвердить что ты не робот

Строковая интерполяция в Scala 2.10 - Как интерполировать переменную String?

Интерполяция строк доступна в Scala начиная Scala 2.10

Это основной пример

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

Мне было интересно, есть ли способ сделать динамическую интерполяцию, например. следующее (не компилируется, просто для иллюстрации)

 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)
  • Есть ли способ "динамически" оценивать такую ​​String? (или это изначально неправильно/невозможно)

  • А что такое s точно? это не метод def (по-видимому, это метод на StringContext), а не объект (if это было бы, он бы выбросил другую ошибку компиляции, чем не нашел, я думаю)


4b9b3361

Ответ 1

s - фактически метод на StringContext (или что-то, что может быть неявно преобразовано из StringContext). Когда вы пишете

whatever"Here is text $identifier and more text"

компилятор помещает его в

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

По умолчанию StringContext предоставляет методы s, f и raw *.

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

Однако вы можете использовать vars, чтобы вы могли поменять значения, которые вы хотите. И метод s по умолчанию просто вызывает toString (как и следовало ожидать), чтобы вы могли играть в такие игры, как

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 уже был вызван REPL в этом примере).

Что самое лучшее, что вы можете сделать.

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

Ответ 2

Вот возможное решение # 1 в контексте исходного вопроса, основанного на отличном ответе 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

Ответ 3

  • Интерполяция строк происходит во время компиляции, поэтому компилятор обычно не имеет достаточной информации для интерполяции s(str). Он ожидает строковый литерал, в соответствии с SIP.
  • В разделе "Расширенное использование" в связанной вами документации объясняется, что выражение формы id"Hello $name ." преобразуется во время компиляции в new StringContext("Hello", "."). id(name).

Обратите внимание, что id может быть определяемым пользователем интерполятором, введенным через неявный класс. В документации приведен пример интерполятора json,

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

Ответ 4

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