homepage Welcome to WebmasterWorld Guest from 54.161.247.22
register, free tools, login, search, pro membership, help, library, announcements, recent posts, open posts,
Become a Pro Member

Home / Forums Index / Code, Content, and Presentation / Perl Server Side CGI Scripting
Forum Library, Charter, Moderators: coopster & jatar k & phranque

Perl Server Side CGI Scripting Forum

    
IF x=this OR x=that OR x=other.
Is there a way to shorten this?
StaceyJ




msg:4264343
 8:02 pm on Feb 8, 2011 (gmt 0)

I have quite a few IF...OR statements like this, some of which can go out to 10 or so ORs

if (($x == 1)||($x == 3)||($x == 6)||($x == 7)||($x == 12||($x == 13)||($x == 18)) {
Do this
}

Is there a better, more compact/efficient way to write these?

 

Demaestro




msg:4264348
 8:18 pm on Feb 8, 2011 (gmt 0)

Not sure about the perl syntax but
Something like this:

if x in (1, 3, 6, 7, 12,13, 18) {
Do this
}

or something like this:

conditions_array = [1, 3, 6, 7, 12,13, 18]

if x in conditions_array {
Do this
}

janharders




msg:4264368
 9:08 pm on Feb 8, 2011 (gmt 0)

my $x = 3;
if( grep { $x == $_ } (1, 3, 6, 7, 12, 13, 18) ) {
print "found it";
}


is probably less efficient but much easier to read. It's basically what Demaestro suggested, converted to perl.

bodine




msg:4264391
 9:54 pm on Feb 8, 2011 (gmt 0)

Depending on how often the numbers will change, you may want to put that at the top of your code in an array:

my $x = 3;
my @numbers = (1, 3, 6, 7, 12, 13, 18);

foreach my $number (@numbers)
{
if ($x == $number)
{
print "Found it!\n";
}

}

rocknbil




msg:4264779
 6:14 pm on Feb 9, 2011 (gmt 0)

One would have to wonder **why** you have to hard code a list of numbers like that, is there a better way to do what you're asking? A perfect example is applying that to say, document ID's in a CMS for a menu. "If it's in this list, put it in the main navigation." In that scenario, you could add a link attribute to the main nav links as the trigger, or an "include in menu" field (and checkbox.) This takes the hard coding out of your program and puts it where it belongs, in the hands of the user as a configuration setting.

You could apply that to anything that would require conditions based on these numbers, just a thought. :-)

g1smd




msg:4264786
 6:22 pm on Feb 9, 2011 (gmt 0)

Look at either arrays or regular expressions.

$var = array(
1 => true;
3 => true;
7 => true
)


if $var[$x] == true

needs just one compare operation.


Alternatively,

$var = '/^(1[238]?|3|6|7)$/';

if preg_match($var, $x) == true

is quite compact and efficient code, and quick to run if the list is short.

The examples above are using PHP syntax, and will have to be adapted to the PERL way of doing things.

[edited by: g1smd at 6:28 pm (utc) on Feb 9, 2011]

StaceyJ




msg:4264787
 6:23 pm on Feb 9, 2011 (gmt 0)

Thank you all for your suggestions! You gave me several ideas and things to think about. I really appreciate the input!

janharders




msg:4264804
 7:04 pm on Feb 9, 2011 (gmt 0)

just a quick note on efficiency (and please, correct me if my benchmark stinks):


use strict;
use Benchmark qw(:all);

timethese(10000000, {
'grep' => sub { my $x = 3; my $t = 1 if( grep { $x == $_ } (1, 3, 6, 7, 12,13, 18) ); },
'ifwithor' => sub { my $x = 3; my $t; $t = 1 if( ($x == 1)||($x == 3)||($x == 6)||($x == 7)||($x == 12)||($x == 13)||($x == 18) ); },
'array' => sub { my $x = 3; my @numbers = (1, 3, 6, 7, 12, 13, 18); my $t; foreach my $number (@numbers) { $t = 1 if ($x == $number); } },
'regexp' => sub { my $x = 3; my $t; $t = 1 if($x =~ m/^(1[238]?|3|6|7)$/) },
'hash' => sub { my $x = 3; my %numbers = (1 => 1, 3 => 1, 6 => 1, 7 => 1, 12 => 1, 13 => 1, 18 => 1); my $t; $t = 1 if ($numbers{ $x });},

});



perl test.pl
Benchmark: timing 10000000 iterations of array, grep, hash, ifwithor, regexp...
array: 38 wallclock secs (37.02 usr + 0.00 sys = 37.02 CPU) @ 270153.45/s (n=10000000)
grep: 18 wallclock secs (16.61 usr + 0.00 sys = 16.61 CPU) @ 602083.21/s (n=10000000)
hash: 51 wallclock secs (49.63 usr + 0.00 sys = 49.63 CPU) @ 201511.34/s (n=10000000)
ifwithor: 6 wallclock secs ( 5.37 usr + 0.00 sys = 5.37 CPU) @ 1860465.12/s (n=10000000)
regexp: 14 wallclock secs (13.14 usr + 0.00 sys = 13.14 CPU) @ 761035.01/s (n=10000000)


I'm suprised at the regexp-speed. Could there be internal caching involved? Also: hash-lookup seems to be very expensive.

bodine




