in reply to Compiled regex as hash key vs. string?

n.b. blahblah and ikegami and me had a long conversation about his real problem on the chatterbox, earlier today, and we found several entirely different approaches to achieve what he really wants — ikegami even provided a complete alternative program. So, just for the annals, here's a little reply that is just taking the direction of his question here.

I now know you want to distinguish between data matching one of two kinds of sources, it either equals this hash key string (if it's supposed to be treated as a string) or matches the regexp (if it represents a regexp) — the above hash is a kind of dispatch table, originally just matching literal strings, and now he has to extend it to match using a pattern — preferably in a backward compatible way. But you can't distinguish between strings and regexes in hash keys any more, since every hash key is nothing but a string.

One approach I prosposed is to always use a regexp for the hash keys, converting strings to a regexp using quotemeta and string start/end anchors. That way you don't have to make the distinction. But that involves recompiling the regex every time you want to do a test. Not so good.

So another approach is to have an extra caching hash, that maps these string keys to, well, whatever you like. I suggest you use something looking like globs on the left, because it's probably easier for maintenance, eventually converting them to the equivalent regexp by code — once.

my %process = ( 'foo*' => qr/^foo.*$/, foo => 'foo' );
Now you can make the distinction you asked about, in the values of this hash. And regexps remain Regexps.

As a sample converter of glob to regexp, you can do something like this:

{ my %replace; sub glob_to_regexp { my $pattern = shift; %replace or %replace = ( '*' => '.*', '?' => '.' ); $pattern =~ s/(\W)/ $replace{$1} || quotemeta($1)/ge; return qr/^$pattern\z/; } }

Of course, you can implement more advanced glob features if you like, patching the convertor code accordingly.