How does perl recognise end of variable while printing? - perl

How does perl recognise end of variable?
For example, this code:
use warnings;
my $a = 10;
print "Value of a is $a:::";
Output:
Use of uninitialized value $a:: in concatenation (.) or string at tryprint.pl line 6.
Value of a is :
Why does it consider $a:: and not $a: or $a:::
This works:
print "Value of a is $a\:::";
prints:
Value of a is 10:::

:: is used to print/access a variable from a package/symbol table. For eg, to access a scalar variable x in package abc, Perl uses $abc::x where abc is the name of the symbol table and x is the variable. Similarly, when you used $a:::, Perl thought there is a package whose name is 'a' and the variable name as :, and hence those error.
See this example below:
our $a = 10;
{
my $a=20;
print "lexical a is $a \n";
print "Value of a is $main::a";
}

Related

Perl ref() does not print the 'HASH' type

The following code does not print the 'HASH' type. What is wrong with this code ?
#! /usr/bin/perl
$prices{'pizza'} = 12.00;
$prices{'coke'} = 1.25;
$prices{'sandwich'} = 3.00;
print ref($prices);
First of all, you should put use strict; and use warnings; at the top of your script (and do that for all the future Perl code as well). After doing so, you will see the following:
Global symbol "%prices" requires explicit package name at ./a.pl line 4.
Global symbol "%prices" requires explicit package name at ./a.pl line 5.
Global symbol "%prices" requires explicit package name at ./a.pl line 6.
Global symbol "$prices" requires explicit package name at ./a.pl line 7.
Execution of ./a.pl aborted due to compilation errors.
What it means is that you tried to use to separate variables: a %prices hash and a $prices scalar.
After fixing variable declaration using my %prices;, you can get a reference to your %prices hash as follows:
my $prices_ref = \%prices;
print ref($prices_ref);
From formal standpoint, the answer may be shorter:
ref($prices) would return 1 if $prices value were a reference to another variable or false otherwise.
ref($prices) is the first use of an undeclared variable $prices (previous lines refer to another undeclared variable - hash %prices).
The value of $prices is undef and ref($prices) is an empty string.
Probably, your idea was to write
$prices->{'pizza'} = 12.00;
$prices->{'coke'} = 1.25;
$prices->{'sandwich'} = 3.00;
print ref($prices);

Symbolic reference to sub in a package, trying to understand

use strict;
use warnings;
package Foo::Bar;
sub baz { print "$_[0]\n" }
package main;
{ # test 1
my $quux = "Foo::Bar::baz";
no strict 'refs';
&$quux(1);
}
{ # test 2
my $qux = 'Foo::Bar';
my $quux = "$qux\::baz";
no strict 'refs';
&$quux(2);
}
{ # test 3
my $qux = 'Foo::Bar';
my $quux = "$qux::baz";
no strict 'refs';
&$quux(3);
}
Output:
Name "qux::baz" used only once: possible typo at test31.pl line 21.
1
2
Use of uninitialized value $qux::baz in string at test31.pl line 21.
Undefined subroutine &main:: called at test31.pl line 23.
Why does test 2 work, and why does backslash has to be placed exactly there?
Why does test 3 not work, is it so far, in syntax, from test 1?
I tried to write that string as "{$qux}::baz", but it doesn't work, too.
I came across this looking at source of Image::Info distribution.
$qux::baz refers to scalar baz in package qux.
"$qux::baz" is the stringification of that scalar.
"$qux::baz" is another way of writing $qux::baz."".
Curlies can be used to indicate where the variable ends.
"$foo bar" means $foo." bar"
"${f}oo bar" means $f."oo bar"
As such,
"${qux}::baz" is another way of writing $qux."::baz".
"$qux\::baz" is a cute way of doing $qux."::baz" since \ can't appear in variable names.
A variable name can either be simple like $foo or a fully qualified name (in the case of package variables). Such a fully qualified name looks like $Foo::bar. This is the “global” variable $bar in the package Foo.
If an interpolated variable is to be followed by a double colon :: and the variable should not be interpreted as a fully qualified package variable name, then you can either:
Use string concatenation: $qux . "::baz"
Terminate the variable name with a backslash: "$qux\::baz"
Surround the variable name (not the sigil) with curly braces, as if the name were a symbolic reference: "${qux}::bar".

Unable to create Hash in Perl?

