home *** CD-ROM | disk | FTP | other *** search
/ isnet Internet / Isnet Internet CD.iso / prog / hiz / 09 / 09.exe / adynware.exe / perl / lib / site / String / Approx.pm
Encoding:
Perl POD Document  |  1999-12-28  |  19.6 KB  |  839 lines

  1. package String::Approx;
  2.  
  3. =head1 NAME
  4.  
  5. String::Approx - match and substitute approximately (aka fuzzy matching)
  6.  
  7. =head1 SYNOPSIS
  8.  
  9.     use String::Approx qw(amatch asubstitute);
  10.  
  11. =head1 DESCRIPTION
  12.  
  13. B<Approximate> is defined here as I<k-differences>.  One I<difference>
  14. is an insertion, a deletion, or a substitution of one character.
  15. The I<k> in the I<k-differences> is the maximum number of differences.
  16.  
  17. For example I<1-difference> means that a match is found if there is
  18. one character too many (insertion) or one character missing (deletion)
  19. or one character changed (substitution).  Those are I<exclusive or>s:
  20. that is, I<not> one of each type of modification but I<exactly one>.
  21.  
  22. =head2 The default approximateness
  23.  
  24. The default approximateness is I<10 %> of the length of the
  25. approximate pattern or I<at least 1>: I<0-differences> being the exact
  26. matching which can be done very effectively using the usual Perl
  27. function C<index()> or normal regular expression matching.
  28.  
  29. =head2 amatch
  30.  
  31.     use String::Approx qw(amatch);
  32.  
  33.     amatch("PATTERN");
  34.     amatch("PATTERN", @LIST);
  35.     amatch("PATTERN", [ @MODS ]);
  36.     amatch("PATTERN", [ @MODS ], @LIST);
  37.  
  38. The PATTERN is B<a string>, not a regular expression.  The regular
  39. expression metanotation (C<. ? * + {...,...} ( ) | [ ] ^ $ \w ...>)
  40. will be understood as literal characters, that is, a C<*> means in
  41. regex terms C<\*>, not I<"match 0 or more times">.
  42.  
  43. The LIST is the list of strings to match against the pattern.
  44. If no LIST is given matches against C<$_>.
  45.  
  46. The MODS are the modifiers that tell how approximately to match.
  47. See below for more detailed explanation.
  48. B<NOTE>: The syntax really is C<[ @MODS ]>, the square
  49. brackets C<[ ]> must be in there.  See below for examples.
  50.  
  51. In scalar context C<amatch()> returns the number of successful
  52. matches.  In list context C<amatch()> returns the strings that
  53. had matches.
  54.  
  55. Example:
  56.  
  57.     use String::Approx qw(amatch);
  58.  
  59.     open(WORDS, '/usr/dict/words') or die;
  60.  
  61.     while (<WORDS>) {
  62.         print if amatch('perl');
  63.     }
  64.  
  65. or the same ignoring case:
  66.  
  67.     use String::Approx qw(amatch);
  68.  
  69.     open(WORDS, '/usr/dict/words') or die;
  70.  
  71.     while (<WORDS>) {
  72.         print if amatch('perl', ['i']);
  73.     }
  74.  
  75. =head2 asubstitute
  76.  
  77.     use String::Approx qw(asubstitute);
  78.  
  79.     asubstitute("PATTERN", "SUBSTITUTION");
  80.     asubstitute("PATTERN", "SUBSTITUTION", @LIST);
  81.     asubstitute("PATTERN", "SUBSTITUTION", [ @MODS ]);
  82.     asubstitute("PATTERN", "SUBSTITUTION", [ @MODS ], @LIST);
  83.  
  84. The PATTERN is B<a string>, not a regular expression.  The regular
  85. expression metanotation (C<. ? * + {...,...} ( ) | [ ] ^ $ \w ...>)
  86. will be understood as literal characters, that is, a C<*> means in
  87. regex terms C<\*>, not I<"match 0 or more times">.
  88.  
  89. Also the SUBSTITUTION is B<a string>, not a regular expression.  Well,
  90. mostly.  I<Most of the> regular expression metanotation (C<.>, C<?>,
  91. C<*>, C<+>, ...) will be not understood as literal characters, that
  92. is, a C<*> means in regex terms C<\*>, not I<"match 0 or more times">.
  93. The understood notations are
  94.  
  95. =over 8
  96.  
  97. =item    C<$`>
  98.  
  99. the part I<before> the approximate match
  100.  
  101. =item    C<$&>
  102.  
  103. the approximately matched part
  104.  
  105. =item    C<$'>
  106.  
  107. the part I<after> the approximate match
  108.  
  109. =back
  110.  
  111. The MODS are the modifiers that tell how approximately to match.
  112. See below for more detailed explanation.
  113. B<NOTE>: Yes, the syntax is really C<[ @MODS ]>, the square
  114. brackets C<[ ]> must be in there.  See below for examples.
  115.  
  116. The LIST is the list of strings to substitute against the pattern.
  117. If no LIST is given substitutes against C<$_>.
  118.  
  119. In scalar context C<asubstitute()> returns the number of successful
  120. substitutions.  In list context C<asubstitute()> returns the strings
  121. that had substitutions.
  122.  
  123. Examples:
  124.  
  125.     use String::Approx qw(asubstitute);
  126.  
  127.     open(WORDS, '/usr/dict/words') or die;
  128.     while (<WORDS>) {
  129.         print if asubstitute('perl', '($&)');
  130.     }
  131.  
  132. or the same ignoring case:
  133.  
  134.     use String::Approx qw(asubstitute);
  135.  
  136.     open(WORDS, '/usr/dict/words') or die;
  137.     while (<WORDS>) {
  138.         print if asubstitute('perl', '($&)', [ 'i' ]);
  139.     }
  140.  
  141. =head2 Modifiers
  142.  
  143. The MODS argument both in amatch() and asubstitute() is a list of
  144. strings that control the matching of PATTERN.  The first two, B<i> and
  145. B<g>, are the usual regular expression match/substitute modifiers, the
  146. rest are special for approximate matching/substitution.
  147.  
  148. =over 8
  149.  
  150. =item    i
  151.  
  152. Match/Substitute ignoring case, case-insensitively.
  153.  
  154. =item    g
  155.  
  156. Substitute I<globally>, that is, all the approximate matches, not just
  157. the first one.
  158.  
  159. =item    I<k>
  160.  
  161. The maximum number of differences.
  162. For example 2.
  163.  
  164. =item    II<k>
  165.  
  166. The maximum number of insertions.
  167. For example 'I2'.
  168.  
  169. =item    DI<k>
  170.  
  171. The maximum number of deletions.
  172. For example 'D2'.
  173.  
  174. =item    SI<k>
  175.  
  176. The maximum number of substitutions.
  177. For example 'S2'.
  178.  
  179. =item    I<k>%
  180.  
  181. The maximum relative number of differences.
  182. For example '10%'.
  183.  
  184. =item    II<k>%
  185.  
  186. The maximum relative number of insertions.
  187. For example 'I5%'.
  188.  
  189. =item    DI<k>%
  190.  
  191. The maximum relative number of deletions.
  192. For example 'D5%'.
  193.  
  194. =item    SI<k>%
  195.  
  196. The maximum relative number of substitutions.
  197. For example 'S5%'.
  198.  
  199. =back
  200.  
  201. I<The regular expression modifiers> C<o m s x> I<are> B<not supported>
  202. because their definitions for approximate matching are less than clear.
  203.  
  204. The relative number of differences is relative to the length of the
  205. PATTERN, rounded up: if, for example, the PATTERN is C<'bouillabaise'>
  206. and the MODS is C<['20%']> the I<k> becomes I<3>.
  207.  
  208. If you want to B<disable> a particular kind of difference you need
  209. to explicitly set it to zero: for example C<'D0'> allows no deletions.
  210.  
  211. In case of conflicting definitions the later ones silently override,
  212. for example:
  213.  
  214.     [2, 'I3', 'I1']
  215.  
  216. equals
  217.  
  218.     ['I1', 'D2', 'S2']
  219.  
  220. =head1 EXAMPLES
  221.  
  222. The following examples assume the following template:
  223.  
  224.     use String::Approx qw(amatch asubstitute);
  225.  
  226.     open(WORDS, "/usr/dict/words") or die;
  227.     while (<WORDS>) {
  228.     }
  229.  
  230. and the following examples just replace the above 'C<# E<lt>--->' line.
  231.  
  232. =head2 Matching from the C<$_>
  233.  
  234. =over 8
  235.  
  236. =item Match 'perl' with one difference
  237.  
  238.     print if amatch('perl');
  239.  
  240. The I<one difference> is automatically the result in this case because
  241. first the rule of the I<10 %> of the length of the pattern ('C<perl>')
  242. is used and then the I<at least 1> rule.
  243.  
  244. =item Match 'perl' with case ignored
  245.  
  246.     print if amatch('perl', [ 'i' ]);
  247.  
  248. The case is ignored in matching (C<i>).
  249.  
  250. =item Match 'perl' with one insertion
  251.  
  252.     print if amatch('perl', [ '0', 'I1' ]);
  253.  
  254. The I<one insertion> is easiest achieved with first disabling any
  255. approximateness (C<0>) and then enabling one insertion (C<I1>).
  256.  
  257. =item Match 'perl' with zero deletions
  258.  
  259.     print if amatch('perl', [ 'D0' ]);
  260.  
  261. The I<zero deletion> is easily achieved with simply disabling any
  262. deletions (C<D0>), the other types of differences, the insertions and
  263. substitutions, are still enabled.
  264.  
  265. =item Substitute 'perl' approximately with HTML emboldening
  266.  
  267.     print if amatch('perl', '<B>$&</B>', [ 'g' ]);
  268.  
  269. All (C<g>) of the approximately matching parts of the input are
  270. surrounded by the C<HTML> emboldening markup.
  271.  
  272. =back
  273.  
  274. =head2 Matching from a list
  275.  
  276. The above examples match against the default variable B<$_>.
  277. The rest of the examples show how the match from a list.
  278. The template is now:
  279.  
  280.     use String::Approx qw(amatch asubstitute);
  281.  
  282.     open(WORDS, "/usr/dict/words") or die;
  283.     @words = <words>;
  284.  
  285. and the examples still go where the 'C<# E<lt>--->' line is.
  286.  
  287. =over 8
  288.  
  289. =item Match 'perl' with one difference from a list
  290.  
  291.     @matched = amatch('perl', @words);
  292.  
  293. The C<@matched> contains the elements of the C<@words> that matched
  294. approximately.
  295.  
  296. =item Substitute 'perl' approximately with HTML emphasizing from a list
  297.  
  298.     @substituted = asubstitute('perl', '<EM>$&</EM>', [ 'g' ], @words);
  299.  
  300. The C<@substituted> contains B<with all> (C<g>) B<the substitutions>
  301. the elements of the C<@words> that matched approximately.
  302.  
  303. =back
  304.  
  305. =head1 ERROR MESSAGES
  306.  
  307. =over 8
  308.  
  309. =item amatch: $_ is undefined: what are you matching against?
  310.  
  311. =item asubstitute: $_ is undefined: what are you matching against?
  312.  
  313. These happen when you have nothing in C<$_> and try to C<amatch()> or
  314. C<asubstitute()>.  Perhaps you are using the Perl option C<-e> but you
  315. did forget the Perl option C<-n>?
  316.  
  317. =item amatch: too long pattern.
  318.  
  319. This happens when the pattern is too long for matching.
  320.  
  321. When matching long patterns, C<String::Approx> attempts to partition
  322. the match.  In other words, it tries to do the matching incrementally
  323. in smaller parts.
  324.  
  325. If this fails the above message is shown.  Please try using shorter
  326. match patterns.
  327.  
  328. See below for L<LIMITATIONS/Pattern length> for more detailed
  329. explanation why this happens.
  330.  
  331. =item asubstitute: too long pattern.
  332.  
  333. This happens when the pattern is too long for substituting.
  334.  
  335. The partitioning scheme explained above that is used for matching long
  336. patterns cannot, sadly enough, be used substituting.
  337.  
  338. Please try using shorter substitution patterns.
  339.  
  340. See below for L<LIMITATIONS/Pattern length> for more detailed
  341. explanation why this happens.
  342.  
  343. =back
  344.  
  345. =head1 VERSION
  346.  
  347. Version 2.1.
  348.  
  349. =head1 LIMITATIONS
  350.  
  351. =head2 Fixed pattern
  352.  
  353. The PATTERNs of C<amatch()> and C<asubstitute()> are fixed strings,
  354. they are not regular expressions.  The I<SUBSTITUTION> of
  355. C<asubstitute()> is a bit more flexible than that but not by much.
  356.  
  357. =head2 Pattern length
  358.  
  359. The approximate matching algorithm is B<very aggressive>.  In
  360. mathematical terms it is I<O(exp(n) * x**2)>. This means that
  361. when the pattern length and/or the approximateness grows the
  362. matching or substitution take much longer time and memory.
  363.  
  364. For C<amatch()> this can be avoided by I<partitioning> the pattern,
  365. matching it in shorter subpatterns.  This makes matching a bit slower
  366. and a bit more fuzzier, more approximate.  For C<asubstitute()> this
  367. partitioning cannot be done, the absolute maximum for the substitution
  368. pattern length is B<19> but sometimes, for example it the approximateness
  369. is increased, even shorter patterns are too much.  When this happens,
  370. you must use shorter patterns.
  371.  
  372. =head2 Speed
  373.  
  374. I<Despite the about 20-fold speed increase> from the C<String::Approx>
  375. I<version 1> B<agrep is still faster>.  If you do not know what
  376. C<agrep> is: it is a program like the UNIX grep but it knows, among
  377. other things, how to do approximate matching.  C<agrep> is still about
  378. 30 times faster than I<Perl> + C<String::Approx>.  B<NOTE>: all these
  379. speeds were measured in one particular system using one particular set
  380. of tests: your mileage will vary.
  381.  
  382. For long patterns, more than about B<40>, the first 
  383.  
  384. =head2 Incompatibilities with C<String::Approx> I<v1.*>
  385.  
  386. If you have been using regular expression modifiers (B<i>, B<g>) you
  387. lose.  Sorry about that.  The syntax simply is not compatible.  I had
  388. to choose between having C<amatch()> match and C<asubstitute()>
  389. substitute elsewhere than just in $_ I<and> the old messy way of
  390. having an unlimited number of modifiers.  The first need won.
  391.  
  392. B<There is a backward compability mode>, though, if you do not want to
  393. change your C<amatch()> and C<asubstitute()> calls.  You B<have> to
  394. change your C<use> line, however:
  395.  
  396.     use String::Approx qw(amatch compat1);
  397.  
  398. That is, you must add the C<compat1> symbol if you want to be
  399. compatible with the C<String::Approx> version 1 call syntax.
  400.  
  401. =head1 AUTHOR
  402.  
  403. Jarkko Hietaniemi C<E<lt>jhi@iki.fiE<gt>>
  404.  
  405. =head1 ACKNOWLEDGEMENTS
  406.  
  407. Nathan Torkington C<E<lt>gnat@frii.comE<gt>>
  408.  
  409. =cut
  410.  
  411. require 5;
  412.  
  413. use strict;
  414. $^W = 1;
  415.  
  416. use vars qw($PACKAGE $VERSION $compat1
  417.         @ISA @EXPORT_OK
  418.         %P @aL @dL @Pl %Pp);
  419.  
  420. $PACKAGE = 'String::Approx';
  421. $VERSION = '2.0';
  422.  
  423. $compat1 = 0;
  424.  
  425. require Exporter;
  426.  
  427. @ISA = qw(Exporter);
  428.  
  429. @EXPORT_OK = qw(amatch asubstitute);
  430.  
  431.  
  432. sub import {
  433.     my $this = shift;
  434.     my (@list, $sym);
  435.     for $sym (@_) { $sym eq 'compat1' ? $compat1 = 1 : push(@list, $sym) }
  436.     local $Exporter::ExportLevel = 1; 
  437.     Exporter::import($this, @list);
  438. }
  439.  
  440. sub _estimate {
  441.     my ($l, $m) = @_;
  442.     my $p = 5 ** ($m + 2);
  443.  
  444.     (3 * $p * $l ** 2 + (8 - $p) * $l - $p) / 8;
  445. }
  446.  
  447. sub _compile {
  448.     my ($pattern, $I, $D, $S) = @_;
  449.     my ($j, $p, %p, %q, $l, $k, $mxm);
  450.     my @p = ();
  451.  
  452.     $mxm = $I;
  453.     $mxm = $D if ($D > $mxm);
  454.     $mxm = $S if ($S > $mxm);
  455.  
  456.     $l = length($pattern);
  457.  
  458.  
  459.  
  460.     my $est = _estimate($l, $mxm);
  461.  
  462.     if ($est > 32767) {
  463.     my ($a, $b, $i);
  464.     my $mp;
  465.  
  466.  
  467.  
  468.     unless (defined $Pl[$l][$mxm]) {
  469.         my ($np, $sp, $fp, $gp);
  470.  
  471.         $np = int(log($l)) + 1;
  472.         $np = 2 if ($np < 2);
  473.         $sp = int($l / $np);
  474.         $fp = $l - $np * $sp;
  475.         $gp = $sp + $fp;
  476.         $mp = int($mxm / $np);
  477.         $mp = 1 if ($mp < 1);
  478.  
  479.  
  480.         $est = _estimate($gp, $mp);
  481.  
  482.  
  483.         while ($est > 32767) {
  484.         $sp--;
  485.         $np = int($l / $sp);
  486.         $fp = $l - $np * $sp;
  487.         $gp = $sp + $fp;
  488.         $mp = int($mxm / $np);
  489.         $mp = 1 if ($mp < 1);
  490.         $est = _estimate($gp, $mp);
  491.         }
  492.  
  493.         ($a, $b) = (0, $sp + $fp);
  494.         push(@{$Pl[$l][$mxm]}, [$a, $b]);
  495.         $a += $fp;
  496.         $b  = $sp;
  497.         for ($i = 1; $i < $np; $i++) {
  498.         $a += $sp;
  499.         push(@{$Pl[$l][$mxm]}, [$a, $b]);
  500.         }
  501.     }
  502.  
  503.     my $pi = $I ? int($mp / $I + 0.9) : 0;
  504.     my $pd = $D ? int($mp / $D + 0.9) : 0;
  505.     my $ps = $S ? int($mp / $S + 0.9) : 0;
  506.  
  507.  
  508.     unless (defined $Pp{$pattern}[$mxm]) {
  509.         for $i (@{$Pl[$l][$mxm]}) {
  510.         push(@{$Pp{$pattern}[$mxm]},
  511.              [substr($pattern, $$i[0], $$i[1]), $pi, $pd, $ps]);
  512.         }
  513.     }
  514.  
  515.     @p = @{$Pp{$pattern}[$mxm]};
  516.     
  517.     } else {
  518.     push(@p, [$pattern, $I, $D, $S]);
  519.     }
  520.  
  521.     my $i0 = 1;        # The start index for the insertions.
  522.  
  523.     my $pp;        # The current partition.
  524.  
  525.     for $pp (@p) {    # The partition loop.
  526.  
  527.     %p = ();
  528.  
  529.     my ($i, $d, $s) = @$pp[1..4];    # The per-partition I, D, S.
  530.  
  531.     $pp = $$pp[0];            # The partition string itself.
  532.  
  533.  
  534.     $p{$pp} = length($pp);
  535.  
  536.     while ($i or $d or $s) {
  537.  
  538.         %q = ();
  539.     
  540.  
  541.         if ($i) {
  542.         $i--;
  543.         while (($p, $l) = each %p) {
  544.             my $lp1 = $l + 1;
  545.  
  546.             for ($j = $i0; $j < $l; $j++) {
  547.             $k = $p;
  548.             substr($k, $j) = '.' . substr($k, $j);
  549.             $q{$k} = $lp1;
  550.             }
  551.         }
  552.  
  553.  
  554.         $i0 = 0;
  555.         }
  556.  
  557.  
  558.         if ($d) {
  559.         $d--;
  560.         while (($p, $l) = each %p) {
  561.             if ($l) {
  562.             my $lm1 = $l - 1;
  563.  
  564.             for ($j = 0; $j < $l; $j++) {
  565.                 $k = $p;
  566.                 substr($k, $j) = substr($k, $j + 1);
  567.                 $q{$k} = $lm1;
  568.             }
  569.             }
  570.         }
  571.         }
  572.  
  573.  
  574.         if ($s) {
  575.         $s--;
  576.         while (($p, $l) = each %p) {
  577.             for ($j = 0; $j <= $l; $j++) {
  578.             $k = $p;
  579.             substr($k, $j, 1) = '.';
  580.             $q{$k} = $l;
  581.             }
  582.         }
  583.         }
  584.  
  585.         while (($k, $l) = each %q) { $p{$k} = $l }
  586.     }
  587.  
  588.  
  589.     push(@{$P{$pattern}[$I][$D][$S]},
  590.          join('|', sort { length($b) <=> length($a) } keys %p));
  591.  
  592.     }
  593. }
  594.  
  595. sub _mods {
  596.     my ($mods, $aI, $aD, $aS, $rI, $rD, $rS) = @_;
  597.     my $remods = '';
  598.     my $mod;
  599.  
  600.     for $mod (@$mods) {
  601.     while ($mod ne '') {
  602.         if ($mod =~ s/^([IDS]?)(\d+)(%?)//) {
  603.         if ($1 ne '') {
  604.             if ($3 ne '') {
  605.             if    ($1 eq 'I') { $$rI = 0.01 * $2 }
  606.             elsif ($1 eq 'D') { $$rD = 0.01 * $2 }
  607.             else              { $$rS = 0.01 * $2 }
  608.             } else {
  609.             if    ($1 eq 'I') { $$aI = $2 }
  610.             elsif ($1 eq 'D') { $$aD = $2 }
  611.             else              { $$aS = $2 }
  612.             }
  613.         } else {
  614.             if ($3 ne '') {
  615.             $$rI = $$rD = $$rS = 0.01 * $2;
  616.             } else {
  617.             $$aI = $$aD = $$aS = $2;
  618.             }
  619.         }
  620.         } elsif ($compat1 and $mod =~ s/^([igmsxo])//) {
  621.         $remods .= $1;
  622.         } elsif ($mod =~ s/^([ig])//) {
  623.         $remods .= $1;
  624.         } else {
  625.         die $PACKAGE, ": unknown modifier '$mod'\n";
  626.         }
  627.     }
  628.     }
  629.  
  630.     $remods ne '' ? $remods : undef;
  631. }
  632.  
  633. sub _mids {
  634.     my ($len, $aI, $aD, $aS, $rI, $rD, $rS) = @_;
  635.  
  636.     my $r = int(0.1 * $len + 0.9);
  637.  
  638.     if    (    defined $rI) { $aI = int($rI * $len) }
  639.     elsif (not defined $aI) { $aI = $r }
  640.  
  641.     if    (    defined $rD) { $aD = int($rD * $len) }
  642.     elsif (not defined $aD) { $aD = $r }
  643.  
  644.     if    (    defined $rS) { $aS = int($rS * $len) }
  645.     elsif (not defined $aS) { $aS = $r }
  646.  
  647.     ($aI, $aD, $aS);
  648. }
  649.  
  650. sub amatch {
  651.     my ($pattern, @list) = @_;
  652.     my ($aI, $aD, $aS, $rI, $rD, $rS);
  653.  
  654.     my $len = length($pattern);
  655.  
  656.     my $remods;
  657.  
  658.     if ($compat1 or ref $list[0]) {
  659.     my $mods;
  660.  
  661.     if ($compat1) {
  662.         $mods = [ @list ];
  663.         @list = ();
  664.     } else {
  665.         $mods = shift(@list);
  666.     }
  667.  
  668.     $remods = _mods($mods, \$aI, \$aD, \$aS, \$rI, \$rD, \$rS);
  669.  
  670.     ($aI, $aD, $aS) = _mids($len, $aI, $aD, $aS, $rI, $rD, $rS);
  671.     } else {
  672.     $dL[$len] = int(0.1 * $len + 0.9) unless $dL[$len];
  673.     $aI = $aD = $aS = $dL[$len];
  674.     }
  675.  
  676.     die "amatch: \$_ is undefined: what are you matching against?\n"
  677.     if (not defined $_ and @list == 0);
  678.  
  679.     _compile($pattern, $aI, $aD, $aS)
  680.     unless ref $P{$pattern}[$aI][$aD][$aS];
  681.  
  682.     my @mpat = @{$P{$pattern}[$aI][$aD][$aS]};
  683.     my $mpat;
  684.  
  685.  
  686.     if (@mpat == 1) {
  687.  
  688.  
  689.     $mpat = $mpat[0];
  690.  
  691.     $mpat = '(?' . $remods . ')' . $mpat if defined $remods;
  692.  
  693.  
  694.     if (@list) {
  695.  
  696.  
  697.         my @m = eval { grep /$mpat/, @list };
  698.         die "amatch: too long pattern.\n"
  699.         if ($@ =~ /regexp too big/);
  700.         return @m;
  701.     }
  702.  
  703.  
  704.     my $m;
  705.  
  706.     eval { $m = /$mpat/ };
  707.     die "amatch: too long pattern.\n"
  708.         if ($@ =~ /regexp too big/);
  709.     return ($_) if $m;
  710.  
  711.     } else {
  712.  
  713.  
  714.     if (@list) {
  715.  
  716.  
  717.         my @pos = ();
  718.         my @bad = ();
  719.         my ($i, $bad);
  720.  
  721.         for $mpat (@mpat) {
  722.         if (@pos) {
  723.             for $i (@list) {
  724.             pos($i) = shift(@pos);
  725.             }
  726.         } else {
  727.             @pos = ();
  728.         }
  729.         for ($i = $bad = 0; $i < @list; $i++) {
  730.             unless ($bad[$i]) {
  731.             if (eval { $list[$i] =~ /$mpat/g }) {
  732.                 die "amatch: too long pattern.\n"
  733.                 if ($@ =~ /regexp too big/);
  734.                 $pos[$i] = pos($list[$i]);
  735.             } else {
  736.                 $bad[$i] = $bad++;
  737.                 return () if $bad == @list;
  738.             }
  739.             }
  740.         }
  741.         }
  742.         
  743.         my @got = ();
  744.  
  745.         for ($i = 0; $i < @list; $i++) {
  746.         push(@got) unless $bad[$i];
  747.         }
  748.  
  749.         return @got;
  750.     }
  751.     
  752.  
  753.     while ($mpat = shift(@mpat)) {
  754.         return () unless eval { /$mpat/g };
  755.         die "amatch: too long pattern.\n"
  756.         if ($@ =~ /regexp too big/);
  757.         return ($_) if (@mpat == 0);
  758.     }
  759.     }
  760.  
  761.     return ();
  762. }
  763.  
  764. sub _subst {
  765.     my ($sub, $pre, $match, $post) = @_;
  766.  
  767.     $sub =~ s/\$`/$pre/g;
  768.     $sub =~ s/\$&/$match/g;
  769.     $sub =~ s/\$'/$post/g;
  770.  
  771.     $sub;
  772. }
  773.  
  774. sub asubstitute {
  775.     my ($pattern, $sub, @list) = @_;
  776.     my ($aI, $aD, $aS, $rI, $rD, $rS);
  777.  
  778.     my $len = length($pattern);
  779.  
  780.     my $remods;
  781.  
  782.     if ($compat1 or ref $list[0]) {
  783.     my $mods;
  784.  
  785.     if ($compat1) {
  786.         $mods = [ @list ];
  787.         @list = ();
  788.     } else {
  789.         $mods = shift(@list);
  790.     }
  791.  
  792.     $remods = _mods($mods, \$aI, \$aD, \$aS, \$rI, \$rD, \$rS);
  793.  
  794.     ($aI, $aD, $aS) = _mids($len, $aI, $aD, $aS, $rI, $rD, $rS);
  795.     } else {
  796.     $dL[$len] = $len < 11 ? 1 : int(0.1 * $len) unless $dL[$len];
  797.     $aI = $aD = $aS = $dL[$len];
  798.     }
  799.  
  800.     die "asubstitute: \$_ is undefined: what are you matching against?\n"
  801.     if (not defined $_ and @list == 0);
  802.  
  803.     _compile($pattern, $aI, $aD, $aS)
  804.     unless defined $P{$pattern}[$aI][$aD][$aS];
  805.  
  806.     my @spat = @{$P{$pattern}[$aI][$aD][$aS]};
  807.     my $spat = $spat[0];
  808.     
  809.     $spat = '(?' . $remods . ')' . $spat if defined $remods;
  810.  
  811.     if (@list) {
  812.     my (@m, $sm, $s);
  813.  
  814.     for $sm (@list) {
  815.         eval { $s = $sm =~ s/($spat)/_subst($sub, $`, $1, $')/e };
  816.         die "asubstitute: too long pattern, maximum pattern length 19.\n"
  817.         if ($@ =~ /regexp too big/);
  818.         push(@m, $sm) if ($s);
  819.     }
  820.  
  821.     return @m;
  822.     }
  823.  
  824.     die "asubstitute: \$_ is undefined: what are you matching against?\n"
  825.     unless defined $_;
  826.  
  827.     my $s;
  828.  
  829.     eval { $s = s/($spat)/_subst($sub, $`, $1, $')/e };
  830.     die "asubstitute: too long pattern, maximum pattern length 19.\n"
  831.     if ($@ =~ /regexp too big/);
  832.     return ($_) if $s;
  833.  
  834.     ();
  835. }
  836.  
  837. 1;
  838.  
  839.