Я пытаюсь найти элементы в списке 1, которые являются частичными совпадениями строки с элементами из списка 2, используя Tcl.
Я использую это, но это очень медленно. Есть ли более эффективный способ сделать это?
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 содержит элементы с подстановочными знаками, такими как '?' и '*'.
Я ожидаю, что следующий метод будет работать быстрее. По крайней мере, это устраняет проблему, упомянутую выше:
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)
поначалу может показаться странным. Он использует сопоставление с регулярным выражением, а затем сообщает регулярному выражению обработать шаблон как литеральную строку. Но это приводит к частичному совпадению, которое вам нужно.
Это отличается от вашей версии тем, что она может выдавать результаты в другом порядке, и один и тот же элемент из списка1 может быть сообщен несколько раз, если он соответствует более чем одному элементу в списке2.
Если это нежелательно, вы можете продолжить:
set l_matchlist [lmap item1 $list1 {
if {$item1 ni $l_matchlist} continue
set item1
}]
Конечно, это уменьшит часть прироста скорости, достигнутого ранее.
Вы можете немного схитрить и превратить задачу обработки списка в задачу обработки строк. Последние обычно немного быстрее в 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
}
}