简体   繁体   中英

How do I check a palindrome in Perl?

I am trying to write a palindrome checker in Perl. It's supposed to return "true" if the input is the same forwards and backwards.

Example:

./determine kayak
True
./determine "Do geese see God?"
True
./determine "Do ducks see God?"
False

My code:

#!/usr/bin/perl -w 

$name = $ARGV[0];
$name = lc $name;
$name =~ s/[^a-z]//g;
@all = split //,$name;
$half = $#all/2;

foreach $i (0..$half){
    my $count = 0;
    if($all[$i] eq $all[-($i+1)]){
        #print"$all[$i]\n";
        #print"$all[-($i+1)]\n";
        print "true\n";
        last;       
    }else{
        print "flase\n";
    }

}

It can't determine true or false after read all.

For example: when read: "Do ducks see God?" , the first two letter is same, so it will print true, but this should be false.

Your code only checks the first letter and then stops the loop if they are the same. You've correctly identified that problem. It's because of the last .

foreach $i (0..$half){
    my $count = 0;
    if($all[$i] eq $all[-($i+1)]){
        print "true\n";
        last;                       # <-- here
    }else{
        print "flase\n";
    }
}

This will make Perl exit the loop. What you want to do instead is keep checking every letter. Once you've reached the middle, your result is true because the string is a palindrome . But as soon as you see a mismatch, you have to fail and exit.

You do that by keeping a status in a variable, and checking afterwards. I've added strict and warnings and made the code a bit shorter.

my $name = lc 'Do ducks see God?';
$name =~ s/[^a-z]//g;
my @all = split //, $name;
my $half = $#all / 2;

my $is_palindrome = 1;
foreach my $i ( 0 .. $half ) {
    my $count = 0;
    if ( $all[$i] ne $all[ -( $i + 1 ) ] ) {
        $is_palindrome = 0;
        last;
    }
}

if ($is_palindrome) {
    print "true\n";
} else {
    print "false\n";
}

It's easier to use a ne check , because having an empty block after the if like this is confusing.

if ( 1 == 0 ) {
} else {
    # do stuff
}

It's easier to turn the condition around and get rid of the block, or use unless , which is the same as if not in Perl.

if ( 1 != 0 ) {
    # do stuff
}

unless ( 1 == 0 ) {
    # do stuff
} 

Note that there is a way shorter implementation: You can compare the string with its reverse . The reverse built-in , when called in scalar context, turns the string around.

my $name = lc '12321';
$name =~ s/[^a-z]//g;

if ($name eq reverse $name) {
    print "true\n";
} else {
    print "false\n";
}

Currently, you print "true" or "false" for every iteration of your array of characters. You really want to check each letter and only print the result at the end.

my $is_palindrome = 1;

foreach $i (0..$half){
    if ($all[$i] ne $all[-($i+1)]){
        $is_palindrome = 0;
        last;
    }
}

say $is_palindrome ? 'true' : 'false';

But, personally, I think it's easier just to check your $name variable is the same as its reverse.

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my $name = $ARGV[0];
$name = lc $name;
$name =~ s/[^a-z]//g;

say $name eq reverse($name) ? 'true' : 'false';

Note: Please use use warnings instead of -w and also use strict .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM