Print the return of a method - perl

I know in php I can do something like this
echo "{$this->method}";
and I swear there was a way to do it in perl
Update:
What I am trying to do is print a scalar that the method returns. I was kind of hoping of doing within the string like in php, just because I'm lazy :P.

Are you just trying to evaluate an arbitrary expression inside a double quoted string? Then maybe you're thinking of
print "#{[$this->method]}";
There is also a trick to call the method in scalar context, but the syntax is a little less clean.
print "${\($this->method)}";

Well, if $this->method outputs a string or a number (like PHP, Perl can automatically convert numbers to strings when required), then you can do print $this->method . "\n";.
If $this->method outputs a data structure (eg an array reference or a hash reference), you can use Data::Dumper to look at the structure of the data. Basically, print Dumper($foo) is the Perl equivalent of PHP's var_dump($foo).
What are you trying to do, exactly?

If $this->method is returning a string, you can do this:
print $this->method . "\n";
without quotes. That will print your string. Sometimes, that can lead to a clumsy looking statement:
print "And we have " . $this->method . " and " . $that->method . " and " . $there->method . "\n";
In that case you can use a little programming trick of:
print "And we have #{[$this->method]} and #{[that->method]} and #{[$their->method]}\n";
Surrounding a function with #{[]} prints out the function's value. Someone explained this to me once, but I can't remember why it works.

Related

Why can't I concatenate and print the value returned by a function?

