Parsing some input - easy to explain but lengthy to code - suggestions?

D

doolittle

Hi,

I have to check that some input conforms to some rules. Although the
rules are simple to expain, my perl function looks too complex/long,
can it be done more simply?

The rule is that the first character must be a letter, and the second
and third characters must form a number between 1 and 25 (so the third
character is optional)

sub isOK {
my $str = shift;

# get the first character
my $chr = lc substr $str, 0, 1;

# return false if the first character is not a letter between a and Z
return 0 if $chr !~ /[a-z]/o;

# get the second character
$chr = substr $str, 1, 1;

# return false if the second character is not
# a number between 1 and 9
return 0 if $chr !~ /[1-9]/o;

# get the third character
$chr = substr $str, 2, 1;

if ($chr) {
# return false if the third character exists, but is not a number
return 0 if $chr !~ /\d/o;

# return false if the combination of the 2nd and
# 3rd characters is over 25
return 0 if (substr $str, 1) > 25;
}

# otherwise return true
return 1
}
 
J

Jürgen Exner

doolittle said:
Hi,

I have to check that some input conforms to some rules. Although the
rules are simple to expain, my perl function looks too complex/long,
can it be done more simply?

The rule is that the first character must be a letter, and the second
and third characters must form a number between 1 and 25 (so the third
character is optional)

UNTESTED!!!

