我有一个以下字符串:
'(1:A&((2:B | 3:C)&(4:D | 5:E)))| (6:F& 7:G)'
我希望将它们解析为树层次结构对象。 1:A,2:B,3:C应该是叶子和&或者在根。 括号很重要,因此示例中的字符串应转换为:
|
/ \
/ \
& &
/ \ / \
/ \ / \
1:A & 6:F 7:G
/ \
/ \
/ \
| |
/ \ / \
2:B 3:C 4:D 5:E
我试图使用拆分括号的模式,但我无法将结果转换为合理的层次结构。 我的想法用完了,也许你们中的一些人会有一些想法?
答案 0 :(得分:1)
答案 1 :(得分:0)
我认为这样做(至少对你的例子输入而言)。
首先,我将所有X:Y
个实例替换为Leaf
个实例,将Leaf存储在缓存列表中,并将X:Y
替换为缓存中Leaf的索引。
然后,我反复查找(L O R)
模式,为它创建一个Node实例(在缓存中查找L和R),将其添加到缓存中,并将其替换为字符串中的缓存索引。
然后你留下一个字符串11 | 9
(或类似的),可以像以前一样转换成一个节点。
希望能够解释它。如果这是针对任何严重的问题,或者将来需要扩展,那么使用正确的解析器生成器可能是一个更明智的解决方案......
import groovy.transform.*
def input = '(1:A & ((2:B | 3:C) & (4:D | 5:E))) | (6:F & 7:G)'
def cache = []
// Remove all X:Y items into a lookup table
input = input.replaceAll( /\w+:\w+/ ) {
cache << new Leaf( it )
cache.size() - 1
}
// Then keep finding Nodes filling them with cached values and adding them into the cache
while( input ==~ /.*\(\d+ [&|] \d+\).*/ ) {
input = input.replaceAll( /\(\d+ [&|] \d+\)/ ) {
def (left,op,right) = it[ 1..-2 ].tokenize()
cache << new Node( cache[ left.toInteger() ], op, cache[ right.toInteger() ] )
cache.size() - 1
}
}
// Then we're left with "11 | 9", so make the root node
def (left,op,right) = input.tokenize()
def tree = new Node( cache[ left.toInteger() ], op, cache[ right.toInteger() ] )
println tree
///////////////////////////////////////////////////////
// Classes to hold leafs and nodes
@Canonical
class Leaf {
String value
String toString( String prefix ) {
"${prefix}${value}"
}
}
@TupleConstructor
class Node {
def left, op, right
String toString( String prefix ) {
"""${prefix}left:
|${prefix}${left.toString( prefix + ' ' )}
|${prefix}op: $op
|${prefix}right:
|${prefix}${right.toString( prefix + ' ' )}""".stripMargin()
}
String toString() {
toString( '' )
}
}
答案 2 :(得分:0)
使用正则表达式的替代解决方案。
这个想法是在paranthesis中递归地解析匹配叶节点和表达式的输入字符串。 parse
方法的每个invokation都将匹配它的整个输入,并返回一个带有输入值的节点(如果它是一个叶子)或一个带有两个子节点的节点,这两个子节点是递归计算的(如果它匹配一个模式的“表达式运算符表达式”)。
目前的形式需要高度定期的输入:
每当正则表达式匹配时,将收集所有匹配的组并删除空值。将有8个非空组(因为分组括号的数量始终相同),内部组将始终为数字2,3和5,其中3是运算符,2和5是左和右的输入分别是右节点。
toString
方法只是用波兰表示法写出表达式(op ex1 ex2)。
class Node {
String value
Node left, right
static leaf = /(\d:[A-Z])/
static op = /([&|])/
static par = /\((.+)\)/
static parse(String text) {
if (text ==~ "^$leaf\$") {
return new Node(value: text)
}
def matcher = text =~ "^($leaf|$par) $op ($leaf|$par)\$"
if (matcher.matches()) {
def (l,o,r) = (matcher[0] - null)[2,3,5]
return new Node(left: parse(l), value: o, right: parse(r))
}
throw new IllegalArgumentException("Irregular input: $text")
}
String toString() {
left && right ? "$value $left $right" : value
}
}
示例:
println Node.parse('1:A')
println Node.parse('1:A & 2:B')
println Node.parse('(1:A & 2:B) | 3:C')
println Node.parse('(1:A & ((2:B | 3:C) & (4:D | 5:E))) | (6:F & 7:G)')
生成以下波兰表示法输出:
1:A
& 1:A 2:B
| & 1:A 2:B 3:C
| & 1:A & | 2:B 3:C | 4:D 5:E & 6:F 7:G