I have a Perl program that calls a function, in this case ref, and checks the result. Specifically, I am testing that a variable is a hash reference. In that case, ref will return 'HASH'. I tested it and it worked.
Then I decided to log it, adding a print that displays the result of the same call, but it didn't work correctly. Here is a reduced version:
use strict;
use warnings;
my $book_ref = {};
$book_ref->{'title'} = 'The Lord of the Rings';
if (ref $book_ref eq 'HASH') {
print "ref \$book_ref is a " . ref $book_ref . "\n";
}
print "Program is over\n";
To my surprise, this was the output:
ref $book_ref is a Program is over
And despite using strict and warnings there were neither errors nor warnings.
The call to ref is exactly the same (it's a copy and paste), but while it works correctly inside the if condition, print doesn't display anything, and actually seems to be interrupted, as the newline character is clearly skipped. Why does the behaviour change?
The reason is that the function ref is called without parentheses, and this causes the line to be parsed incorrectly.
When ref is called inside the if, the condition is clearly delimited by parentheses, which means that ref knows perfectly well what its argument is: $book_ref. There's no ambiguity.
Instead, when printing the result, the lack of parentheses means that Perl will parse the line in a way that was not intended:
First it will concatenate $book_ref and "\n". In scalar context, $book_ref evaluates to a string like HASH(0x1cbef70), therefore the result is the string "HASH(0x1cbef70)\n"
Then, ref will be called on the string "HASH(0x1cbef70)\n", producing as output an empty string: ''.
At this point, print prints the empty string, that is, nothing, and stops there. The newline character \n is skipped because it has already been consumed by ref, so print doesn't even see it. And there are no errors.
All of this descends from Perl's operator precedence: from the table,
(...)
8. left + - .
9. left << >>
10. nonassoc named unary operators
11. nonassoc < > <= >= lt gt le ge
12. nonassoc == != <=> eq ne cmp ~~
(...)
where the "function call" is actually a "named unary operator" (the unary operator being ref). So the . operator at line 8 has higher precedence than the function call at line 10, which is why the result of print is not the expected one. On the other hand, the function call has higher precedence than eq (at line 12), which is why inside the if everything works as expected.
The solution to precedence problems is to use .
A possibility is to use parentheses to emphasize the function call:
print "ref \$book_ref is a " . ref($book_ref) . "\n";
Another one, which I like less but nevertheless works, is to use parentheses to isolate the string that must be concatenated, by putting the opening bracket just before ref:
print "ref \$book_ref is a " . (ref $book_ref) . "\n";
Another possible approach, suggested by zdim in a comment, is to use commas:
print "ref \$book_ref is a ", ref $book_ref, "\n";
When I first wrote the if I decided to avoid the parentheses to make the code more readable. Then I copied it and didn't notice the problem. I ended up wasting 2 hours to find the bug.
The best solution seems to be the first one, because if you copy it to another place (like another print) you are guaranteed to also copy the parentheses that prevent the problem. With the second one I probably wouldn't realize how important the parentheses are and wouldn't copy them. And the third one works only if you remember that you have to always use commas and not dots, which is not obvious and therefore error prone. So, although they work, I consider them less safe.
Other comments have also suggested to use printf, which requires dealing with format specifiers, or expression interpolation, like print "ref \$book_ref is a ${\ ref $book_ref }\n";, which I find harder to read.
Bottom line: always use the parentheses.

Perl appending (s)printf output to string

I'm parsing some data and organizing it, and now I need to capture it inside a variable.
I've never used printf or sprintf before this.
I'm using printf in a manner like this to organize the data:
printf("%-30s %18s %18s\n", "$a", "$b", "$c\n");
Now I have a variable that's storing a string, and I want to append the organized data to the variable $result.
I tried something like
$result.printf("%-30s %18s %18s\n", "$a", "$b", "$c\n");
and it doesn't work. I tried sprintf too.
Any ideas?
Thanks, S
printf outputs the constructed string to the specified handle (or the current default if omitted) and returns a boolean which indicates whether an IO error occurred or not. Not useful. sprintf returns the constructed string, so you want this.
To concatenate two strings (append one to another), one uses the . operator (or join)
$result . sprintf(...)
But you said this doesn't work. Presumably, it's because you also want to store the produced string in $result, which you can do using
$result = $result . sprintf(...);
or the shorter
$result .= sprintf(...);
Don't know what you mean by "tried sprintf too", because there's no reason it would not work if you do it right. Although that syntax you showed does not look much like perl, more like python or ruby?
my $foo = sprintf("%-30s %18s %18s\n", "$a", "$b", "$c\n");
$result .= $foo;

perl: printing object properties

I'm playing a bit with the Net::Amazon::EC2 libraries, and can't find out a simple way to print object properties:
This works:
my $snaps = $ec2->describe_snapshots();
foreach my $snap ( #$snaps ) {
print $snap->snapshot_id . " " . $snap->volume_id . "\n";
}
But if I try:
print "$snap->snapshot_id $snap->volume_id \n";
I get
Net::Amazon::EC2::Snapshot=HASH(0x4c1be90)->snapshot_id
Is there a simple way to print the value of the property inside a print?
$snap->volume_id is not a property, it is a method call. While you could interpolate a method call inside a string, it is exceedingly ugly.
To get all the properties of an object you can use the module Data::Dumper, included with core perl:
use Data::Dumper;
print Dumper($object);
Not in the way you want to do it. In fact, what you're doing with $snap->snapshot_id is calling a method (as in sub). Perl cannot do that inside a double-quoted string. It will interpolate your variable $snap. That becomes something like HASH(0x1234567) because that is what it is: a blessed reference of a hash.
The interpolation only works with scalars (and arrays, but I'll omit that). You can go:
print "$foo $bar"; # scalar
print "$hash->{key}"; # scalar inside a hashref
print "$hash->{key}->{moreKeys}->[0]"; # scalar in an array ref in a hashref...
There is one way to do it, though: You can reference and dereference it inside the quoted string, like I do here:
use DateTime;
my $dt = DateTime->now();
print "${\$dt->epoch }"; # both these
print "#{[$dt->epoch]}"; # examples work
But that looks rather ugly, so I would not recommend it. Use your first approach instead!
If you're still interested in how it works, you might also want to look at these Perl FAQs:
What's wrong with always quoting "$vars"?
How do I expand function calls in a string?
From perlref:
Here's a trick for interpolating a subroutine call into a string:
print "My sub returned #{[mysub(1,2,3)]} that time.\n";
The way it works is that when the #{...} is seen in the double-quoted
string, it's evaluated as a block. The block creates a reference to an
anonymous array containing the results of the call to mysub(1,2,3) .
So the whole block returns a reference to an array, which is then
dereferenced by #{...} and stuck into the double-quoted string. This
chicanery is also useful for arbitrary expressions:
print "That yields #{[$n + 5]} widgets\n";
Similarly, an expression that returns a reference to a scalar can be
dereferenced via ${...} . Thus, the above expression may be written
as:
print "That yields ${\($n + 5)} widgets\n";
Stick with the first sample you showed. It looks cleaner and is easier to read.
I'm answering this because it took me a long time to find this and I feel like other people may benefit as well.
For nicer printing of objects use Data::Printer and p():
use DateTime;
use Data::Printer;
my $dt = DateTime->from_epoch( epoch => time );
p($dt);
The PERL translator has limited depth perception within quotes. Removing them should solve the problem. Or just load the real values into a simple variable that you can print within the quotes. Might need to do that if you have objects which contain pointers to other objects:
SwissArmyChainSaw =/= PureMagic:
print("xxx".$this->{whatever}."rest of string\n");
The problem is that $snap is being interpolated inside the string, but $snap is a reference. As perldoc perlref tells us: "Using a reference as a string produces both its referent's type, including any package blessing as described in perlobj, as well as the numeric address expressed in hex."
In other words, within a string, you can't dereference $snap. Your first try was the correct way to do it.
I agree with most comment, stick to concatenation for easy reading. You can use
say
instead of print to spare of using the "\n".

Perl, how to print evaluation of sum of numbers using "print"

Look at the following code sample.
$ct=5;
print "hey $ct+1";
When evaluated, this returns:
hey 5+1
However, I'm trying to get the code to return:
hey 6
Does anybody know if there is some sort of evaluation call that I can make to have this occur correctly, or a way to change the syntax a tad? I know I could simply do:
$dum=$ct+1;
print "hey $dum";
But what I'm showing you is a simple version of something more complicated. If I could get this small example to work correctly, my larger problem is solved. Thanks.
You want to execute a Perl addition operator, but the code only features a Perl string literal. You want
print "hey ", $ct+1;
If you did have Perl source code in a variable and you wanted to execute it, you'd have to invoke the Perl parser and compiler: eval EXPR.
Another alternative is formatted printing.
my $ct = 5;
printf "hey %s", $ct+1;
This is kind of intermediate Perl, but you can put your expression into a reference and dereference it inside double quotes:
print "hey #{[$ct+1]}";
print "hey ${\($ct+1)}";
Code like this is harder to read and gives Perl the reputation of being unfriendly to newbies, so I don't recommend it over extracting the expressions out of the quotes:
print "hey ", ($ct+1);
print "hey " . ($ct+1);

why can't I use this in my subroutine?

I have the following:
$sql="SELECT * FROM table WHERE name = ? ";
&checkDB( $sql , "bob" , "while" );
sub checkDB(){
my $sth=$dbh->prepare($_[0]) or warn "$DBI::errstr";
$sth->execute( $_[1] ) or warn "$DBI::errstr";
print $_[2] . "\n"; # this works
$_[2] ( my #rows= $sth -> fetchrow() ) { # this doesn't work
blah, blah, blah
}
}
I pass my sql statement, 'bob' and either a "while" or "unless" variable into the subroutine.
My subroutine will let me pass the "while" variable (it will print "while\n") but it won't let me use it to fetchrows. What am I doing wrong?
my exact error is "syntax error at script.pl near "}" "....works fine if I substitute $_[2] with the actual word "while"
Because while and "while" are different things.
You can't substitute arbitrary language constructs for strings.
It is not possible to use variable in place of keyword in perl. If you really must do such constructs, look at eval - it lets you run arbitrary string as perl code.
I think what you would need here is an eval, however this has evil written all over it. You definitely want to be sure that nothing in that code is tainted (coming from user). I think you probably should think very hard about why you would want to do this and if there isn't a better way (two different functions for example). I know we programmers like to be lazy, but evals are dangerous.