具有可变长度的scala字符串插值

时间:2016-12-07 08:48:34

标签: scala

我使用scala f string interpolator如下:

def format(id: Int) = f"A$id%04d"
format(21)  // A0021

但是,我希望能够一劳永逸地定义一个长度(在固定为4之前),并获得一个函数,它将使用该长度格式化字符串。 所以,而不是

def format(length: Int, id: Int) = ???
f(5, 21)       // A00021

我想有这个:

def format(length: Int)(id: Int) = ???
val f = format(5)
f(21)       // A00021

如何使用scala f插值器或其他实现这个?

更新

我不是在运行时寻找这样一个涉及编译器的解决方案,但我很欣赏som-snytt的回答。这里有一个基于他的答案的工作解决方案:

import scala.tools.reflect._,scala.reflect.runtime._,universe._

def defFormat(length: Int): Int => String = {
  val code = raw"""(i: Int) => f"A$$i%0${length}d""""
  tb.eval(tb.parse(code)).asInstanceOf[Int => String]
}

val format = defFormat(length = 5)

format(21)

2 个答案:

答案 0 :(得分:6)

scala> def format(n: Int)(i: Int) =
     | f"A%%0${n}d" format i
format: (n: Int)(i: Int)String

scala> format(5) _
res0: Int => String = <function1>

scala> .apply(21)
res1: String = A00021

编辑:

scala> import scala.tools.reflect._,scala.reflect.runtime._,universe._
import scala.tools.reflect._
import scala.reflect.runtime._
import universe._

scala> val tb = currentMirror.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@2d10e0b1

scala> def f(n: Int)(i: Int): String = {
     |   val code = raw"""f"A$${$i}%0${n}d""""
     |   tb.eval(tb.parse(code)).asInstanceOf[String]
     | }
f: (n: Int)(i: Int)String

scala> val g = f(5) _
g: Int => String = <function1>

scala> g(21)
res9: String = A00021

这实际上没有多大帮助。你真的想要

scala> tb.typecheck(tb.parse(code))
scala.tools.reflect.ToolBoxError: reflective typecheck has failed: illegal conversion character 'k'
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:178)
如果格式不好,则抛出

scala>   val code = raw"""(i: Int) => f"A$${i}%k0${10}d""""
code: String = (i: Int) => f"A${i}%k010d"

scala> tb.typecheck(tb.parse(code))
scala.tools.reflect.ToolBoxError: reflective typecheck has failed: illegal conversion character 'k'
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:178)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$typecheck$1.apply(ToolBoxFactory.scala:170)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$11.apply(ToolBoxFactory.scala:148)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$11.apply(ToolBoxFactory.scala:148)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$9.apply(ToolBoxFactory.scala:138)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$9.apply(ToolBoxFactory.scala:138)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$withContext$1$1.apply(ToolBoxFactory.scala:139)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$withContext$1$1.apply(ToolBoxFactory.scala:139)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$7.apply(ToolBoxFactory.scala:137)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1$$anonfun$7.apply(ToolBoxFactory.scala:137)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1.apply(ToolBoxFactory.scala:148)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$transformDuringTyper$1.apply(ToolBoxFactory.scala:121)
  at scala.reflect.internal.Trees$class.wrappingIntoTerm(Trees.scala:1716)
  at scala.reflect.internal.SymbolTable.wrappingIntoTerm(SymbolTable.scala:16)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.withWrapping$1(ToolBoxFactory.scala:120)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.transformDuringTyper(ToolBoxFactory.scala:121)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.typecheck(ToolBoxFactory.scala:169)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$$anonfun$typecheck$2.apply(ToolBoxFactory.scala:375)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$$anonfun$typecheck$2.apply(ToolBoxFactory.scala:367)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.liftedTree2$1(ToolBoxFactory.scala:355)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$withCompilerApi$.apply(ToolBoxFactory.scala:355)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.typecheck(ToolBoxFactory.scala:367)
  at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.typecheck(ToolBoxFactory.scala:27)
  ... 32 elided

scala>   val code = raw"""(i: Int) => f"A$${i}%0${10}d""""
code: String = (i: Int) => f"A${i}%010d"

scala> tb.typecheck(tb.parse(code))
res19: tb.u.Tree =
((i: Int) => ({
  val arg$macro$9: Int = i;
  new scala.collection.immutable.StringOps("A%010d").format(arg$macro$9)
}: String))

答案 1 :(得分:1)

你无法使用f来完成它,因为它的重点是确保它可以检查格式字符串中的类型错误,因此格式字符串必须是静态的。 f可以明确支持此方案,但事实并非如此。

可以使format成为一个宏,但这似乎是一种矫枉过正。更不用说它必须在一个单独的模块中定义,这对于这种情况看起来非常不方便。