Version 5 of Search pattern list

Updated 2015-10-08 16:52:44 by pooryorick

HE 2015-10-08:

lsearch determines which values in a list match a pattern. I want to determine which patterns in a list match a value.

Here is my solution:

Syntax
searchPatList mode value patList

$mode specifies the type of pattern, and is is one of:

-exact
Exact string matching.
-glob
glob-style matching as provided by string match.
-regexp
regexp matching.

$value is a string to apply each pattern to.

$patList is a list of patterns.

searchPatList returns the index of the the first pattern in $patList that matches $value, or -1 if no pattern matches.

proc searchPatList {mode value patList} {
    set n 0
    switch -exact $mode {
        -exact {
            foreach el $patList {
                if {$el eq $value} {
                    return $n
                }
                incr n
            }
            return -1
        }
        -glob {
            foreach el $patList {
                if {[string match $el $value]} {
                    return $n
                }
                incr n
            }
            return -1
        }
        -regexp {
            foreach el $patList {
                if {[regexp -- $el $value]} {
                    return $n
                }
                incr n
            }
            return -1
        }
        default {
            error "Unknown mode '$mode'!"
        }
    }
    return
}

And the examples:

set patList [list \
        {test 3.4.1} \
        {dummy} \
        {^test *[0-9.]*$} \
        {test *} \
]

searchPatList -exact {test 3.4.1} $patList
0
searchPatList -exact {test 3.4.2} $patList
-1
searchPatList -exact {dummy} $patList
1
searchPatList -glob {test 3.4.1} $patList
0
searchPatList -glob {test 3.4.2} $patList
3
searchPatList -glob {dummy} $patList
1
searchPatList -regexp {test 3.4.1} $patList
0
searchPatList -regexp {test 3.4.2} $patList
2
searchPatList -regexp {dummy} $patList
1
searchPatList -default {dummy} $patList
Unknown mode '-default'!

PYK 2015-10-08:

$patList mixes patterns which are clearly intended to be used with some particular command, yet searchPatList only operates in one mode at a time, making the overall design a bit over-engineered. This type of situation just might be the right place to use if without bracing the expressions:

set matchers {
    {{test 3.4.1} eq [lindex @val@]}
    {0 && [lindex @val@]}
    {[string match {^test *[0-9.]*$} @val@]}
    {[string match {test *} @val@]}
}
foreach matcher $matchers {
    if [string map [list @val@ [list $val]] $matcher] {
        puts [list matched $matcher]
        break
    }
}

The advantages of this approach are that patterns and their associated commands appear together, and arbitrary matching algorithms can be introduced as needed. The [lindex @val@] bit serves merely as an identity function, which is one way to properly quote a value being inserted into an expr template. Note also the use of [list $val] which is the right way to escape a value being interpolated into a Tcl script template.