Here is the Perl 6 solution. I use a grammar that knows how to capture four interesting characters, despite interstitial things. More complex requirements require a different grammar, but it is not so difficult.
At each match, an object of the NString :: Actions class gets a change to check for compliance. He does the same as before. This seems like a lot of work, and for this trivial example. For more complex examples, this will not be much worse. My version of Perl 5 would have to do many tools to understand what to save or not to save.
use Text::Levenshtein; my $string = 'The quixotic purple and jasmine butterfly flew over the quick zany dog'; grammar NString { regex n-chars { [<.ignore-chars>* \w]**4 } regex ignore-chars { \s } } class NString::Actions { # See my subset IntInf where Int:D | Inf; has $.target; has Str $.closest is rw = ''; has IntInf $.closest-distance is rw = Inf; method n-chars ($/) { my $string = $/.subst: /\s+/, '', :g; my $distance = distance( $string, self.target ); # say "Matched <$/>. Distance for $string is $distance"; if $distance < self.closest-distance { self.closest = $string; self.closest-distance = $distance; } } } my $action = NString::Actions.new: target => 'Perl'; loop { state $from = 0; my $match = NString.subparse( $string, :rule('n-chars'), :actions($action), :c($from) ); last unless ?$match; $from++; } say "Shortest is { $action.closest } with { $action.closest-distance }";
(I made a direct port from Perl 5, which I will leave here)
I tried the same thing in Perl 6, but I'm sure this is a bit verbose. I was wondering if there is a smart way to capture groups of N characters for comparison. Maybe I will have some improvement later.
use Text::Levenshtein; put edit( "four", "foar" ); put edit( "four", "noise fo or blur" ); sub edit ( Str:D $start, Str:D $target --> Int:D ) { my $target-modified = $target.subst: rx/\s+/, '', :g; my $last-position-to-check = [-] map { .chars }, $target-modified, $start; my $closest = Any; my $closest-distance = $start.chars + 1; for 0..$last-position-to-check -> $starting-pos { my $substr = $target-modified.substr: $starting-pos, $start.chars; my $this-distance = distance( $start, $substr ); put "So far: $substr -> $this-distance"; if $this-distance < $closest-distance { $closest = $substr; $closest-distance = $this-distance; } last if $this-distance = 0; } return $closest-distance // -1; }
source share