一个列表与另一个列表的 Tcl 部分字符串匹配

Tcl partial string match of one list against another

我正在尝试使用 Tcl 在 list1 中查找与 list2 中的项目部分字符串匹配的项目。

我正在使用它,但速度很慢。有没有更有效的方法来做到这一点?

set list1 [list abc bcd cde]
set list2 [list ab cd]

set l_matchlist [list]
foreach item1 $list1 {
     foreach item2 $list2 {
          if {[string match -nocase "*${item2}*" $item1]} {
               lappend l_matchlist $item1
               break
          }
     }
}

我的实际列表很长,这需要很长时间。这是最好的方法吗?

除了速度慢之外,如果 list2 包含具有 glob 通配符(例如“?”)的元素,也会出现问题。和'*'。

我希望下面的方法工作得更快。至少它解决了上面提到的问题:

set list1 [list abc BCD ace cde]
set list2 [list cd ab de]

set l_matchlist [list]
foreach item2 $list2 {
    lappend l_matchlist \
      {*}[lsearch -all -inline -nocase -regexp $list1 (?q)$item2]
}

-regexp 选项与 (?q) 结合使用乍一看可能很奇怪。它使用正则表达式匹配,然后告诉正则表达式将模式视为文字字符串。但这具有执行您之后的部分匹配的效果。

这与您的版本不同,因为它可能会以不同的顺序生成结果,并且如果 list1 中的同一项目与 list2 中的多个项目匹配,则可能会报告多次。

如果不需要,您可以跟进:

set l_matchlist [lmap item1 $list1 {
    if {$item1 ni $l_matchlist} continue
    set item1
}]

当然,这会降低之前取得的一些速度收益。

你可以稍微作弊,把它从一个 list-processing 任务变成一个字符串处理任务。后者在 Tcl 中通常要快得多。

下面我先把list1转成一个字符串,原来的列表元素之间用ASCII字段分隔符“\x1F”隔开。然后可以通过正则表达式搜索在单个循环中获得结果。正则表达式查找由包含 item2:

的字段分隔符字符限定的第一个子字符串
# convert list to string:
set string1 \x1F[join $list1 \x1F]\x1F

set l_matchlist [list]
foreach item2 $list2 {
    # escape out regexp special chars:
    set item2 [regsub -all {\W} $item2 {\&}]
    # use append to assemble regexp pattern
    set item2 [append x {[^\x1F]*} $item2 {[^\x1F]*}][unset x]
    if {[regexp -nocase $item2 $string1 match]} {
        lappend l_matchlist $match
    }
}