以函数式方法拆分复杂的字符串模式(无正则表达式)
Splitting complex String Pattern (without regex) in a functional approach
我正在尝试以更惯用的函数方法拆分没有正则表达式的字符串。
case class Parsed(blocks: Vector[String], block: String, depth: Int)
def processChar(parsed: Parsed, c: Char): Parsed = {
import parsed._
c match {
case '|' if depth == 0
=> parsed.copy(block = "", blocks = blocks :+ block ,
depth = depth)
case '[' => parsed.copy(block = block + c,
depth = depth + 1)
case ']' if depth == 1
=> parsed.copy( block = "", blocks = blocks :+ (block + c),
depth = depth - 1)
case ']' => parsed.copy(block = block + c,
depth = depth - 1)
case _ => parsed.copy(block = block + c)
}
}
val s = "Str|[ts1:tssub2|ts1:tssub2]|BLANK|[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]|[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]|BLANK |BLANK |[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]|[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]|BLANK |BLANK |[s14|s15]"
val parsed = s.foldLeft(Parsed(Vector(), "", 0))(processChar)
parsed.blocks.size //20
parsed.blocks foreach println
我希望得到以下结果(parsed.blocks.size 应该是 12)。
Str
[ts1:tssub2|ts1:tssub2]
BLANK|
[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]
[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
BLANK
BLANK
[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]
[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]
BLANK
BLANK
[s14|s15]
但是我得到的结果是(parsed.blocks.size是20)
Str
[ts1:tssub2|ts1:tssub2]
BLANK
[INT1|X.X.X.X|INT2|BLANK|BLANK||X.X.X.X|[INT3|s1]]
[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
BLANK
BLANK
[s2|s3|s4|INT16|INT17]
;[s5|s6|s7|INT18|INT19]
[[s8|s9|s10|INT20|INT21]|ts1:tssub2||]
;[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK]
BLANK
BLANK
[s14|s15]
据我了解,这是 parenthesis balancing problem 的细微变化。然而在这种情况下 ;
将意味着某种延续。
在这种情况下我有两个问题
1) [ts1:tssub2|ts1:tssub2]
之后的额外条目/space是怎么来的,
之后也是
[INT1|X.X.X.X|INT2|BLANK|BLANK||X.X.X.X|[INT3|s1]]
, [INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
和
;[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK]
在我的结果中也是如此?
2) 目前这里 [s2|s3|s4|INT16|INT17]
和 ;[s5|s6|s7|INT18|INT19]
作为两个不同的条目进入。但是,这应该合并为
[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]
单个条目[
[[s8|s9|s10|INT20|INT21]|ts1:tssub2||]
和
;[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK])
也是]。任何关于如何这样做的线索?
问题 1
出现额外的空字符串块是因为每次都是前一个案例
case ']' if depth == 1
它添加了一个空块并减少了深度。那么我们有
case '|' if depth == 0
这还会添加另一个空块,将之前的空块推入生成的 Vector。
在回答第二个问题之前,我想建议另一种实现此解析器的方法,这种方法稍微更惯用一些。我对当前的主要批评是使用中间对象 (Parsed
) 来包装状态并在每种情况下复制它。实际上,我们不需要它:更常见的方法是使用递归函数,尤其是当涉及 depth 时。
因此,在不显着修改您的 case
的处理的情况下,它可以表示如下:
def parse(blocks: Seq[String],
currentBlock: String,
remaining: String,
depth: Int): Seq[String] =
if (remaining.isEmpty) {
blocks
} else {
val curChar = remaining.head
curChar match {
case '|' if depth == 0 =>
parse(blocks :+ currentBlock, "", remaining.tail, depth)
case '[' =>
parse(blocks, currentBlock + curChar, remaining.tail, depth + 1)
case ']' =>
if (depth == 1)
parse(blocks :+ (currentBlock + curChar), "", remaining.tail, depth - 1)
else
parse(blocks, currentBlock + curChar, remaining.tail, depth - 1)
case _ =>
parse(blocks, currentBlock + curChar, remaining.tail, depth)
}
}
它产生与原始解决方案完全相同的输出。
要解决空块问题,我们需要更改 case '|'
:
case '|' if depth == 0 =>
val updatedBlocks = if (currentBlock.isEmpty) blocks
else blocks :+ currentBlock
parse(updatedBlocks, "", remaining.tail, depth)
如果当前块包含空字符串,我们将跳过它。
问题二
要合并 ;
字符之间的两个块,我们需要带回一个已解析的块并将其 return 放入 currentBlock
引用中。这表示另一种情况:
case ';' =>
parse(blocks.init, blocks.last + curChar, remaining.tail, depth)
现在,之后
val result = parse(Seq(), "", s, 0)
result.foreach(println)
输出为
Str
[ts1:tssub2|ts1:tssub2]
BLANK
[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]
[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
BLANK
BLANK
[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]
[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]
BLANK
BLANK
[s14|s15]
它看起来与您正在寻找的非常相似。
我正在尝试以更惯用的函数方法拆分没有正则表达式的字符串。
case class Parsed(blocks: Vector[String], block: String, depth: Int)
def processChar(parsed: Parsed, c: Char): Parsed = {
import parsed._
c match {
case '|' if depth == 0
=> parsed.copy(block = "", blocks = blocks :+ block ,
depth = depth)
case '[' => parsed.copy(block = block + c,
depth = depth + 1)
case ']' if depth == 1
=> parsed.copy( block = "", blocks = blocks :+ (block + c),
depth = depth - 1)
case ']' => parsed.copy(block = block + c,
depth = depth - 1)
case _ => parsed.copy(block = block + c)
}
}
val s = "Str|[ts1:tssub2|ts1:tssub2]|BLANK|[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]|[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]|BLANK |BLANK |[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]|[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]|BLANK |BLANK |[s14|s15]"
val parsed = s.foldLeft(Parsed(Vector(), "", 0))(processChar)
parsed.blocks.size //20
parsed.blocks foreach println
我希望得到以下结果(parsed.blocks.size 应该是 12)。
Str
[ts1:tssub2|ts1:tssub2]
BLANK|
[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]
[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
BLANK
BLANK
[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]
[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]
BLANK
BLANK
[s14|s15]
但是我得到的结果是(parsed.blocks.size是20)
Str
[ts1:tssub2|ts1:tssub2]
BLANK
[INT1|X.X.X.X|INT2|BLANK|BLANK||X.X.X.X|[INT3|s1]]
[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
BLANK
BLANK
[s2|s3|s4|INT16|INT17]
;[s5|s6|s7|INT18|INT19]
[[s8|s9|s10|INT20|INT21]|ts1:tssub2||]
;[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK]
BLANK
BLANK
[s14|s15]
据我了解,这是 parenthesis balancing problem 的细微变化。然而在这种情况下 ;
将意味着某种延续。
在这种情况下我有两个问题
1) [ts1:tssub2|ts1:tssub2]
之后的额外条目/space是怎么来的,
[INT1|X.X.X.X|INT2|BLANK|BLANK||X.X.X.X|[INT3|s1]]
, [INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
和
;[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK]
在我的结果中也是如此?
2) 目前这里 [s2|s3|s4|INT16|INT17]
和 ;[s5|s6|s7|INT18|INT19]
作为两个不同的条目进入。但是,这应该合并为
[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]
单个条目[
[[s8|s9|s10|INT20|INT21]|ts1:tssub2||]
和
;[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK])
也是]。任何关于如何这样做的线索?
问题 1
出现额外的空字符串块是因为每次都是前一个案例
case ']' if depth == 1
它添加了一个空块并减少了深度。那么我们有
case '|' if depth == 0
这还会添加另一个空块,将之前的空块推入生成的 Vector。
在回答第二个问题之前,我想建议另一种实现此解析器的方法,这种方法稍微更惯用一些。我对当前的主要批评是使用中间对象 (Parsed
) 来包装状态并在每种情况下复制它。实际上,我们不需要它:更常见的方法是使用递归函数,尤其是当涉及 depth 时。
因此,在不显着修改您的 case
的处理的情况下,它可以表示如下:
def parse(blocks: Seq[String],
currentBlock: String,
remaining: String,
depth: Int): Seq[String] =
if (remaining.isEmpty) {
blocks
} else {
val curChar = remaining.head
curChar match {
case '|' if depth == 0 =>
parse(blocks :+ currentBlock, "", remaining.tail, depth)
case '[' =>
parse(blocks, currentBlock + curChar, remaining.tail, depth + 1)
case ']' =>
if (depth == 1)
parse(blocks :+ (currentBlock + curChar), "", remaining.tail, depth - 1)
else
parse(blocks, currentBlock + curChar, remaining.tail, depth - 1)
case _ =>
parse(blocks, currentBlock + curChar, remaining.tail, depth)
}
}
它产生与原始解决方案完全相同的输出。
要解决空块问题,我们需要更改 case '|'
:
case '|' if depth == 0 =>
val updatedBlocks = if (currentBlock.isEmpty) blocks
else blocks :+ currentBlock
parse(updatedBlocks, "", remaining.tail, depth)
如果当前块包含空字符串,我们将跳过它。
问题二
要合并 ;
字符之间的两个块,我们需要带回一个已解析的块并将其 return 放入 currentBlock
引用中。这表示另一种情况:
case ';' =>
parse(blocks.init, blocks.last + curChar, remaining.tail, depth)
现在,之后
val result = parse(Seq(), "", s, 0)
result.foreach(println)
输出为
Str
[ts1:tssub2|ts1:tssub2]
BLANK
[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]
[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
BLANK
BLANK
[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]
[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]
BLANK
BLANK
[s14|s15]
它看起来与您正在寻找的非常相似。