sub isOK {
my $str = shift;
if ($str =~ /^\w(\d{1,2}?)/
# check for first character being a letter,
# next 1 or 2 characters being digits and capture them
and $1 > 0 and $1 < 25 ) {
return 1;
} else {
return 0;
}

jue
 
J

John W. Krahn

doolittle said:
I have to check that some input conforms to some rules. Although the
rules are simple to expain, my perl function looks too complex/long,
can it be done more simply?

The rule is that the first character must be a letter, and the second
and third characters must form a number between 1 and 25 (so the third
character is optional)

sub isOK {
my $str = shift;

# get the first character
my $chr = lc substr $str, 0, 1;

# return false if the first character is not a letter between a and Z
return 0 if $chr !~ /[a-z]/o;

# get the second character
$chr = substr $str, 1, 1;

# return false if the second character is not
# a number between 1 and 9
return 0 if $chr !~ /[1-9]/o;

# get the third character
$chr = substr $str, 2, 1;

if ($chr) {
# return false if the third character exists, but is not a number
return 0 if $chr !~ /\d/o;

# return false if the combination of the 2nd and
# 3rd characters is over 25
return 0 if (substr $str, 1) > 25;
}

# otherwise return true
return 1
}

sub isOK { $_[ 0 ] =~ /\A[a-zA-Z](?:[1-9]|1[0-9]|2[0-5])\z/ ? 1 : 0 }




John
 
A

anno4000

Bob Walton said:
doolittle said:
Hi,

I have to check that some input conforms to some rules. Although the
rules are simple to expain, my perl function looks too complex/long,
can it be done more simply?

The rule is that the first character must be a letter, and the second
and third characters must form a number between 1 and 25 (so the third
character is optional)
Try:

use warnings;
use strict;
while(<DATA>){
chomp;
print "$_->".isOK($_)."\n";
}
sub isOK{
my $str=shift;
if($str=~/^[a-z](\d\d?)/i){
return(($1>0 and $1<26)?1:0);
}
else{return 0}
}
__END__

[data snipped]

That's still more complicated than it has to be. Neither "if {} else
{}" nor the "?:" construct are necessary:

sub is_ok {
shift =~ /^[a-z](\d\d?)/i and 1 <= $1 and $1 <= 25;
}

The only difference is that your routine returns 0 for false while
is_ok() returns Perl's boolean false which prints as an empty string.

Anno
 
D

doolittle

Bob Walton said:
doolittle said:
Hi,

I have to check that some input conforms to some rules. Although the
rules are simple to expain, my perl function looks too complex/long,
can it be done more simply?

The rule is that the first character must be a letter, and the second
and third characters must form a number between 1 and 25 (so the third
character is optional)
Try:

use warnings;
use strict;
while(<DATA>){
chomp;
print "$_->".isOK($_)."\n";
}
sub isOK{
my $str=shift;
if($str=~/^[a-z](\d\d?)/i){
return(($1>0 and $1<26)?1:0);
}
else{return 0}
}
__END__

[data snipped]

That's still more complicated than it has to be. Neither "if {} else
{}" nor the "?:" construct are necessary:

sub is_ok {
shift =~ /^[a-z](\d\d?)/i and 1 <= $1 and $1 <= 25;
}

The only difference is that your routine returns 0 for false while
is_ok() returns Perl's boolean false which prints as an empty string.

Anno

--
$anagram = 'Knuth heals rare project'; # by Abigail
push @{ $pos{ $_}}, $pos ++ for split //, lc $anagram;
print "print +(split //, '$anagram')[ $_]\n" for
join ', ', map shift @$_, @pos{ split //, lc "Just another Perl hacker"};

Thanks for the suggestions everyone.

This looks the shortest:

sub is_ok {
shift =~ /^[a-z]([1-9]\d?)$/i and 1 <= $1 and $1 <= 25;
}

I changed it slightly from your original to deal with the fact that Q01
is not allowed, nor is Q123 (no trailing characters)
 
D

doolittle

Tad said:
return 0 if $chr !~ /[a-z]/o;
return 0 if $chr !~ /[1-9]/o;
return 0 if $chr !~ /\d/o;


Why do you think you need the m//o modifier there?

(you don't, it is a no-op)

Because I grokked 'o' as something you could always append to a regexp
and it would never be bad, provided the regexp didn't contain a
variable that could change.

Incidentally, I grok grok as 'understand partially or
(in)sufficiently', but if grok means 'understand deeply' (as a little
delving suggests) do I grok grok anymore? Guess thats one of those
things you either know or you don't.
 
T

Tad McClellan

doolittle said:
Tad said:
return 0 if $chr !~ /[a-z]/o;
return 0 if $chr !~ /[1-9]/o;
return 0 if $chr !~ /\d/o;


Why do you think you need the m//o modifier there?

(you don't, it is a no-op)


It is bad netiquette to quote .sigs.

Because I grokked 'o' as something you could always append to a regexp
and it would never be bad, provided the regexp didn't contain a
variable that could change.


A modifier means "something out the ordinary here!".

That causes a pause when reading the code, to go figure out what
is different...

.... only to discover that nothing was different.

You "trick" yourself into spending more time on the code than
is necesary.

That is bad for maintenance.

You should only "use" modifiers when you "make use" of the modifications
that they trigger.

Incidentally, I grok grok as 'understand partially or
(in)sufficiently',


You do not grok grok.

but if grok means 'understand deeply' (as a little
delving suggests) do I grok grok anymore?


You didn't grok grok, but now you do grok grok.

Guess thats one of those
things you either know or you don't.


It is one of those things the you either grok or you don't.
 
A

anno4000

doolittle said:
Bob Walton said:
doolittle wrote:
Hi,

I have to check that some input conforms to some rules. Although the
rules are simple to expain, my perl function looks too complex/long,
can it be done more simply?

The rule is that the first character must be a letter, and the second
and third characters must form a number between 1 and 25 (so the third
character is optional)

Try:

use warnings;
use strict;
while(<DATA>){
chomp;
print "$_->".isOK($_)."\n";
}
sub isOK{
my $str=shift;
if($str=~/^[a-z](\d\d?)/i){
return(($1>0 and $1<26)?1:0);
}
else{return 0}
}
__END__

[data snipped]

That's still more complicated than it has to be. Neither "if {} else
{}" nor the "?:" construct are necessary:

sub is_ok {
shift =~ /^[a-z](\d\d?)/i and 1 <= $1 and $1 <= 25;
}

The only difference is that your routine returns 0 for false while
is_ok() returns Perl's boolean false which prints as an empty string.

Anno

--
$anagram = 'Knuth heals rare project'; # by Abigail
push @{ $pos{ $_}}, $pos ++ for split //, lc $anagram;
print "print +(split //, '$anagram')[ $_]\n" for
join ', ', map shift @$_, @pos{ split //, lc "Just another Perl hacker"};

Thanks for the suggestions everyone.

This looks the shortest:

sub is_ok {
shift =~ /^[a-z]([1-9]\d?)$/i and 1 <= $1 and $1 <= 25;
}

If you have the numeric tests you don't have to special-case
the first digit. Any one-or-two-digit number will be sufficiently
checked. That makes the regex clearer. Using a posix character
class instead of /[a-z]/i makes it still clearer but changes
the semantics slightly. Untested:

shift =~ /^[[:alpha:]](\d{1,2})/ and 1 <= $1 and $1 <= 25;

Anno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top