【问题标题】:Scala: How to parse a string and generate a vector of nested vectorsScala:如何解析字符串并生成嵌套向量的向量
【发布时间】:2019-01-09 14:23:49
【问题描述】:

目标:

给定一个字符串的形式 val strang = "ab(cde(fgh)ijk)lmn)opq)rs"

首先我想构造一个由“(”和“)”字符分割的字符串向量。这是通过,

val regexPattern = "((?<=\\()|(?=\\())|((?<=\\))|(?=\\)))"
val data: Vector[String] = strang .split(regexPattern ).map(_.toString).toVector

请注意,实际的字符串会更加多样化,但它是如何拆分的。

其次,这是我需要帮助的困难部分。我想遍历数据向量并用以下逻辑构造一个嵌套向量(下面是psudo-codish)

val newVector =
for(row <- data) {
    if(row == "(") // opening angle bracket
        start constructing a nested vector inside of newVector
        keep appending to the nested vector until we encounter a ")"
        character (closing parenthesis).
        IF before encountering a ")" we encounter another "(" then
        create another nestedVector inside the previous nested vector
        and keep appending to it until a closing parenthesis ")" is
        encountered

    else if(row == ")")
        if we encounter a closing parenthesis, go up one level of the nested 
        vectors
    else
        simply append this row to the new vector
        newVector :+ row
}

我在这方面做了很多尝试,但都取得了有限的成功。使用递归函数,我只管理一层嵌套,但要超越它,我最终使用了 while 循环,这似乎有点过头了。嵌套向量可以是特殊类型。例如,我尝试过 case class Rowy(row: String, rows: Vector[Vector[String]]) 或者 case class Rowy(row: String, rows: Vector(Rowy))

我对 scala 比较陌生,我想知道是否有一种优雅的方法可以使用 scanLeft 或 foldLeft 方法来解决这个问题。任何帮助将不胜感激。

PS:我不使用实际的 for 循环来遍历“数据”向量,而是使用递归函数。 for 循环只是为了传达正在发生的迭代。任何帮助将不胜感激。

【问题讨论】:

  • 这只是一个学术研究问题还是一个实际问题?在后一种情况下,您将如何使用已解析的数据结构?除了不是很有用的Vector[Any] 之外,没有任何简单的标准类型可以包含这样的数据结构,因此您需要一个自定义的类型,并且根据您的使用场景对其进行定制可能会有意义。
  • 术语说明:这些是括号,而不是尖括号(尖括号是&lt; 和&gt;)。
  • 您的正则表达式模式可以简化:"((?&lt;=[()])|(?=[()]))"

标签: scala recursion nested


【解决方案1】:

在您的示例中,字符串中的括号不平衡,并且从您的算法定义中并不清楚应该如何处理它。如果我们假设输入实际上是平衡的,那么您的本机数据结构将是一棵树,分支是一级括号,叶子是“单词”。那么你需要的算法只是一个简单的基于栈的树结构。在下面的例子中我使用了显式堆栈,但是你可以将算法重写为递归,然后堆栈将是隐式的。

// Tree structure definition
sealed trait Data
object Data {
  case class Leaf(data: String) extends Data
  case class Branch(parts: Vector[Data]) extends Data {
    // a convenience method to append data to a branch more easily
    def :+(data: Data): Branch = copy(parts = parts :+ data)
  }
}

object Main extends App {
  val regexPattern = "((?<=\\()|(?=\\())|((?<=\\))|(?=\\)))"
  val string = "ab(cde(fgh)ijk)lmn"  // now the input is balanced
  val data: Vector[String] = string.split(regexPattern).toVector

  println(data)

  var stack: List[Data.Branch] = Data.Branch(Vector.empty) :: Nil
  for (part <- data) {
    part match {
      case "(" =>
        stack = Data.Branch(Vector.empty) :: stack
      case ")" =>
        val top :: parent :: rest = stack
        stack = (parent :+ top) :: rest
      case _ =>
        stack = (stack.head :+ Data.Leaf(part)) :: stack.tail
    }
  }
  val result = stack.head

  println(result)
}

它输出这个:

Vector(ab, (, cde, (, fgh, ), ijk, ), lmn)
Branch(Vector(Leaf(ab), Branch(Vector(Leaf(cde), Branch(Vector(Leaf(fgh))), Leaf(ijk))), Leaf(lmn)))

如果我们重新格式化一下,我们会得到

Branch(Vector(
  Leaf(ab),
  Branch(Vector(
    Leaf(cde),
    Branch(Vector(
      Leaf(fgh)
    )),
    Leaf(ijk)
  )),
  Leaf(lmn)
))

如您所见,它反映了字符串中的结构。

如果您的输入可能有不平衡的括号,那么您可能需要在 case ")" 部分的循环中添加额外的检查。现在,如果输入不平衡,则会抛出 MatchError,因为 stack 变量没有所需的元素数量。

【讨论】:

  • 谢谢弗拉基米尔,看起来像一棵树是要走的路。括号不会平衡,但这不应该是问题,因为简单的 daData 检查结束将解决问题。关于尖括号,原始代码被尖括号分开,但堆栈没有显示它们,所以我切换到括号,但一定没有在某处交换单词。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-09
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 1970-01-01
  • 2014-01-06
相关资源
最近更新 更多