首页 文章

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

提问于
浏览
2

我使用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插值器或其他实现此功能?

Update

我不是在运行时寻找涉及编译器的这种解决方案,但我很欣赏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 回答

  • 5
    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

    你不能使用 f 这样做,因为它的重点是确保它可以检查格式字符串的类型错误,因此格式字符串必须是静态的 . f 可以明确支持这种情况,但事实并非如此 .

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

相关问题