Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
scala中保留缩进的字符串插值_Scala - Fatal编程技术网

scala中保留缩进的字符串插值

scala中保留缩进的字符串插值,scala,Scala,我想知道在scala中进行字符串插值时是否有保留缩进的方法。本质上,我想知道是否可以插入自己的上下文。宏可以解决这个问题,但我想等到它们正式发布 这就是我想要的: val x = "line1 \nline2" val str = s"> ${x}" str应评估为 > line1 line2 您可以编写自己的插值器,也可以使用自己的插值器对标准插值器进行阴影处理。现在,我不知道你的例子背后的语义是什么,所以我甚至不打算尝试 请查看我在Sc

我想知道在scala中进行字符串插值时是否有保留缩进的方法。本质上,我想知道是否可以插入自己的上下文。宏可以解决这个问题,但我想等到它们正式发布

这就是我想要的:

val x = "line1 \nline2"
val str = s">       ${x}"
str应评估为

>       line1
        line2

您可以编写自己的插值器,也可以使用自己的插值器对标准插值器进行阴影处理。现在,我不知道你的例子背后的语义是什么,所以我甚至不打算尝试


请查看我在Scala 2.10中关于或的演示,因为它们包含了有关编写/重写插值器的所有方式的示例。从第40张幻灯片开始(目前,演示文稿可能会一直更新到2.10最终发布)。

回答我的问题,并将Daniel Sobral非常有用的答案转换为代码。希望它对其他有同样问题的人有用。我还没有使用隐式类,因为我还在2.10之前

用法:
导入压头。
并使用字符串插值,如
e“$foo”

例子 应该打印

class Foo
   x: Int
   y: String
   z: Double
这是自定义缩进上下文。
类IndentStringContext(sc:StringContext){
def e(args:Any*):字符串={
val sb=新的StringBuilder()
对于((s,a)0){
sb追加a.toString().replaceAll(“\n”,“\n”+ind)
}否则{
sb附加a.toString()
}
}
如果(sc.parts.size>参数尺寸)
某人最后加上一部分
(某人)
}
//在最后一个新行(如果有)后获得白色缩进
def getindent(str:String):String={
val lastnl=str.lastIndexOf(“\n”)
如果(lastnl==-1)”
否则{
val ind=str.substring(lastnl+1)
如果(ind.trim.isEmpty)ind//ind都是空白。请使用
否则“
}
}
}
物体压头{
//仅在2.10及以上版本中允许顶级隐式定义
隐式定义toISC(sc:StringContext)=新缩进StringContext(sc)
}

对于任何寻求post 2.10答案的人:

object Interpolators {
  implicit class Regex(sc: StringContext) {
    def r = new util.matching.Regex(sc.parts.mkString, sc.parts.tail.map(_ => "x"): _*)
  }

  implicit class IndentHelper(val sc: StringContext) extends AnyVal {
    import sc._

    def process = StringContext.treatEscapes _

    def ind(args: Any*): String = {
      checkLengths(args)
      parts.zipAll(args, "", "").foldLeft("") {
        case (a, (part, arg)) =>
          val processed = process(part)

          val prefix = processed.split("\n").last match {
            case r"""([\s|]+)$d.*""" => d
            case _                   => ""
          }

          val argLn = arg.toString
            .split("\n")

          val len = argLn.length

          // Todo: Fix newline bugs
          val indented = argLn.zipWithIndex.map {
            case (s, i) =>
              val res = if (i < 1) { s } else { prefix + s }
              if (i == len - 1) { res } else { res + "\n" }
          }.mkString

          a + processed + indented
      }
    }
  }
}
对象插值器{
隐式类正则表达式(sc:StringContext){
def r=new util.matching.Regex(sc.parts.mkString,sc.parts.tail.map(=>“x”):*)
}
隐式类IndentHelper(val sc:StringContext)扩展了AnyVal{
进口sc_
def process=StringContext.treatEscapes_
def ind(参数:任意*):字符串={
校验长度(args)
parts.zipAll(args,“,”).foldLeft(“”){
案例(a,(部分,参数))=>
val processed=过程(部分)
val prefix=processed.split(“\n”)。上次匹配{
案例r“”([\s |]+)$d.*”=>d
大小写=>“”
}
val argLn=arg.toString
.split(“\n”)
val len=argLn.length
//Todo:修复换行错误
val缩进=argLn.zipWithIndex.map{
案例(s,i)=>
val res=if(i<1){s}else{prefix+s}
如果(i==len-1){res}else{res+“\n”}
}.mkString
a+加工+缩进
}
}
}
}

