#!/usr/bin/perl
use strict;
use warnings;
my #array = qw[a b c];
foreach my($a,$b,$c) (#array) {
print "$a , $b , $c\n";
}
I receive following error:
Missing $ on loop variable
What is wrong?
I am using: perl v5.10.1 (*) built for x86_64-linux-thread-multi
To grab multiple list items per iteration, use something like List::MoreUtils::natatime
or use splice:
my #tmparray = #array; # don't trash original array
while ( my ($a,$b,$c) = splice(#tmparray,0,3) ) {
print "$a , $b , $c\n";
}
Or reorganize your data into multiple arrays and use one of the Algorithm::Loops::MapCar* functions to loop over multiple arrays at once.
I'm not aware that foreach can eat up more than one parameter at a time in Perl. I might be reading the documentation wrong.
As mentioned in the other answers, Perl does not directly support iterating over multiple values in a for loop. This is one of the issues I addressed in my module List::Gen:
use List::Gen;
my #array = qw/a b c d e f/;
for (every 3 => #array) {
print "#$_\n";
}
outputs:
a b c
d e f
Edit: The list slices that List::Gen produces are aliased to the original list, so that means you can change values in the slice to change the original list. That functionality does not seem possible with several of the other solutions posted for this question.
Block::NamedVar provides nfor that DWIM. This is more convenient than the alternative ways to iterate.
use Block::NamedVar;
my #array = qw[a b c];
nfor my($a,$b,$c) (#array) {
print "$a , $b , $c\n";
}
I think you are missing the point of foreach loop. It goes through every value in an array. It handles one value at a time. Here's the correct code:
#!/usr/bin/perl
use strict;
use warnings;
my #array = qw[a b c];
foreach my $z (#array) {
print "$z\n";
}
Related
I was wondering if it is possible to make a hash assigning its keys and values at once. Or in general use map and for at one line:
#!/usr/bin/perl
%h = map {$_, $i} qw[a b c] for $i (1..3)
But unfortunatelly not => Number found where operator expected, meant number in the parenthesis. So my question is why am I not able to make double loop by this way? And how otherwise would someone assign hash keys to values (and I dont concern something like $h = {a=>1,b=>2,c=>3} but rather assigning %h = (#keys = #values) ... in other words, how to assign hash by:
2 arrays only (#keys,#values), no scalars
At once (at one line - without block)
Is it even possible in perl?
Populating a hash is simply a matter of assigning a list with alternating keys and values, so you just have to construct the list using the two arrays in an alternating fashion.
use strict;
use warnings;
my #keys = qw(a b c);
my #values = 1..3;
my %h = map { ($keys[$_], $values[$_]) } 0..$#keys;
List::UtilsBy provides a useful abstraction for this in zip_by.
use List::UtilsBy 'zip_by';
my %h = zip_by { #_ } \#keys, \#values;
But actually it's even easier to use slice assignment. Though you technically can't do this in the same statement as the declaration, it's by far the neatest option:
my %h;
#h{#keys} = #values;
Use List::MoreUtils 'zip' or add your own since that module is not a core module:
sub zip(\##){map{($_[0][$_-1],$_[$_])}1..#{$_[0]}}
my %h = zip #keys, #values;
Well, the question is not very clear on 'why?' -- same can be achieved with following code
use strict;
use warnings;
use Data::Dumper;
my $debug = 1;
my %h;
#h{qw(a b c)} = (1..3);
print Dumper(\%h) if $debug;
I am trying to use List::MoreUtils methods. But, need some clarity on its usage it in some scenarios.
Please let me know, if it can be used with a map. For example:
#!/usr/bin/perl
use strict;
use warnings;
use List::Util;
use List::MoreUtils;
use Data::Dumper;
my #udData1 = qw(WILL SMITH TOMMY LEE JONES);
my #arr = qw(WILL TOMMY);
my %output = map{$_=>List::MoreUtils::firstidx{/$_/} #udData1} #arr;
print Dumper %output;
print List::MoreUtils::firstidx{/TOMMY/} #udData1;
print "\n";
Output:
$VAR1 = 'TOMMY';
$VAR2 = 0;
$VAR3 = 'WILL';
$VAR4 = 0;
2
As observed I am not getting the values correctly when using map, but getting it fine when used in the later command.
I intend to use $_ as an element of #arr. This may be incorrect. So, please suggest me an alternative. Shall i have to use foreach?
The problem is this bit right here:
List::MoreUtils::firstidx{/$_/} #udData1
In this bit of code, you're expecting $_ to be both the pattern taken from #arr and the string taken from #udData1 at the same time. (Remember that firstidx{/TOMMY/} means firstidx{$_ =~ /TOMMY/}, and likewise firstidx{/$_/} means firstidx{$_ =~ /$_/}.)
What actually happens is that $_ is the value from #udData1 (since that's the innermost loop) and you wind up matching that against itself. Because it's a simple alphabetic string, it always matches itself, and firstidx correctly returns 0.
Here's one solution using a temporary lexical variable:
my %output = map{ my $p = $_;
$p => List::MoreUtils::firstidx{/$p/} #udData1 } #arr;
OK, I have the following code:
use strict;
my #ar = (1, 2, 3);
foreach my $a (#ar)
{
$a = $a + 1;
}
print join ", ", #ar;
and the output?
2, 3, 4
What the heck? Why does it do that? Will this always happen? is $a not really a local variable? What where they thinking?
Perl has lots of these almost-odd syntax things which greatly simplify common tasks (like iterating over a list and changing the contents in some way), but can trip you up if you're not aware of them.
$a is aliased to the value in the array - this allows you to modify the array inside the loop. If you don't want to do that, don't modify $a.
See perldoc perlsyn:
If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.
There is nothing weird or odd about a documented language feature although I do find it odd how many people refuse check the docs upon encountering behavior they do not understand.
$a in this case is an alias to the array element. Just don't have $a = in your code and you won't modify the array. :-)
If I remember correctly, map, grep, etc. all have the same aliasing behaviour.
As others have said, this is documented.
My understanding is that the aliasing behavior of #_, for, map and grep provides a speed and memory optimization as well as providing interesting possibilities for the creative. What happens is essentially, a pass-by-reference invocation of the construct's block. This saves time and memory by avoiding unnecessary data copying.
use strict;
use warnings;
use List::MoreUtils qw(apply);
my #array = qw( cat dog horse kanagaroo );
foo(#array);
print join "\n", '', 'foo()', #array;
my #mapped = map { s/oo/ee/g } #array;
print join "\n", '', 'map-array', #array;
print join "\n", '', 'map-mapped', #mapped;
my #applied = apply { s/fee//g } #array;
print join "\n", '', 'apply-array', #array;
print join "\n", '', 'apply-applied', #applied;
sub foo {
$_ .= 'foo' for #_;
}
Note the use of List::MoreUtils apply function. It works like map but makes a copy of the topic variable, rather than using a reference. If you hate writing code like:
my #foo = map { my $f = $_; $f =~ s/foo/bar/ } #bar;
you'll love apply, which makes it into:
my #foo = apply { s/foo/bar/ } #bar;
Something to watch out for: if you pass read only values into one of these constructs that modifies its input values, you will get a "Modification of a read-only value attempted" error.
perl -e '$_++ for "o"'
the important distinction here is that when you declare a my variable in the initialization section of a for loop, it seems to share some properties of both locals and lexicals (someone with more knowledge of the internals care to clarify?)
my #src = 1 .. 10;
for my $x (#src) {
# $x is an alias to elements of #src
}
for (#src) {
my $x = $_;
# $_ is an alias but $x is not an alias
}
the interesting side effect of this is that in the first case, a sub{} defined within the for loop is a closure around whatever element of the list $x was aliased to. knowing this, it is possible (although a bit odd) to close around an aliased value which could even be a global, which I don't think is possible with any other construct.
our #global = 1 .. 10;
my #subs;
for my $x (#global) {
push #subs, sub {++$x}
}
$subs[5](); # modifies the #global array
Your $a is simply being used as an alias for each element of the list as you loop over it. It's being used in place of $_. You can tell that $a is not a local variable because it is declared outside of the block.
It's more obvious why assigning to $a changes the contents of the list if you think about it as being a stand in for $_ (which is what it is). In fact, $_ doesn't exist if you define your own iterator like that.
foreach my $a (1..10)
print $_; # error
}
If you're wondering what the point is, consider the case:
my #row = (1..10);
my #col = (1..10);
foreach (#row){
print $_;
foreach(#col){
print $_;
}
}
In this case it is more readable to provide a friendlier name for $_
foreach my $x (#row){
print $x;
foreach my $y (#col){
print $y;
}
}
Try
foreach my $a (#_ = #ar)
now modifying $a does not modify #ar.
Works for me on v5.20.2
I have a list of possible values:
#a = qw(foo bar baz);
How do I check in a concise way that a value $val is present or absent in #a?
An obvious implementation is to loop over the list, but I am sure TMTOWTDI.
Thanks to all who answered! The three answers I would like to highlight are:
The accepted answer - the most "built-in" and backward-compatible way.
RET's answer is the cleanest, but only good for Perl 5.10 and later.
draegtun's answer is (possibly) a bit faster, but requires using an additional module. I do not like adding dependencies if I can avoid them, and in this case do not need the performance difference, but if you have a 1,000,000-element list you might want to give this answer a try.
If you have perl 5.10, use the smart-match operator ~~
print "Exist\n" if $var ~~ #array;
It's almost magic.
Perl's bulit in grep() function is designed to do this.
#matches = grep( /^MyItem$/, #someArray );
or you can insert any expression into the matcher
#matches = grep( $_ == $val, #a );
This is answered in perlfaq4's answer to "How can I tell whether a certain element is contained in a list or array?".
To search the perlfaq, you could search through the list of all questions in perlfaq using your favorite browser.
From the command line, you can use the -q switch to perldoc to search for keywords. You would have found your answer by searching for "list":
perldoc -q list
(portions of this answer contributed by Anno Siegel and brian d foy)
Hearing the word "in" is an indication that you probably should have used a hash, not a list or array, to store your data. Hashes are designed to answer this question quickly and efficiently. Arrays aren't.
That being said, there are several ways to approach this. In Perl 5.10 and later, you can use the smart match operator to check that an item is contained in an array or a hash:
use 5.010;
if( $item ~~ #array )
{
say "The array contains $item"
}
if( $item ~~ %hash )
{
say "The hash contains $item"
}
With earlier versions of Perl, you have to do a bit more work. If you are going to make this query many times over arbitrary string values, the fastest way is probably to invert the original array and maintain a hash whose keys are the first array's values:
#blues = qw/azure cerulean teal turquoise lapis-lazuli/;
%is_blue = ();
for (#blues) { $is_blue{$_} = 1 }
Now you can check whether $is_blue{$some_color}. It might have been a good idea to keep the blues all in a hash in the first place.
If the values are all small integers, you could use a simple indexed array. This kind of an array will take up less space:
#primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
#is_tiny_prime = ();
for (#primes) { $is_tiny_prime[$_] = 1 }
# or simply #istiny_prime[#primes] = (1) x #primes;
Now you check whether $is_tiny_prime[$some_number].
If the values in question are integers instead of strings, you can save quite a lot of space by using bit strings instead:
#articles = ( 1..10, 150..2000, 2017 );
undef $read;
for (#articles) { vec($read,$_,1) = 1 }
Now check whether vec($read,$n,1) is true for some $n.
These methods guarantee fast individual tests but require a re-organization of the original list or array. They only pay off if you have to test multiple values against the same array.
If you are testing only once, the standard module List::Util exports the function first for this purpose. It works by stopping once it finds the element. It's written in C for speed, and its Perl equivalent looks like this subroutine:
sub first (&#) {
my $code = shift;
foreach (#_) {
return $_ if &{$code}();
}
undef;
}
If speed is of little concern, the common idiom uses grep in scalar context (which returns the number of items that passed its condition) to traverse the entire list. This does have the benefit of telling you how many matches it found, though.
my $is_there = grep $_ eq $whatever, #array;
If you want to actually extract the matching elements, simply use grep in list context.
my #matches = grep $_ eq $whatever, #array;
Use the first function from List::Util which comes as standard with Perl....
use List::Util qw/first/;
my #a = qw(foo bar baz);
if ( first { $_ eq 'bar' } #a ) { say "Found bar!" }
NB. first returns the first element it finds and so doesn't have to iterate through the complete list (which is what grep will do).
One possible approach is to use List::MoreUtils 'any' function.
use List::MoreUtils qw/any/;
my #array = qw(foo bar baz);
print "Exist\n" if any {($_ eq "foo")} #array;
Update: corrected based on zoul's comment.
Interesting solution, especially for repeated searching:
my %hash;
map { $hash{$_}++ } #a;
print $hash{$val};
$ perl -e '#a = qw(foo bar baz);$val="bar";
if (grep{$_ eq $val} #a) {
print "found"
} else {
print "not found"
}'
found
$val='baq';
not found
If you don't like unnecessary dependency, implement any or first yourself
sub first (&#) {
my $code = shift;
$code->() and return $_ foreach #_;
undef
}
sub any (&#) {
my $code = shift;
$code->() and return 1 foreach #_;
undef
}
I am new to programming and hence I am stuck on a basic level problem.
Following is code I wrote for comparison. But the result I get does not make sense to me. I would appreciate if someone could tell me what is going wrong.
There are two arrays: #array1 , #array2 of unequal length.
I wish to compare both and list down values not present in #array1.
my %temp = map {$_,$_}#array2;
for (#array1){
next if exists $temp{$_};
open (FILE, ">>/filename") or die "$!";
print FILE "$_\n";
close(FILE);
}
See the FAQ How do I compute the difference of two arrays? How do I compute the intersection of two arrays?
Adapting the code you posted:
#!/usr/bin/perl
use strict; use warnings;
my #x = 1 .. 10;
my #y = grep { $_ % 2 } #x;
my %lookup = map { $_ => undef } #y;
for my $x ( #x ) {
next if exists $lookup{$x};
print "$x\n";
}
If you're doing this for a test, which I assume you are I would highly suggest is_deeply in the newer versions of Test::More
You'll have to update Test::More
cpanp install Test::More
or if you're on perl 5.5
cpan Test::More
Then you'll have use it
use Test::More;
tests => 1
is_deeply ( \#arr1, \#arr2, 'test failed' );
If you're not doing this for testing, but you're doing this for introspective purposes and the arrays are small, I'd suggest using XXX:
cpanp install http://search.cpan.org/CPAN/authors/id/I/IN/INGY/XXX-0.12.tar.gz
Then you'll have use it
use XXX;
YYY [ \#arr1, \#arr2 ];
That's some pretty clever code you've got there. Your code is more or less identical to what the Perl FAQ says. I might be tempted to do this, however:
my %tmp = map { $_ => 1 } #array2;
my #diff = grep { not exists $tmp{$_} } #array1;
This gets everything in #array1 that's not in #array2, but avoiding all of those out-of-style looping constructs (yay for functional programming). Though what I'd really do is this:
sub comp (\#\#) {
my %t = map { $_ => 1 } #{$_[1]};
return grep { not exists $t{$_} } #{$_[0]};
}
Then you can just do:
my #diff = comp(#array1, #array2); # get items in #array1 not in #array2
#diff = comp(#arraty2, #array1); # vice versa
Or you can go to CPAN. List::Compare::Functional::complement() does what you want, though the syntax is reversed.
Swap #array1 and #array2 in your code?
For simple values like strings or numbers, the following should work
my #result;
my $hosts = [qw(host1 host2 host3 host4 host5)];
my $stie_obj = [qw(host1 host5 host6)];
#result = map { my $a=$_; my $b=grep {/$a/} #$site_obj; $b==0 ? $a : () } #$hosts;
print Dumper (#result);
Should give :
$VAR1 = 'host2';
$VAR2 = 'host3';
$VAR3 = 'host4';