O.k., here's how I would do it. The following sub is stand-alone. I've changed the spec slightly: lists within curlies are comma-separated rather than colon separated. That seems a bit more natural to me.
Indeed just at the same time as the infamous bug popped out, by pure coincidence I had a "real world case" in which it would have been desirable to have list expansion along with range expansion. More precisely, why using two different delimiters where one would suffice? I will use commas to separate items and colons to specify ranges and the former will have precedency over the latter.
I think that the following code is self_explanatory:
#!/usr/bin/perl -ln
use strict;
use warnings;
sub expand;
sub doit;
print for expand $_;
sub expand {
doit split /\[(.*?)\]/, shift, -1;
}
sub doit {
return @_ if @_ == 1;
my ($pre,$pat,$post)=splice @_, 0, 3;
map { doit $pre . $_ . $post, @_ }
map { /(\w+):(\w+)/ ? $1..$2 : $_ }
split /,/, $pat;
}
__END__
Example:
echo pre_[aa,01:03,bb]_[x,y]_post | ./xstr.pl
pre_aa_x_post
pre_aa_y_post
pre_01_x_post
pre_01_y_post
pre_02_x_post
pre_02_y_post
pre_03_x_post
pre_03_y_post
pre_bb_x_post
pre_bb_y_post
Now you may wonder why I still leave in expand() whereas it's
not really needed anymore. Well, this was originally meant as a quick hack,
not tremendously concerned with efficiency, for example. But it is somewhat
less and less so. So I though: well we can improve it allowing for quoting
of "special charachters" here, for example with
sub expand {
doit split
/ (?<!\\)\[
(.*?)
(?<!\\)\]/x, shift, -1;
}
instead of the above, and similarly for the regexen specifying for commas
and colons. Of course all this would require additional works if we want (as
is reasonable) to remove the quoting charachter from output and we (also
quite as consistently and reasonably) want to allow one to put literal
backslashes there by quoting them the same way (i.e. '\\').
Once we have done all this, I thought, as a side effect we can have
nested patterns, at which point expand() would become handy
once again. On a second thought, however, which occurred something like
a nanosecond later, i realized that we can't, not so naively
at least for this would require to match balanced text.
Now, if we had at our disposal Perl6's rules we could still use a
split() statement like the above (modulo any syntactical
change). Talking Perl5, I think it should be possible to do it anyway,
possibly using some (still marked as) experimental regex feature. Or else we
could resort to Text::Balanced in which case I would probably write
a separate split()ty sub for clarity, but we would loose the IMHO
aesthetically appealing immediateness of the
sub expand {
doit split /<SOME_REGEX>/, shift, -1;
}
construct.
I must say that I am puzzled by the possibility of doing all this with no
external module, and I think have devised a relatively simple way to do it,
albeit not in one swept but with the aid of some pre and post
transformations, which could be sensible after all, since we've never been
tremendously concerned about efficiency in the first place, but I will save
this for another post!
|