这里有一个简短的解决方案。完整的代码和测试。这里有两种版本,一种是普通的
缩进的
插值器,另一种是稍微复杂一点的
缩进的TripMargin
插值器,使其更具可读性:

assert(indentedWithStripMargin"""abc               
                                |123456${"foo\nbar"}-${"Line1\nLine2"}""" == s"""|abc
                                                                                 |123456foo
                                                                                 |      bar-Line1
                                                                                 |          Line2""".stripMargin)
以下是核心功能:

  def indentedHelper(parts: List[String], args: List[String]): String = {
    // In string interpolation, there is always one more string than argument
    assert(parts.size == 1+args.size)

    (parts, args) match {
      // The simple case is where there is one part (and therefore zero args). In that case,
      // we just return the string as-is:
      case (part0 :: Nil, Nil) => part0
      // If there is more than one part, we can simply take the first two parts and the first arg,
      // merge them together into one part, and then use recursion. In other words, we rewrite
      //    indented"A ${10/10} B ${2} C ${3} D ${4} E"
      // as
      //           indented"A 1 B ${2} C ${3} D ${4} E"
      // and then we can rely on recursion to rewrite that further as:
      //              indented"A 1 B 2 C ${3} D ${4} E"
      // then:
      //                 indented"A 1 B 2 C 3 D ${4} E"
      // then:
      //                    indented"A 1 B 2 C 3 D 4 E"
      case (part0 :: part1 :: tailparts, arg0 :: tailargs) => {
        // If 'arg0' has newlines in it, we will need to insert spaces. To decide how many spaces,
        // we count many characters after after the last newline in 'part0'. If there is no
        // newline, then we just take the length of 'part0':
        val i = part0.reverse.indexOf('\n')
        val n = if (i == -1)
                  part0.size // if no newlines in part0, we just take its length
                else
                  i // the number of characters after the last newline
        // After every newline in arg0, we must insert 'n' spaces:
        val arg0WithPadding = arg0.replaceAll("\n", "\n" + " "*n)
        val mergeTwoPartsAndOneArg = part0 + arg0WithPadding + part1
        // recurse:
        indentedHelper(mergeTwoPartsAndOneArg :: tailparts, tailargs)
      }
      // The two cases above are exhaustive, but the compiler thinks otherwise, hence we need
      // to add this dummy.
      case _ => ???
    }
  }

谢谢这真漂亮!在我的例子后面没有太多的语义。。。我正在输出格式化的源代码,我希望能够说
s“class Foo:\n${fields}”
以获得字段排列的结果。感谢这个有趣的示例!下面是一个指向StringContext和字符串插值文档的链接:
assert(indentedWithStripMargin"""abc               
                                |123456${"foo\nbar"}-${"Line1\nLine2"}""" == s"""|abc
                                                                                 |123456foo
                                                                                 |      bar-Line1
                                                                                 |          Line2""".stripMargin)
  def indentedHelper(parts: List[String], args: List[String]): String = {
    // In string interpolation, there is always one more string than argument
    assert(parts.size == 1+args.size)

    (parts, args) match {
      // The simple case is where there is one part (and therefore zero args). In that case,
      // we just return the string as-is:
      case (part0 :: Nil, Nil) => part0
      // If there is more than one part, we can simply take the first two parts and the first arg,
      // merge them together into one part, and then use recursion. In other words, we rewrite
      //    indented"A ${10/10} B ${2} C ${3} D ${4} E"
      // as
      //           indented"A 1 B ${2} C ${3} D ${4} E"
      // and then we can rely on recursion to rewrite that further as:
      //              indented"A 1 B 2 C ${3} D ${4} E"
      // then:
      //                 indented"A 1 B 2 C 3 D ${4} E"
      // then:
      //                    indented"A 1 B 2 C 3 D 4 E"
      case (part0 :: part1 :: tailparts, arg0 :: tailargs) => {
        // If 'arg0' has newlines in it, we will need to insert spaces. To decide how many spaces,
        // we count many characters after after the last newline in 'part0'. If there is no
        // newline, then we just take the length of 'part0':
        val i = part0.reverse.indexOf('\n')
        val n = if (i == -1)
                  part0.size // if no newlines in part0, we just take its length
                else
                  i // the number of characters after the last newline
        // After every newline in arg0, we must insert 'n' spaces:
        val arg0WithPadding = arg0.replaceAll("\n", "\n" + " "*n)
        val mergeTwoPartsAndOneArg = part0 + arg0WithPadding + part1
        // recurse:
        indentedHelper(mergeTwoPartsAndOneArg :: tailparts, tailargs)
      }
      // The two cases above are exhaustive, but the compiler thinks otherwise, hence we need
      // to add this dummy.
      case _ => ???
    }
  }