Scala“函数式”使用“错误嵌套”转换序列的方法格式良好的 XML

发布于 2024-11-05 06:35:06 字数 718 浏览 1 评论 0原文

抱歉,如果这是常见问题解答,我没有在任何地方找到它。这可能是一个新手 Scala 和/或函数式编程问题。我有很多 Java 和 OO 经验,但我对 Scala 和 FP 还很陌生。

假设我有一个列表,可能有:

a, b, b, c1, b, d, c2, d, a, ce, b, a, c1, b, ce, a, b

现在我要处理这个列表并返回一个 XML 树(scala.xml.NodeSeq 或其他)。棘手的部分是我需要替换任何 c的情况。范围包含所有后续项目,直到下一个 ce。更复杂的事情是必须处理嵌套,但是遇到的任何“ce”项都需要关闭所有待处理的标签。

所以我想得到这样的东西:

<a/>
<b/>
<b/>
<span style="style1">
  <b/>
  <d/>
  <span style="style2">
     <d/>
     <a/>
  </span>
</span>
<b/>
<a/>
<span style="style1">
  <b/>
</span>
<a/>
<b/>

这是在 Scala 中,我更喜欢以“纯函数方式”来完成此操作,并使用 Scala 最佳实践。我很沮丧,我无法解决这个问题。

谢谢。

Apologies if this is a FAQ, I did not find it anywhere. It is probably a newbie Scala and/or functional programming question. I have lots of Java and OO experience, but I am new to Scala and FP.

Lets say I have a list that perhaps has:

a, b, b, c1, b, d, c2, d, a, ce, b, a, c1, b, ce, a, b

Now I'm going to process this list and return an XML tree (scala.xml.NodeSeq or whatever). The tricky part is I need to replace any cases of c<n> with a span that encloses any following items, up to the next ce. Further complicating things is nesting must be handled, but any "ce" items encountered need to close out all pending tags.

So I want to get something like this out:

<a/>
<b/>
<b/>
<span style="style1">
  <b/>
  <d/>
  <span style="style2">
     <d/>
     <a/>
  </span>
</span>
<b/>
<a/>
<span style="style1">
  <b/>
</span>
<a/>
<b/>

This is in Scala, and I would prefer to do this in a "purely functional way", and using Scala best practices. I'm quite frustrated, I cannot get my head around this.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

单调的奢华 2024-11-12 06:35:06

我喜欢 Synesso 使用 foldLeft 的想法。您只需要跟踪您有多少嵌套级别。

val cN = """c(\d+)""".r
def encode(l: List[String]) = l.foldLeft("" -> 0) {
    case ((acc, nesting), "ce")      => (acc + "</span>" * nesting, 0)
    case ((acc, nesting), cN(style)) =>
        (acc + """<span style="%s">""".format(style), nesting + 1)
    case ((acc, nesting), el)        => (acc + "<%s/>".format(el), nesting)
}._1

这会返回一个 String,您稍后可以轻松地将其转换为 XML

I like Synesso's idea of using foldLeft. You just need to keep track of how many nesting levels you have.

val cN = """c(\d+)""".r
def encode(l: List[String]) = l.foldLeft("" -> 0) {
    case ((acc, nesting), "ce")      => (acc + "</span>" * nesting, 0)
    case ((acc, nesting), cN(style)) =>
        (acc + """<span style="%s">""".format(style), nesting + 1)
    case ((acc, nesting), el)        => (acc + "<%s/>".format(el), nesting)
}._1

This returns a String, which you can easily convert later to XML.

☆獨立☆ 2024-11-12 06:35:06

一些获胜的递归:

  import scala.xml._
  import scala.annotation.tailrec

  val list = List("a", "b", "b", "c1", "b", "d", "c2", "d", "a", "ce", "b", "a", "c1", "b", "ce", "a", "b")

  case class Processor(value: NodeSeq = Nil, rest: List[String] = Nil, isSub: Boolean = false)

  @tailrec
  def toXml(processor: Processor): Processor = 
    processor.rest match {
      case head::tail if(head == "ce") =>   
        if(processor.isSub) Processor(processor.value, processor.rest)        
        else toXml(Processor(processor.value, tail))

      case head::tail if(head.startsWith("c")) => 
        val result = toXml(Processor(Nil, tail, true)) 
        toXml(processor.copy(value = processor.value ++ <span>{result.value}</span>, rest = result.rest))  

      case head::tail => toXml(processor.copy(value = processor.value ++ Elem(null, head, null, TopScope), rest = tail))

      case Nil => processor
    }

  def toXml(input: List[String]): NodeSeq = toXml(Processor(Nil, input)).value


scala> toXml(list).toString
res28: String = <a></a><b></b><b></b><span><b></b><d></d><span><d></d><a></a></span></span><b></b><a></a><span><b></b></span><a></a><b></b>

Some recursion for the win:

  import scala.xml._
  import scala.annotation.tailrec

  val list = List("a", "b", "b", "c1", "b", "d", "c2", "d", "a", "ce", "b", "a", "c1", "b", "ce", "a", "b")

  case class Processor(value: NodeSeq = Nil, rest: List[String] = Nil, isSub: Boolean = false)

  @tailrec
  def toXml(processor: Processor): Processor = 
    processor.rest match {
      case head::tail if(head == "ce") =>   
        if(processor.isSub) Processor(processor.value, processor.rest)        
        else toXml(Processor(processor.value, tail))

      case head::tail if(head.startsWith("c")) => 
        val result = toXml(Processor(Nil, tail, true)) 
        toXml(processor.copy(value = processor.value ++ <span>{result.value}</span>, rest = result.rest))  

      case head::tail => toXml(processor.copy(value = processor.value ++ Elem(null, head, null, TopScope), rest = tail))

      case Nil => processor
    }

  def toXml(input: List[String]): NodeSeq = toXml(Processor(Nil, input)).value


scala> toXml(list).toString
res28: String = <a></a><b></b><b></b><span><b></b><d></d><span><d></d><a></a></span></span><b></b><a></a><span><b></b></span><a></a><b></b>
夏日落 2024-11-12 06:35:06

我怀疑该列表不仅仅是孤立存在的,它以文件中的行、逗号分隔值的字符串或其他某种格式开始 - 然后您可以根据需要将其拆分以生成列表。

您可以从原始输入开始,然后重新表述问题,而不是这样做:

给定这个原始输入,我如何处理它以产生以下结果
树结构?

(我将其输出为 XML)

答案是“使用 Scala 的解析器”,这将为您提供一些精美的声明性代码。您还会发现它比您自己滚动的任何东西都更容易维护。

I suspect that list didn't just come to exist in isolation, it began as lines in a file, or a string of comma-separated values, or some other format - You then split it as appropriate to generate the list.

Instead of doing that, you could start with the original input and then rephrase the question:

Given this raw input, how can I process it to produce the following
tree structure?

(which I'll be outputting as XML)

To which the answer is "use Scala's Parsers", which will give you some beautifully declarative code. You'll also find it easier to maintain than anything you roll yourself.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文