Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there a + before the first left parenthesis following print in this Perl example? [duplicate]

Tags:

perl

Recently I came across a statement like this

print +( map { $_ + 1 } @$_ ), "\n" for @$array; # <- AoA

I had not seen the + operator used with print like this before. Running the statement, it is not hard to infer what it is doing. But I am having trouble finding documentation on the + operator used this way.

my @tab = (
[1,2,3,4],
[qw(a b c d)],
);

my $test = "(", +( map { qq( >>$_<< ) } @$_ ), ")\n" for @tab;
print $test;
my @test = "(", +( map { qq( >>$_<< ) } @$_ ), ")\n" for @tab;
print @test;
my %test = "(", +( map { qq( >>$_<< ) } @$_ ), ")\n" for @tab;
print %test;

Above produces the warnings: Useless use of a constant ()) in void context for all three tests. Use of uninitialized value for the scalar test and Odd number of elements in hash assignment for both the array and hash tests, It would be nice to be able to store the statements output in a scalar. I know I can store an Identical string in a scalar using a for loop like below.

print "(", +( map { qq( >>$_<< ) } @$_ ), ")\n" for @tab;

my $out = '';
for (@tab) {
    $out .= "(" . ( join '', map { qq( >>$_<< ) } @$_ ) . ")\n";
}   
print $out;

# output ...
( >>1<<  >>2<<  >>3<<  >>4<< )
( >>a<<  >>b<<  >>c<<  >>d<< )
---
( >>1<<  >>2<<  >>3<<  >>4<< )
( >>a<<  >>b<<  >>c<<  >>d<< )

Why can't I store the statement in any variable or test its type. Id like in detail what is actually happening with the print builtin when using the + operator.

EDIT: I belive my original post was kind of confusing, I did want to learn more about the + operator used with print but what I was really after is how to store each iteration of the shorthand for statement into a scalar string - I found a way after some testing ...

use strict;
use warnings;
my @a = ([1,2,3],[qw/a b c/]);
my $one = '';
$one .= "(" . ( join '', map { " >>$_<< " } @$_ ) . ")\n" for @a;
print $one;

# output ...
( >>1<<  >>2<<  >>3<< )
( >>a<<  >>b<<  >>c<< )
like image 227
BryanK Avatar asked Jan 09 '23 14:01

BryanK


1 Answers

I think you've asked two questions here, so I'll answer both of them as well as I can... Firstly, you're referring to the unary "+" operator, from perlop

Unary "+" has no effect whatsoever, even on strings. It is useful syntactically for separating a function name from a parenthesized expression that would otherwise be interpreted as the complete list of function arguments.

To expand on what exactly is going on in the print statement, I found the explanation on perlmaven to be pretty good.

The documentation explains that the + separates the print function from the parentheses ( and tells perl that these are not the parentheses wrapping the parameters of the print function.

That might satisfy you, but if you are further intersted you can use the B::Deparse module to ask perl how does it understand this code-snippet:

print +(stat $filename)[7];

We save that content in the plus.pl file and run perl -MO=Deparse plus.pl. The result is:

print((stat $filename)[7]);

Hope this is helpful!

Edit

Explaining in detail why you can't use the same command to print that sequence and assign it to a scalar is maybe outside of the scope of this question, but I'll attempt to briefly explain... with the note that if I'm wrong about something, I'd love a correction, I've come to understand how this works through writing code using this, not from reading the perl docs on its inner workings :) With the print statement

print "(", +( map { qq( >>$_<< ) } @$_ ), ")\n" for @tab;

perl is interpreting it as

(print "(", +( map { qq( >>$_<< ) } @$_ ), ")\n") for @tab;

That is, it's grouping all of the stuff you want to print, and performing it for each element in @tab. However, with your scalar assignment

my $test = "(", +( map { qq( >>$_<< ) } @$_ ), ")\n" for @tab;

perl is performing the for @tab code on ")\n", thus your Useless use of a constant ()) in void context warning. To do what you want, you need to make it explicit what you want for'd

my $test; ( $test .= "(" . join ('', ( map { qq( >>$_<< ) } @$_)) . ")\n") for @tab; print $test;

Should get you what you're looking for. Note the parenthesis around the entire assignment, and the omission of the unary "+" before the map, which is no longer necessary.

like image 121
Zippers Avatar answered Jan 11 '23 04:01

Zippers