I am learning Perl Script from here.
I am having problem creating Hash. The code is here:
print "Hello World!\n";
#days = ("1", "2");
print "There are $#days days\n";
print "1 is $days[0]\n";
%months = ("a" => 1, "b" => 2, "c" => 3);
print "There are $#months keys\n";
print "a is $months[0]\n";
for $i (keys %months)
{ print "$i has value $months[$i].\n"}
Now its working fine with the array.
But for Hash its printing "There are -1 keys".
Also its not printing anything for the variable values in last to print calls.
You are using the array syntax on a hash, which does not do what you think at all. Instead of operating on your hash, you are operating on an array called #months. For example:
print "There are $#months keys\n";
This will look for the array #months, see that it is empty, and happily print -1.
When you do
for $i (keys %months) {
print "$i has value $months[$i].\n"
}
Perl will try to convert the keys a, b and c to numbers, which will be 0. This will issue a warning:
Argument "a" isn't numeric in array element ...
Then it will print the empty array element $month[0]. Which will issue an undefined value warning. You do not get these warnings, because you did not use
use strict;
use warnings;
In your script. strict would have told you that #months has not been declared, and stopped this bug right away.
The syntax you should have used is:
print "There are " . (keys %months) . " keys\n";
...
print "$i has value $months{$i}\n";
In Perl, accessing elements in a hash use a slightly different syntax to arrays. Use curlies for hashes, square brackets for arrays:
print "a is $months{a}\n"; # "a is 1"
And $#months is another way of saying 'last index of #months', when what you really meant was to count the number of keys in %months:
printf "There are %d keys\n", scalar keys %months;
If you insist on print instead of printf:
print "There are $#{[keys %months]} keys\n";
(but maybe it's a few steps ahead of where you want to be at the moment)
$#months and $months[0]refer to an array and not a hash. You access the value of a hash by using curly braces $months{key}.
Also, you should use strict; and initialize variables with my(). If you had done that, you would have gotten a compiler error that #months does not exist.

Where is the error "Use of uninitialized value in string ne" coming from?

Where is the uninitialised value in the below code?
#!/usr/bin/perl
use warnings;
my #sites = (undef, "a", "b");
my $sitecount = 1;
my $url;
while (($url = $sites[$sitecount]) ne undef) {
$sitecount++;
}
Output:
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
You can't use undef in a string comparison without a warning.
if ("a" ne undef) { ... }
will raise a warning. If you want to test if a variable is defined or not, use:
if (defined $var) { ... }
Comments about the original question:
That's a strange way to iterate over an array. The more usual way of doing this would be:
foreach my $url (#sites) { ... }
and drop the $sitecount variable completely, and don't overwrite $url in the loop body. Also drop the undef value in that array. If you don't want to remove that undef for some reason (or expect undefined values to be inserted in there), you could do:
foreach my $url (#sites) {
next unless defined $url;
...
}
If you do want to test for undefined with your form of loop construct, you'd need:
while (defined $sites[$sitecount]) {
my $url = $sites[$sitecount];
...
$sitecount++;
}
to avoid the warnings, but beware of autovivification, and that loop would stop short if you have undefs mixed in between other live values.
The correct answers have already been given (defined is how you check a value for definedness), but I wanted to add something.
In perlop you will read this description of ne:
Binary "ne" returns true if the left argument is stringwise not equal
to the right argument.
Note the use of "stringwise". It basically means that just like with other operators, such as ==, where the argument type is pre-defined, any arguments to ne will effectively be converted to strings before the operation is performed. This is to accommodate operations such as:
if ($foo == "1002") # string "1002" is converted to a number
if ($foo eq 1002) # number 1002 is converted to a string
Perl has no fixed data types, and relies on conversion of data. In this case, undef (which coincidentally is not a value, it is a function: undef(), which returns the undefined value), is converted to a string. This conversion will cause false positives, that may be hard to detect if warnings is not in effect.
Consider:
perl -e 'print "" eq undef() ? "yes" : "no"'
This will print "yes", even though clearly the empty string "" is not equal to not defined. By using warnings, we can catch this error.
What you want is probably something like:
for my $url (#sites) {
last unless defined $url;
...
}
Or, if you want to skip to a certain array element:
my $start = 1;
for my $index ($start .. $#sites) {
last unless defined $sites[$index];
...
}
Same basic principle, but using an array slice, and avoiding indexes:
my $start = 1;
for my $url (#sites[$start .. $#sites]) {
last unless defined $url;
...
}
Note that the use of last instead of next is the logical equivalent of your while loop condition: When an undefined value is encountered, the loop is exited.
More debugging: http://codepad.org/Nb5IwX0Q
If you, like in this paste above, print out the iteration counter and the value, you will quite clearly see when the different warnings appear. You get one warning for the first comparison "a" ne undef, one for the second, and two for the last. The last warnings come when $sitecount exceeds the max index of #sites, and you are comparing two undefined values with ne.
Perhaps the message would be better to understand if it was:
You are trying to compare an uninitialized value with a string.
The uninitialized value is, of course, undef.
To explicitely check if $something is defined, you need to write
defined $something
ne is for string comparison, and undef is not a string:
#!/usr/bin/perl
use warnings;
('l' ne undef) ? 0 : 0;
Use of uninitialized value in string ne at t.pl line 3.
It does work, but you get a [slightly confusing] warning (at least with use warnings) because undef is not an "initialized value" for ne to use.
Instead, use the operator defined to find whether a value is defined:
#!/usr/bin/perl
use warnings;
my #sites = (undef, "a", "b");
my $sitecount = 1;
my $url;
while (defined $sites[$sitecount]) { # <----------
$url = $sites[$sitecount];
# ...
$sitecount++;
}
... or loop over the #sites array more conventionally, as Mat explores in his answer.

Perl Variable value is representing another variable

my $var = "Hello";
my $Hello = "Hi";
my question is: how can I substitute the value of $Hello in $var?
Here $var contains string "Hello" (there is a variable with same name $Hello). I am looking for a way to substitute it with the value of variable $Hello.
Please let me know how can I do that?
This is discussed in Perl FAQ (7): How can I use a variable as a variable name?.
In short, the answer is: Don't do it. Use hashes instead.
Longer answer:
A MUCH better and simpler solution would have been to store the values in a hash:
my %sayings = ("Hello" => "Hi");
my $key = "Hello";
my $var2 = $sayings{$key};
You can also use eval:
my $var = "Hello";
my $Hello = "Hi";
print "1.$var\n";
my $var2 = eval "\$$var";
print "2.$var2\n";
As a last resort, you COULD use symbolic references. HOWEVER (as discussed in the FAQ linked above), they ONLY work on global variables and ONLY when `use strict 'refs`` is not in effect - which it should always be for normal Perl development.
##############################################################
# This works. But only without "use strict" and on a package (global) variable.
##############################################################
no strict; # BOO! Bad coder!
my $var = "Hello";
$Hello = "Hi"; #Look, ma, GLOBAL VARIABLE! Bad Coder!
print "1.$var\n";
my $var2 = ${$var}; # You could use $$var shorthand
print "2.$var2\n";
# OUTPUT:
# 1. Hello
# 2. Hi
##############################################################
# I meant it about package (global variable)
##############################################################
no strict;
my $var = "Hello";
$Hello = "GLOBAL value";
my $Hello = "Hi";
print "1.$var\n";
my $var2 = ${$var}; # You could use $$var shorthand
print "2.$var2\n";
# OUTPUT - see how we don't get "Hi" on a second line
# If we did not assign "GLOBAL Value" to $Hellp on line 3
# , second line would contain no value at all (e.g. "2.")
# 1. Hello
# 2. GLOBAL value
##############################################################
# And, if you do what you SHOULDA done and used strict:
##############################################################
use strict; # Much better!
my $var = "Hello";
my $Hello = "Hi";
print "1.$var\n";
my $var2 = ${$var}; # You could use $$var shorthand
print "2.$var2\n";
# OUTPUT:
# Global symbol "$Hello" requires explicit package name at line 4.
# Execution aborted due to compilation errors.
P.S. If you simply want to use the value of $Hello hardcoded variable, you can do $var2 = $Hello;, but I have a feeling you meant you want to use whichever variable's name is contained in $var.
If you are willing to fill a placeholder in a string with the value of a variable, you could use :
my $Hello = "Hi";
my $var = "$Hello world";
# $var is now "Hi world"
If you want to replace each occurence of some variable name, you can use :
my $Hello = "Hi";
my $greet = "Hello, my name is Jimbo, and I hate Helloween.";
$greet =~ s/Hello/$Hello/g;
# $greet is now "Hi, my name is Jimbo, and I hate Hiween.";
Like proposed by DVK, eval could also partially do the trick :
my $Hello = "Hi";
my $greet = "\$Hello, my name is Jimbo, and I hate \$Helloween.";
$greet = eval "\"$greet\"";
# $greep is now "Hi, my name is Jimbo, and I hate ."
Edit: Sorry for the briefness of my previous answer, I typed it on my phone in a waiting room...