如何参数化 StringContext 使用的格式字符串?

How do I parameterize format strings used by StringContext?

假设我有这样一行代码:

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

我想要的是使 20name 的宽度)本身可以参数化。我该怎么做?

如果我还想使 amt7.2 宽度也参数化怎么办?这可能吗?

[编辑:我希望将 207.2 之类的东西放在变量中,这样它们就不会硬编码在格式字符串中。]

我试过直接使用 StringContext,但似乎我不明白发生在什么宏扩展中:

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

你离事实不远:

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.

我想我已经用另一种语言工作了

您可以实现您的 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,"

你可以分两部分完成。

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