我正在尝试验证字符串是否与规则列表匹配。 例如,这些列表项中的哪一个与哪个匹配规则匹配:
set ListToCheck [list abc_123 def_123 ghi_456 abc_345 xyz_987]
set RulesToCheck [list *_123 abc_*]
我最终将列出许多规则的长列表,以检查一长串字符串,并不断增长。我只想要第一场比赛。
我提出的方法似乎有点蛮力。我在想应该有一个更优雅的方法
set match 0
set matchedrule {}
set matchdict {}
foreach value $ListToCheck {
foreach rule $RulesToCheck {
if {[string match $rule $value] == 1} {
set match 1
set matchedrule $rule
break
}
}
<take some action on the $value and $rule matched here>
...
}
这是最好的方法吗?我觉得应该有更好的方法。
答案 0 :(得分:0)
如果您的规则是由glob模式真实描述的,那么您可以做很多事情来优化检测哪一个匹配。好吧,除非你得到所有匹配理论并做巧妙的重写,但这很难,但这很难。
你可以做一些事情来制作稍微快一点的匹配器。 警告!以下内容包括代码生成。
# Build a lambda that uses [switch -glob]
set ruleset {}
foreach rule $RulesToCheck {
lappend ruleset $rule [list return [list 1 $rule]]
}
set matcher [list value "switch -glob -- \$value [list $ruleset];return {0 {}}"]
# Now we can use the lambda term as much as we want
foreach value $ListToCheck {
lassign [apply $matcher $value] match matchedrule
# take some action on the $value and $rule matched here
# ...
# I tested with:
# if {$match} {puts "$value was matched by $matchedrule"}
}
我没有计时这段代码,但是要检查合理数量的规则和项目,它应该做得更好,因为它会产生更好的字节码(需要花费apply
来做每个匹配,这与调用过程一样昂贵。
答案 1 :(得分:0)
另一种方法是将glob模式的集合转换为单个RE。
# Note that this is Tcl 8.6 syntax - both [lmap] and [string cat] are used
# Rewriting into 8.5 syntax is left as an exercise
set RE [string cat "(" [join [lmap s $RulesToCheck {
# Assuming you've not got any [...] bits in your glob pattern...
string cat "^" [string map {* .* ? . \\ {\\}} $s] "$"
}] ")|("] ")"]
foreach value $ListToCheck {
set matchinfo [regexp -inline -indices -- $RE $value]
if {[llength $matchinfo]} {
foreach pattern $RulesToCheck idxs [lrange $matchinfo 1 end] {
if {[lindex $idxs 0] >= 0} {
puts "matched $pattern against $value"
break
}
}
}
}
我不确定在什么情况下这会比我之前建立一个lambda的答案更有效率;确定哪个匹配发生的成本似乎本身就很烦人(虽然如果匹配率足够低且模式足够复杂,那么可能 ......)