PetaMem has asked for the wisdom of the Perl Monks concerning the following question:

Greetings wise monks.

Again, I've stumbled about a "make it more elegant?"-problem: Actually this time it is more a "keep it generic?" type of a problem.

I have a list of strings (words/phrases.) With reverse and a little looping, it is simple to find those, that are symmetric, i.e. spelled forward and backward alike.

The problem is now, that I use a generic regexp-search within this wordlist to find special entries fitting - well - what an regexp can fit.

It would be cool to reuse this regexp search for the forward/backward test, but I don't know how resp. if this is even possible.

My guess is, that it is not possible with an regexp for words of arbitrary length, as this problem seems to be quite similar to that of balanced parentheses. But then again I might be wrong, as this could be a special case.

Enlightenment anyone?

Bye
 PetaMem
    All Perl:   MT, NLP, NLU

Replies are listed 'Best First'.
Re: Reverse a Word - without reverse??
by Juerd (Abbot) on Mar 07, 2004 at 12:42 UTC

    It would be cool to reuse this regexp search for the forward/backward test, but I don't know how resp. if this is even possible.

    If you drop your "without reverse" requirement, you can use it in the regex!

    /(^.*\z)(?(?{ $+ eq reverse $+ })|(?!))/s
    Or, in a more verbose format:
    / ( # capture group ^ # match begin of string .* # match everything \z # match end of string ) (? # if (?{ $+ eq reverse $+ }) # $+ (last captured group) is palindr +ome # then match nothing (succeed) | # else (?!) # fail on matching nothing (fail the +regex) ) /xs # x for readability, s to make . matc +h \n # Note: can match empty string
    Depending on what you want to match and whether you want backtracking, change the contents of the capture group.

    I think I prefer Perl 6 regexes here :)

    rule palindrome { $foo := (\w+) ::: { fail if $foo ne reverse $foo } } # Note: doesn't match empty string
    (assuming a \w+ word and no backtracking)

    Or a simpler version, but this always backtracks:

    /(.+)(??{ reverse $+ })/s
    Explained:
    / (.+) # match one or more characters (??{ quotemeta reverse $+ }) # match the reverse of what was matc +hed /xs # Note: doesn't match empty string or single character string
    A Perl 6 version:
    rule palindrome { $foo := (\w+) <{ quotemeta reverse $foo }> }

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

Re: Reverse a Word - without reverse??
by matija (Priest) on Mar 07, 2004 at 11:44 UTC
    I am convinced that matching parens and finding a word that is the reverse are equivalent problems. (With the word finding possibly even more difficult, because each character could act as opening as well as closing brace).

    As we aparently both know, the regular expressions (as defined by computer science) can't verify an arbitrary set of parens. You need a grammar for that.

    However, Perl regular expressions are more powerfull than the conventional CS definition of RE, and they can match an arbitrary length string of parens.

    Take a look at Regexp::Common::balanced . I didn't look at the code, but from the docs it appears to be what you need.

    Now all you need to do is change the regexp so that it will work with different kinds of parens (instead of just one opening and one closing), append each candidate word to the pattern word, and see if the match returns true. Piece of cake :-)

    Except that you transformed a O(n) search into an O(n^2) search. Which is not a good thing, unless you get a percentage from hardware sales :-)

Re: Reverse a Word - without reverse??
by ambrus (Abbot) on Mar 07, 2004 at 16:51 UTC

    It is true that the set of palindromes can not be matched with a regexp.

    However, with Perl's extended regexps, it is possible. Indeed, this is in the Regexp::Common module.

    perl -wne 'use Regexp::Common; /^$RE{lingua}{palindrome}$/ and print " +palindrome\n";'

    Update: beware, the above snippet only matches lines consisting of only letters.

Re: Reverse a Word - without reverse??
by Popcorn Dave (Abbot) on Mar 07, 2004 at 20:57 UTC
    I'm not sure you even need a regex to do this. What about using unshift to reverse your string?

    use strict; my $string1 = "abcba"; my $string2 = "abc1da"; if (&reverser($string1)){ print "Matched! Strings are palindromes.\n"; } else{ print "Sorry, the strings are not palindromes.\n"; } if (&reverser($string2)){ print "Matched! Strings are palindromes.\n"; } else{ print "Sorry, the strings are not palindromes.\n"; } sub reverser{ my $temp = shift; my ($reverse, @temp, @temp2); @temp = split ('',$temp); foreach (@temp){ unshift(@temp2, $_); } $reverse = join('', @temp2); $temp eq $reverse?1:0; }

    Should do what you're requiring without using the regex. You're just running the array you've made from your string and reversing it using the unshift.

    There is no emoticon for what I'm feeling now.

      There is an Iterator that gets regexps as arguments.

      Bye
       PetaMem
          All Perl:   MT, NLP, NLU

Re: Reverse a Word - without reverse??
by hv (Prior) on Mar 09, 2004 at 17:24 UTC

    This can be done quite straightforwardly with a recursive regexp:

    our $pal = qr{ .? | (.) (??{$pal}) \1 }x; sub is_palindrome { shift =~ /^$pal\z/; }

    Don't expect it to be particularly efficient. :)

    Hugo