2012-11-06 37 views
39

字符串插值available in Scala开始的Scala 2.10斯卡拉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) 
  1. 有没有办法为“动态”评估像这样的字符串? (或者它本身是错误的/不可能的)

  2. 究竟是什么s它不是一个方法高清apparently it is a method on StringContext),而不是一个对象(如果是,它会抛出比不同的编译错误没有发现我认为)


回答

29

s实际上是对StringContext(或东西可以从StringContext隐式转换)的方法。当你写

whatever"Here is text $identifier and more text" 

编译desugars成

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

默认情况下,StringContext给你sfraw *方法。如你所见,编译器本身挑选出名称并将其提供给方法。由于这是在编译时发生的,因此不能动态地做到这一点 - 编译器在运行时没有关于变量名的信息。

但是,您可以使用变量,以便您可以交换所需的值。并且默认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结束,或者查看源代码并定义你自己的。默认情况下,不存在转义处理,因此定义自己很容易。

+1

一个小小的另外一个可能的解决方案#1在原来的问题的情况下。不仅可以使用标识符。任何有效的scala表达式都可以放在$ {}之间。 – pedrofurla

7
  1. 路线插值发生在编译时,所以编译器通常没有足够的信息来插入s(str)。它期望一个字符串文字,according to the SIP
  2. 高级用法在您链接的文档中,解释了表格id"Hello $name ."在编译时转换为new StringContext("Hello", "."). id(name)

请注意,id可以是通过隐式类引入的用户定义插补器。该文档给出了一个例子为json内插器,

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

这在当前的实现中本质上是不可能的:局部变量名在执行时不可用 - 可能作为调试符号保留,但也可能已被剥离。 (成员变量名称,但这不是你在这里描述的)。

9

这是基于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 
+2

在Scala 2.11.5中,第二行给出错误“缺少参数类型名称”。我认为它需要写成'val template =(name:Any)=> s“Hello $ name”或者'def template(name:Any)= s“Hello $ name”' – Suma

+0

您是否添加了'val name =“World”'之前?我认为它有助于类型推断,没有它,你确实需要包括名称的类型......换句话说,我不知道$ name是否影响参数名称或不... –

+0

我粘贴所有三行代码都放入Scala源代码中。除非将name注释添加到name参数,否则我无法编译它。 – Suma