Как я могу параметризовать строки формата, используемые StringContext? - PullRequest
3 голосов
/ 21 марта 2019

Допустим, у меня есть такая строка кода:

log.info(f"$name%-20s $amt%7.2f")

Я хочу сделать 20 (ширину name) тем параметром, который сам по себе может быть параметризован. Как я могу это сделать?

Что если бы я также хотел сделать ширину 7.2 для amt также параметризованной. Это возможно?

[Редактировать: Я надеюсь поместить такие вещи, как 20 и 7.2 в переменные, чтобы они не были жестко запрограммированы в строке формата.]

Я пытался использовать StringContext напрямую, но, похоже, я не понимаю, в каком макросе происходит расширение:

StringContext(...).f(...)

Ответы [ 3 ]

1 голос
/ 10 апреля 2019

вы можете реализовать свой StringContext
примечание: s"a=${a},b=${b}" это как StringContext("a=",",b=").s(a,b)

case class Format(t: String)

// this implement is not type safe in Compile time,
implicit class MyFormat(val sc: StringContext) {

  def myFormat(args: Any*) = {
    val partIter = sc.parts.iterator
    var stringFormatArgs = List.empty[Object]
    val sb = new StringBuilder(partIter.next())

    def impl(list: List[Any]): Unit = {
      list match {
        case (_: Format) :: tail                  =>
          throw new Exception("")
        case (any: Object) :: (format: Format) :: tail =>
          sb.append(partIter.next()).append(format.t).append(partIter.next())
          stringFormatArgs = any :: stringFormatArgs
          impl(tail)
        case (any: Any) :: tail                        =>
          sb.append(any.toString).append(partIter.next())
          impl(tail)
        case Nil                                       =>
      }
    }

    impl(args.toList)
    String.format(sb.toString(), stringFormatArgs.reverse: _*)
  }
}

// тест

val amt = 1234.1234d
val name = "James"
val len = Format("20s")
val prec = Format("7.2f")

myFormat"$name%$len,$amt%$prec," //"               James,1234.12,"
1 голос
/ 10 апреля 2019

Вы можете сделать это в двух частях.

val name = "Jan"
val nLen = -10  //name length

val amt = 1.1
val ff = 5.3  //float format

s"%${nLen}s".format(name) + s"%${ff}f".format(amt)
//res0: String = Jan       1.100

Или за один проход.

s"%${nLen}s%${ff}f".format(name, amt)
//res1: String = Jan       1.100
1 голос
/ 21 марта 2019

Вы были недалеко от правды:

val amt= 1234.1234d
val name = "James"
println(f"|$name%20s $amt%7.2f|")

Выходы:

| James 1234,12|

Относительно передачи 20 и 7.2 в качестве параметра: шаблон может быть сгенерирован как:

val len = 20
val prec = 7.2
val template = s"$$name%${len}s $$amt%${prec}f"

, что дает: $name%20 $amt%7.2f

Но интерполяция строк, похоже, не является решением, как даже если бы она была разбита на StringContext, она выполняется во время компиляции, затем s"$template урожайность $name%20s $amt%7.2f.

Я думаю, у меня была эта работа с другим языком

...