正则表达式是否支持短路求值?SQLite Tcl中如何实现无捕获匹配?
问题与解答
需求背景
在SQLite Tcl接口中定义应用函数,用于WHERE子句判断JSON数组$jarray是否包含JSON数组$jnodes中的任意元素,仅返回0/1。需要解决两个问题:
- 编写无捕获组的正则表达式(原代码用了捕获组,去掉括号无法得到正确结果)
- 了解正则是否支持短路求值,以及如何实现
用户原代码及测试如下:
proc InJSONArray {jarray jnodes} { #set $node [join [split [string range $p 1 end-1] ,] |] set rnodes [string map {, |} [string range $jnodes 1 end-1] ] return [regexp "(\\\[|,)($rnodes)(,|\\\])" $jarray] } set j {[22,18,49,76,37,45,16]} set n1 {[12,58,93,67]}; # 无匹配,返回0 set n2 {[32,84,37,14]}; # 匹配37,返回1 InJSONArray $j $n1 # 输出:0 InJSONArray $j $n2 # 输出:1
用户还做了性能对比,循环实现和正则性能相近:
proc Intersect {jarray jnodes} { set nodes [split [string range $jnodes 1 end-1] ,] set array [split [string range $jarray 1 end-1] ,] foreach n $nodes { if { $n in $array } {return 1} } return 0 } proc InJSONArray {jarray jnodes} { return [regexp "(\\\[|,)([string map {, |} [string range $jnodes 1 end-1] ])(,|\\\])" $jarray] } set j {[22,18,49,76,37,45,16]} set n1 {[12,58,93,67]}; # 无匹配 set n2 {[32,84,37,14]}; # 匹配37 % timerate {Intersect $j $n1} 5000 1.932625 µs/# 2587155 # 517431 #/sec 5000.000 net-ms % timerate {Intersect $j $n2} 5000 1.835088 µs/# 2724665 # 544933 #/sec 5000.000 net-ms % timerate {InJSONArray $j $n1} 5000 1.779623 µs/# 2809584 # 561916 #/sec 5000.000 net-ms % timerate {InJSONArray $j $n2} 5000 1.617978 µs/# 3090277 # 618055 #/sec 5000.000 net-ms
解决方案
1. 无捕获组的正则写法
原代码中的括号()会创建捕获组,若要保留分组逻辑但不捕获内容,需使用非捕获组(?:...)。非捕获组仅用于定义匹配逻辑,不会保存匹配的子串,完全满足需求。
修改后的函数:
proc InJSONArray {jarray jnodes} { set rnodes [string map {, |} [string range $jnodes 1 end-1] ] # 用(?:...)替代(),消除捕获组 return [regexp "(?:\\\[|,)(?:$rnodes)(?:,|\\\])" $jarray] }
测试验证:调用该函数仍会正确返回0或1,且不会产生任何捕获组。
2. 正则的短路求值实现
Tcl的regexp命令默认是非全局匹配模式,只要找到第一个符合正则的子串,就会立即返回结果并停止扫描整个字符串,这就是天然的短路求值行为,无需额外配置。
当前的正则逻辑已经是按从左到右的顺序匹配数组元素,一旦找到第一个匹配的节点就会停止搜索,和循环中return 1的短路效果一致。
内容的提问来源于stack exchange,提问作者Gary
相关产品推荐
相关产品推荐

