Baz has asked for the wisdom of the Perl Monks concerning the following question:
I have a list of numbers -
10,11,12,13,14,15,17,18,22,23,24,25,26,27,28,29,30,31,32,33,34,35
and a varible $x which stores an arbitary value
I want an if loop which is true if $x matches a value in my list. In other words, if $x is 10 then the if loop is executed, if its 5 then it is not.
whats the simplest way of doing this?
The simplest way
by TheDamian (Vicar) on Apr 13, 2003 at 00:25 UTC
|
Previous respondents have suggested efficient ways for various circumstances. As for the simplest way:
use Quantum::Superpositions;
if ($x == any(@list)) {
...
}
Note that the latest release of Q::S (just uploaded to the CPAN last week) is iterator-based and therefore much more efficient that previous versions. We have Steven Lembark to thank for that.
Note too that, whilst the performance of the grep- and hash-based solutions may improve slightly over time, the any solution will continue to improve significantly with future releases of Quantum::Superpositions, then with the new Perl6::Junctions module, and ultimately with the availability of any as a built-in feature of Perl 6. | [reply] [d/l] |
Re: Making a match
by DrManhattan (Chaplain) on Apr 12, 2003 at 13:30 UTC
|
If you're checking $x aginst your list multiple times, it might be more efficient to use a hash:
my @list = (10,11,12,13,14,15,17,18,22,23,24,25,26,27,28,29,30,31,32,3
+3,34,35 );
my %hash;
@hash{@list} = (1) x @list;
# This solution only makes sense if you're
# making the comparison multiple times
foreach my $x (1..1000)
{
if ($hash{$x})
{
print "$x is in the list\n";
}
}
You spend some initial setup time creating the hash, but then all your comparisons are O(1).
-Matt | [reply] [d/l] |
|
my %hash;
@hash{@list} = (); # Don't bother filling in values
foreach my $x (1..1000)
{
if (exists $hash{$x}) # Just test for existence of key
{
print "$x is in the list\n";
}
}
| [reply] [d/l] |
|
++ to you and perlplexer. I hadn't realized you could do it that way. Benchmark.pm shows defining the hash is over twice as fast as assigning values to it. The benchmark results on the comparison techniques are more interesting:
if ($hash{$x} == 1)
{
# always slower than if($hash{$x})
# or if(defined($hash{$x}))
}
if ($hash{$x})
{
# slower than if(defined($hash{$x}))
# when $hash{$x} is undefined
# faster than if(defined($hash{$x}))
# when $hash{$x} is defined
}
Here's the benchmark code I used:
#!/usr/bin/perl
use strict;
use Benchmark;
my @list = (10,11,12,13,14,15,17,18,22,23,24,25,26,27,28,29,30,31,32,3
+3,34,35 );
my %hash;
@hash{@list} = (1) x @list;
timethese(1000000, {
'Hash Defined Miss' => sub {
if (defined($hash{1})) {
#nop
}
},
'Hash Defined Hit' => sub {
if (defined($hash{10})) {
#nop
}
},
'Hash Assigned Miss Truth' => sub {
if ($hash{1}) {
#nop
}
},
'Hash Assigned Miss Equality' => sub {
if ($hash{1} == 1) {
#nop
}
},
'Hash Assigned Hit Truth' => sub {
if ($hash{10}) {
#nop
}
},
'Hash Assigned Hit Equality' => sub {
if ($hash{10} == 1) {
#nop
}
}
});
Benchmark: timing 1000000 iterations of Hash Assigned Hit Equality, Ha
+sh Assigned Hit Truth,
Hash Assigned Miss Equality, Hash Assigned Miss Truth, Hash Define
+d Hit, Hash Defined Miss...
Hash Assigned Hit Equality: 5 wallclock secs ( 1.52 usr + 0.00 sys =
+ 1.52 CPU) @ 657894.74/s (n=1000000)
Hash Assigned Hit Truth: 3 wallclock secs ( 1.29 usr + -0.01 sys = 1
+.28 CPU) @ 781250.00/s (n=1000000)
Hash Assigned Miss Equality: 3 wallclock secs ( 1.30 usr + 0.00 sys
+= 1.30 CPU) @ 769230.77/s (n=1000000)
Hash Assigned Miss Truth: 1 wallclock secs ( 1.15 usr + 0.01 sys =
+1.16 CPU) @ 862068.97/s (n=1000000)
Hash Defined Hit: 1 wallclock secs ( 1.41 usr + 0.00 sys = 1.41 CPU
+) @ 709219.86/s (n=1000000)
Hash Defined Miss: 2 wallclock secs ( 0.95 usr + 0.00 sys = 0.95 CP
+U) @ 1052631.58/s (n=1000000)
-Matt | [reply] [d/l] [select] |
|
|
Re: Making a match
by perlplexer (Hermit) on Apr 12, 2003 at 13:35 UTC
|
How often does this need to be executed? If you only need to perform the check once, use grep(), as the Anonymous Monk already suggested.
If, however, the check needs to be performed multiple times, say, in a loop, you'll be better off putting those numbers in a hash and then using exists(); e.g.,
my %numbers = ();
@numbers{10..15,17,18,22..35} = ();
if (exists $numbers{$x}){
# do stuff
}
--perlplexer | [reply] [d/l] |
Re: Making a match
by Anonymous Monk on Apr 12, 2003 at 12:52 UTC
|
my @list = (10,11,12,13,14,15,17,18,22,23,24,25,26,27,28,29,30,31,32,33,34,35);
if (grep $_ == $x, @list) {
print "Found $x\n";
}
| [reply] |
Re: Making a match
by Anonymous Monk on Apr 13, 2003 at 12:01 UTC
|
#!/usr/bin/perl
my $x = 14;
my @ary=(10,11,12,13,14);
map{print "$x matches\n" if /^$x$/} @ary;
is nice to look at :) | [reply] |
|
|