msg:4264898
 11:21 pm on Feb 9, 2011 (gmt 0)

janharders,

Interesting indeed. I think the results make sense overall (if real world performance will be any different is another matter). Creating a hash is expensive and creating an array is less so but still more than not creating one.

When perl has to walk an entire array to find what it needs (as with foreach and grep), it's slower. We can see this when it walks the array with a do..until block (note: not good code, demonstration purposes only):


'do_later' => sub
{
my $x = 18;
my $t;
my @numbers = (1, 3, 6, 7, 12, 13, 18);

my $i = -1;
do
{
$i++;
$t = 1;
} until ($x == $numbers[$i] || $i >= scalar @numbers);
},

'do_early' => sub
{
my $x = 3;
my $t;
my @numbers = (1, 3, 6, 7, 12, 13, 18);

my $i = -1;
do
{
$i++;
$t = 1;
} until ($x == $numbers[$i] || $i >= scalar @numbers);
},

'do_never' => sub
{
my $x = 20;
my $t;
my @numbers = (1, 3, 6, 7, 12, 13, 18);

my $i = -1;
do
{
$i++;
$t = 1;
} until ($x == $numbers[$i] || $i >= scalar @numbers);
},


do_later: 27 wallclock secs (26.57 usr + 0.03 sys = 26.60 CPU) @ 375939.85/s (n=10000000)
do_early: 13 wallclock secs (13.68 usr + 0.02 sys = 13.70 CPU) @ 729927.01/s (n=10000000)
do_never: 30 wallclock secs (30.01 usr + 0.07 sys = 30.08 CPU) @ 332446.81/s (n=10000000)


Thus, when perl finds what it needs early on and stops, it doesn't have to continue and it therefore takes less time. I found a similar slowdown when the matched number was later with the 'ifwithfor', i.e., $x = 18. (Tho, ifwithfor is still the fastest and finding $x later took only one second more.)

And I do think the one problem with the benchmark is what you mentioned--that Perl is compiling the regex once and is storing it so it doesn't have to create it 10000000 times; Larry Wall, et al did a great job of optimizing stuff like that (kudos!).

So, if we time the creation of the regexp each time, we (well, at least I do) get a slowdown:


'precompile_regex' => sub
{
my $x = 18;
my $t;

my $num_regex = qr(^(1[238]?|3|6|7)$);
if($x =~ m/$num_regex/)
{
$t = 1;
}
},


precompile_regex: 23 wallclock secs (22.32 usr + 0.06 sys = 22.38 CPU) @ 446827.52/s (n=10000000)
regex: 9 wallclock secs ( 7.85 usr + 0.01 sys = 7.86 CPU) @ 1272264.63/s (n=10000000)

(For fun, I also did a for loop, but it was slower. No surprise.)

So, if we could get those pesky humans out of the way and not need to write easy-to-read, maintainable code, you could use the ifwithfor option. :-P

StaceyJ




msg:4265259
 6:10 pm on Feb 10, 2011 (gmt 0)

Wow, that's more than I ever could have asked for. Thank you for taking the time to do all that! I've never even heard of ifwithor, but it certainly looks the fastest. I don't care too much about readability since I'm just hacking a script and I write good comments for myself so I know what I did. So how could I work that into my code? I'm not really sure where to start, and I know it's not proper to ask for code here, but can someone get me started? Thank you again!

janharders




msg:4265278
 6:29 pm on Feb 10, 2011 (gmt 0)

bodine: thanks for improving and explaining.

StaceyJ: "ifwithor" is just a name in the benchmark used for the code you originally posted. You already came up with the best version, improvements would just be readability / maintainability.

StaceyJ




msg:4265292
 6:42 pm on Feb 10, 2011 (gmt 0)

@janharders: Very cool, thanks! :) I didn't know that's technically what it was called, no wonder I had such a hard time finding an example of what I knew I wanted to do but didn't know how to word it in a search. You guys are awesome!

janharders




msg:4265297
 6:45 pm on Feb 10, 2011 (gmt 0)

You're welcome. And just to clarify, "ifwithor" is not a technical term that I'm aware of, I just needed a name to know, what was benchmarked at what speed. Since it was a simple if with logical ORs, I went with ifwithor.
Obviously, you didn't need an example, you instinctivly chose the fastest solution.

StaceyJ




msg:4265327
 7:35 pm on Feb 10, 2011 (gmt 0)

Lol, it was a lucky guess, not instinct, but thanks just the same. :) I remembered I had to do an IF OR like this once before and knew there was something about it that made it seem to me longer than it needed to be. Something like if ($x == 1 || 3 || 6 || 7 || 12) would be nice. But I digress.

And after you named it ifwithor (which makes sense) I did a Google search and came up with a lot more results than when I was originally trying to search, so you're on to something. :)

Thanks again! I love learning.

Global Options:
 top home search open messages active posts  
 

Home / Forums Index / Code, Content, and Presentation / Perl Server Side CGI Scripting
rss feed

All trademarks and copyrights held by respective owners. Member comments are owned by the poster.
Home ¦ Free Tools ¦ Terms of Service ¦ Privacy Policy ¦ Report Problem ¦ About ¦ Library ¦ Newsletter
WebmasterWorld is a Developer Shed Community owned by Jim Boykin.
© Webmaster World 1996-2014 all rights reserved