How can I use a named iterator with postfix foreach? - perl

#values = (1..5);
foreach $s(#values){
print "$s\n"; #It works
}
print "$_\n",foreach (#values); #It also works
print "$s\n",foreach $s(#values); #It not works
How to give the variable name?
Above the code does not print. It show the syntax error.
How to give the name for the foreach concept in , separated syntax. How can i do it.?

It is not possible to assign a variable name to the for loop iterator when using a statement modifier:
Statement Modifiers
Any simple statement may optionally be followed by a SINGLE modifier, just before the terminating semicolon (or block ending). The possible modifiers are:
...
for LIST
foreach LIST
...
The for(each) modifier is an iterator: it executes the statement once for each item in the LIST (with $_ aliased to each item in turn).
print "Hello $_!\n" for qw(world Dolly nurse);
Instead, as stated above, you must use the $_ variable:
#values = (1..5);
print "$_\n" for #values;
If you want to use a variable name for the iterator, then simply use a for(each) in long form:
for my $var (#values) {
print "$var\n";
}

my #values = (1..5);
print "$_\n" foreach (#values);
__END__
1
2
3
4
5
If you really want the variable to be named $s, you can do this:
my #values = (1..5);
print "$s\n" while($s = shift #values);
but the first approach is more idiomatic and readable.

The other answers are correct, you can't declare a name for the iterator in a for statement modifier. But here is a way you can get the similar effect of doing a loop on one line with a named variable.
perl5i provides a number of language enhancements, including this one...
use perl5i::2;
#a->foreach(func($s) { say $s });
# Similar to...
print "$_\n" for #a;
This also works for grep, map and the notoriously tricky to use correctly "each".
%h->each(func($key, $val) { say "$key => $val" });
You can also work in pairs, triplets, etc...
#a->foreach(func($this,$that) { say "$this, $that" });
Performance may suffer as perl5i makes a full function call for each iteration, rather than just a cheaper block entry.

Although I wouldn't recommend this in general due to readability issues, you could temporarily alias another global variable to the _ variable.
{
our $s;
local *s = *_;
print "$s\n" for 1, 2, 3;
}

You use a do block and reassign the $_ variable inside the block:
#values = (1..5);
do { $s = $_; print "$s\n" },foreach (#values);

Related

Perl map passing arguments

I'm trying map() with my own subroutine. When I tried it with a Perl's builtin function, it works. But when I tried map() with my own subroutine, it fails.
I couldn't point out what makes the error.
Here is the code snippet.
#!/usr/bin/perl
use strict;
sub mysqr {
my ($input) = #_;
my $answer = $input * $input;
return $answer;
}
my #questions = (1,2,3,4,5);
my #answers;
#answers = map(mysqr, #questions); # doesn't work.
#answers = map {mysqr($_)} #questions; #works.
print "map = ";
print join(", ", #answers);
print "\n";
Map always assigns an element of the argument list to $_, then evaluates the expression. So map mysqr($_), 1,2,3,4,5 calls mysqr on each of the elements 1,2,3,4,5, because $_ is set to each of 1,2,3,4,5 in turn.
The reason you can often omit the $_ when calling a built-in function is that many Perl built-in functions, if not given an argument, will operate on $_ by default. For example, the lc function does this. Your mysqr function doesn't do this, but if you changed it to do this, the first form would work:
sub mysqr {
my $input;
if (#_) { ($input) = #_ }
else { $input = $_ } # No argument was given, so default to $_
my $answer = $input * $input;
return $answer;
}
map(mysqr, 1,2,3,4,5); # works now
The difference is that in the second case, you are explicitly passing the argument, and in the first one, you pass nothing.
#answers = map(mysqr, #questions); # same as mysqr(), no argument passed
#answers = map {mysqr($_)} #questions; # $_ is passed on to $input
You might be thinking of the fact that many Perl built-in functions use $_ when no argument is given. This is, however, not the default behaviour of user defined subroutines. If you want that functionality, you need to add it yourself. Though be warned that it often is not a good idea.
Note that if you use use warnings, which you always should, you will get a descriptive error:
Use of uninitialized value $input in multiplication (*) at foo.pl line 8.
Which tells you that no data is passed to $input.
Not using warnings is not removing errors from your code, it is merely hiding them, much like hiding the "low oil" warning lamp in a car does not prevent engine failure.

How is the map function in Perl implemented?

Is map function in Perl written in Perl? I just can not figure out how to implement it. Here is my attempt:
use Data::Dumper;
sub Map {
my ($function, $sequence) = #_;
my #result;
foreach my $item (#$sequence) {
my $_ = $item;
push #result, $function->($item);
}
return #result
}
my #sample = qw(1 2 3 4 5);
print Dumper Map(sub { $_ * $_ }, \#sample);
print Dumper map({ $_ * $_ } #sample);
$_ in $function is undefined as it should be, but how map overcomes this?
map has some special syntax, so you can't entirely implement it in pure-perl, but this would come pretty close to it (as long as you're using the block form of map):
sub Map(&#) {
my ($function, #sequence) = #_;
my #result;
foreach my $item (#sequence) {
local $_ = $item;
push #result, $function->($item);
}
return #result
}
use Data::Dumper;
my #sample = qw(1 2 3 4 5);
print Dumper Map { $_ * $_ } #sample;
print Dumper map { $_ * $_ } #sample;
$_ being undefined is overcome by using local $_ instead of my $_. Actually you almost never want to use my $_ (even though you do want to use it on almost all other variables).
Adding the (&#) prototype allows you not to specify sub in front of the block. Again, you almost never want to use prototypes but this is a valid use of them.
While the accepted answer implements a map-like function, it does NOT do it in the way perl would. An important part of for, foreach, map, and grep is that the $_ they provide to you is always an alias to the values in the argument list. This means that calling something like s/a/b/ in any of those constructs will modify the elements they were called with. This allows you to write things like:
my ($x, $y) = qw(foo bar);
$_ .= '!' for $x, $y;
say "$x $y"; # foo! bar!
map {s/$/!!!/} $x, $y;
say "$x $y"; # foo!!!! bar!!!!
Since in your question, you have asked for Map to use array references rather than arrays, here is a version that works on array refs that is as close to the builtin map as you can get in pure Perl.
use 5.010;
use warnings;
use strict;
sub Map (&\#) {
my ($code, $array) = splice #_;
my #return;
push #return, &$code for #$array;
#return
}
my #sample = qw(1 2 3 4 5);
say join ', ' => Map { $_ * $_ } #sample; # 1, 4, 9, 16, 25
say join ', ' => map { $_ * $_ } #sample; # 1, 4, 9, 16, 25
In Map, the (&\#) prototype tells perl that the Map bareword will be parsed with different rules than a usual subroutine. The & indicates that the first argument will either be a bare block Map {...} NEXT or it will be a literal code reference Map \&somesub, NEXT. Note the comma between the arguments in the latter version. The \# prototype indicates that the next argument will start with # and will be passed in as an array reference.
Finally, the splice #_ line empties #_ rather than just copying the values out. This is so that the &$code line will see an empty #_ rather than the args Map received. The reason for &$code is that it is the fastest way to call a subroutine, and is as close to the multicall calling style that map uses as you can get without using C. This calling style is perfectly suited for this usage, since the argument to the block is in $_, which does not require any stack manipulation.
In the code above, I cheat a little bit and let for do the work of localizing $_. This is good for performance, but to see how it works, here is that line rewritten:
for my $i (0 .. $#$array) { # for each index
local *_ = \$$array[$i]; # install alias into $_
push #return, &$code;
}
My Object::Iterate module is an example of what you are trying to do.

Why is the list my Perl map returns just 1's?

The code I wrote is as below :
#!/usr/bin/perl
my #input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my #output = map { $_ =~ s/\..*$// } #input ;
print #output ;
My intention is to let the file name without the extension stored in the array #output.
but instead it stores the value returned by s/// rather than the changed file name in #output, so the result looks like
1
1
1
so what is the correct way to use map under this situation?
Out of all of those answers, no one simply said that map returns the result of the last evaluated expression. Whatever you do last is the thing (or things) map returns. It's just like a subroutine or do returning the result of their last evaluated expression.
Perl v5.14 adds the non-destructive substitution, which I write about in Use the /r substitution flag to work on a copy. Instead of returning the number of replacements, it returns the modified copy. Use the /r flag:
my #output = map { s/\..*$//r } #input;
Note that you don't need to use the $_ with the binding operator since that's the default topic.
Ok, first off, you probably meant to have $_ =~ s/\..*$// — note the missing s in your example. Also, you probably mean map not grep.
Second, that doesn't do what you want. That actually modifies #input! Inside grep (and map, and several other places), $_ is actually aliased to each value. So you're actually changing the value.
Also note that pattern match does not return the matched value; it returns true (if there is a match) or false (if there isn't). That's all the 1's you're seeing.
Instead, do something like this:
my #output = map {
(my $foo = $_) =~ s/\..*$//;
$foo;
} #input ;
The first copies $_ to $foo, and then modifies $foo. Then, it returns the modified value (stored in $foo). You can't use return $foo, because its a block, not a subroutine.
The problem of $_ aliasing the list's values was already discussed.
But what's more: Your question's title clearly says "map", but your code uses grep, although it looks like it really should use map.
grep will evaluate each element in the list you provide as a second argument. And in list context it will return a list consisting of those elements of the original list for which your expression returned true.
map on the other hand uses the expression or block argument to transform the elements of the list argument returning a new list consisting of the transformed arguments of the original.
Thus your problem could be solved with code like this:
#output = map { m/(.+)\.[^\.]+/ ? $1 : $_ } #input;
This will match the part of the file name that is not an extension and return it as a result of the evaluation or return the original name if there is no extension.
You are missing the 's' for substitution.
$_ =~ /\..*$//
should be
$_ =~ s/\..*$//
Also you might be better off to use s/\.[^\.]*$// as your regular expression to make sure you just remove the extension even when the filename contains a '.' (dot) character.
As mentioned, s/// returns the number of substitutions performed, and map returns the last expression evaluated from each iteration, so your map returns all 1's. One way to accomplish what you want is:
s/\..*$// for my #output = #input;
Another way is to use Filter from Algorithm::Loops
derobert shows you a correct way of mapping #input to #output.
I would, however, recommend using File::Basename:
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;
my #input = qw( a.1.txt b.txt c.txt );
my #output = map { scalar fileparse($_, qr/\.[^.]*/) } #input ;
use Data::Dumper;
print Dumper \#output;
Output:
C:\Temp> h
$VAR1 = [
'a.1',
'b',
'c'
];
The problem: both the s/../.../ operator and Perl's map are imperative, expecting you to want to modify each input item; Perl doesn't in fact have a builtin for the functional map that yields its results without modifying the input.
When using s, an option is to append the r modifier:
#!/usr/bin/perl
my #input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my #output = map { s/\..*$//r } #input ;
print join(' ', #output), "\n";
The general solution (suggested by derobert) is to use List::MoreUtils::apply:
#!/usr/bin/perl
use List::MoreUtils qw(apply);
my #input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my #output = apply { s/\..*$// } #input ;
print join(' ', #output), "\n";
or copy its definition into your code:
sub apply (&#) {
my $action = shift;
&$action foreach my #values = #_;
wantarray ? #values : $values[-1];
}
Your code sample is missing an s in the match operator. Other than that, it worked fine for me:
$, = "\n";
my #input = ( "a.txt" , "b.txt" , "c.txt" );
my #output = grep { $_ =~ s/\..*$// } #input;
print #output;
Output is:
a
b
c

Why is Perl foreach variable assignment modifying the values in the array?

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

foreach my $var (#list) -- $var is a reference?

So, I never knew this and I want to get some clarifcation on it. I know if you do
foreach (#list){
if you change $_ in that loop it will affect the actual data. But, I did not know that if you did
foreach my $var1 (#list){
If you changed $var1 in the loop it would change the actual data. :-/ So, is there a way to loop over #list but keep the variable a read-only copy, or a copy that if changed will not change the value in #list?
$var is aliased to each item in turn.
See http://perldoc.perl.org/perlsyn.html#Foreach-Loops
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.
Easiest way is just to copy it:
foreach my $var1 (#list) {
my $var1_scratch = $var1;
or
foreach my $var1 ( map $_, #list ) {
But if $var1 is a reference, $var1_scratch will be a reference to the same thing.
To be really safe, you'd have to use something like Storable::dclone to do a deep copy:
foreach my $var1 ( #{ Storable::dclone( \#list ) } ) {
}
(untested). Then you should be able to safely change $var1. But it could be expensive if
#list is a big datastructure.
It is an alias, not a reference. If you want to create your own aliases (outside of for) you can use Data::Alias.
The only difference between these loops:
foreach (#array) { ... }
foreach my $var (#array) { ... }
is the loop variable. The aliasing is a function of foreach, not the implicit variable $_. Note that this is an alias (another name for the same thing) and not a reference (a pointer to a thing).
In the simple (and common) case, you can break the aliasing by making a copy:
foreach my $var (#array) {
my $copy = $var;
# do something that changes $copy
}
This works for ordinary scalar values. For references (or objects) you would need to make a deep copy using Storable or Clone, which could be expensive. Tied variables are problematic as well, perlsyn recommends avoiding the situation entirely.
I don't know how to force the variable to be by-value instead of by-reference in the foreach statement itself. You can copy the value of $_ though.
#!/usr/perl/bin
use warnings;
use strict;
use Data::Dumper;
my #data = (1, 2, 3, 4);
print "Before:\n", Dumper(\#data), "\n\n\n";
foreach (#data) {
my $v = $_;
$v++;
}
print "After:\n", Dumper(\#data), "\n\n\n";
__END__
Make a copy of #list in the for statement:
foreach my $var1 (() = #list) {
# modify $var without modifying #list here
}