1

Hi Stack overfloweans,

I am finding a value in an array which is a constant allowed_value. If the main_value equals any of the values in allowed_value array, then I am printing the message 'Allowed value is correct'. So I have done the following piece of code.

use constant allowed_value => qw(value1 value2);

my $main_value = 'value2';

my @attr = (allowed_value);

print "Allowed value is correct" if grep $_ eq $main_value, @attr; 

Is there any way to make the code better and simplify the code ? Please help. Thanks in advance

2
  • 1
    You could also put the allowed values in a hash, then you would avoid the grep call (which can be slower than a hash lookup). For example: print "Allowed value is correct\n" if $attr{$main_value} Commented Jul 7, 2019 at 18:53
  • 1
    Can also use List::Util::any -- say "good" if any { $_ eq $main_value } (allowed_value); Commented Jul 7, 2019 at 18:56

2 Answers 2

4

grep is fine for small sets, but it has to search the whole set every time.

For larger sets you can do this a bit faster using List::Util's any() function. This has the advantage over grep that it will stop searching when it matches once. Assuming random data, on average you'll search half the list.

use strict;
use warnings;
use v5.10;
use List::Util 'any';

use constant allowed_values => qw(value1 value2);
my $main_value = 'value3';

say "Good" if any { $_ eq $main_value } allowed_values;

However, that still searching through even part of allowed_values every time. This is inconsequential for small sets of allowed values, but when it gets large it can get slow.

Instead use a hash as a set. It's a bit more work to set up, but it's more convenient and will perform the same no matter how large the set.

use strict;
use warnings;
use v5.10;

use constant allowed_values => {
    value1 => 1,  # any true value will do
    value2 => 1,
};
my $main_value = 'value2';

say "Good" if allowed_values->{$main_value};

And if your list of allowed values is long, you can generate the hash from a list avoiding a bunch of typing.

use constant allowed_values => {
    map { $_ => 1 }
    qw(value1 value2)
};

The downside is "constant" references aren't really constant. Only the reference is constant, the contents of that reference can be changed.

allowed_values->{foo} = 42;  # this is "fine"

If that's a concern, use Const::Fast instead.

use strict;
use warnings;
use v5.10;

use Const::Fast qw(const);
const my %allowed_values => (
    map { $_ => 1 }
    qw(value1 value2)
);
my $main_value = 'value2';

say "Good" if $allowed_values{$main_value};

Unless you really need an inlined constant, and generally you don't.

Sign up to request clarification or add additional context in comments.

3 Comments

Might be nice to include a benchmark showing the two versions of linear growth (grep vs any) using an "average case" data set.
@Daxim Thank you for the info about Const::Fast, I'll recommend it over Readonly in the future; it's certainly less awkward. However, materially changing other's answers in an edit is inappropriate. In the future, leave a comment or write a supplemental answer.
@DavidO It did occur to me that any might have a constant cost higher than grep such that grep wins at smaller arrays (though both are written in C, so probably not). And the benchmark would be interesting (well volunteered). Practically, skip all that and use a hash.
0

Depending on how large your file is, i would use if (exists) and map the array into a hash. Once wrote a programm with a lot of greps, took ours to run through 400k lines, when i replaced most of the greps, came down to under 20 seconds.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.