Perl preprocessor appended to other variable - perl

I am trying to write following code, where I want $varPRE1 to be $var1 and $varPRE2 to be $var2 after preprocessor is evaluated, but it is not working. Is there a work around?
#define PRE1 1
#define PRE2 2
my $var1 = 10;
my $var2 = 20;
print $varPRE1;
print $varPRE2;

What you wrote doesn't make sense even for the C preprocessor, since varPRE1 would get parsed as one token.
What you want to do is sort of possible, using what are called symbolic references, but it is a bad idea.
$PRE1 = '1';
$PRE2 = '2';
$var1 = 10;
$var2 = 20;
print ${'var' . $PRE1}; # same as print $var1 => 10
print ${'var' . $PRE2};
This will not work under use strict refs. It is also a bad idea to not use strict refs.

Related

How to convert string to floating point number inside a Perl hash?

My motive is to convert the string number into floating point number while creating a hash.
I have placed my entire code and error below. Please help me to solve this issue.
Sample code
use strict;
use warnings;
use Data::Dumper;
my $price = 8.5;
my $g={};
$g->{'get'}=sprintf('%.02f',$price);
print Dumper($g);
Current output
$VAR1 = {
'get' => '8.50'
};
Expected output
$VAR1 = {
'get' => 8.50
};
Despite the single quotes around 8.50 in the Dumper output, Perl will still treat it as a numeric value when you go to use it:
use strict;
use warnings;
my $price = 8.5;
my $g={};
$g->{'get'}=sprintf('%.02f',$price);
my $x = 5;
printf "%.02f\n", $x + $g->{get};
Outputs:
13.50
use Scalar::Util 'looks_like_number';
.
.
print Dumper($g) =~ s/'(.*?)'/looks_like_number($1)?$1:"'$1'"/ger;
Which changes the output from Dumper before it's printed. It removes both 's of every single quoted string if it looks like a number according to Scalar::Util.
I suspect you're worrying unnecessarily here. Perl treats strings and numbers as largely interchangeable and will generally do the right thing with data of either type. The number of times when you should care if you have a string or a number is tiny.
In fact, even if you explicitly give Perl a number in code like yours, it will be displayed as a string:
$ perl -MData::Dumper -E'say Dumper { get => 8.5 }'
$VAR1 = {
'get' => '8.5'
};

Perl: passing two hashes to sub

Below is a small sub to calculate distance between two points by passing two hashes each containing the x and y coordinates. I get a "syntax error near ]{" fatal error at the line calling the sub. Just started on Perl yesterday, not entirely sure what I am doing. How do I pass two hashes to a sub to return a value? Tried this without much success and not sure what I need to work on (hope it is ok to refer to an outside link).
%dot1 = ('x'=>5, 'y'=>6);
%dot2 = ('x'=>7, 'y'=>8);
sub dist {
my (%hash1) = #_[0];
my (%hash2) = #_[1];
$dist = ((#_[0]{'x'}-#_[1]{'x'})**2 + (#_[0]{'y'}-#_[1]{'y'})**2)**0.5;
}
$D = dist(\%dot1,\%dot2);
First and foremost, you should start every file with
use strict;
use warnings;
This lets Perl catch the most obvious errors in code.
This part is mostly fine, but under use strict Perl will complain about %dot1 and %dot2 being undeclared (and without strict they will be implicitly global, which is usually not what you want):
%dot1 = ('x'=>5, 'y'=>6);
%dot2 = ('x'=>7, 'y'=>8);
Change it to
my %dot1 = ('x'=>5, 'y'=>6);
my %dot2 = ('x'=>7, 'y'=>8);
The call
$D = dist(\%dot1,\%dot2);
has the same problem: It should be
my $D = dist(\%dot1,\%dot2);
What it does is pass references to %dot1 and %dot2 to the sub dist.
my (%hash1) = #_[0];
This line doesn't make much sense: #_[0] is a list slice, returning a list of elements of #_ corresponding to the indices 0. In other words, it's a one-element slice and better written as $_[0], accessing the single element directly.
But in either case it doesn't make sense to assign a single element to a hash. Perl will interpret it as a key and set the corresponding value to undef. Your call passed \%dot1 as the first argument, so $_[0] is a reference to a hash. By using it as a hash key, Perl will convert it to a string, yielding something like "HASH(0x0075ADD40)".
Your choices at this point are to either dereference the reference right there and make a copy:
my %hash1 = %{ $_[0] }; # effectively performs %hash1 = %dot1
Or keep the reference and dereference it each time you need access to the hash:
my $hashref1 = $_[0]; # $hashref1->{foo} accesses $dot1{foo} directly
$dist = ((#_[0]{'x'}-#_[1]{'x'})**2 + (#_[0]{'y'}-#_[1]{'y'})**2)**0.5;
There are a few issues here. First, you don't need the (implicitly global) variable $dist. You just want to return a value from the sub, which can be done with return. Then, as explained above, #_[0] and #_[1] should be $_[0] and $_[1], respectively. Fixing that we get
return (($_[0]{'x'} - $_[1]{'x'}) ** 2 + ($_[0]{'y'} - $_[1]{'y'}) ** 2) ** 0.5;
This does indeed work ($_[0]{'x'} is syntactic sugar for $_[0]->{'x'}, i.e. this expression dereferences the hash reference stored in $_[0] to reach the 'x' key of %dot1).
But we didn't use the variables we just created at all. Depending on which way you want to go, you should replace $_[0]{foo} by either $hash1{foo} or $hashref1->{foo} (and similar for $_[1] and %hash2/$hashref2).
Finally, instead of ** 0.5 we can just use sqrt.
Here's how I'd write it:
use strict;
use warnings;
sub dist {
my ($p1, $p2) = #_;
return sqrt(($p1->{x} - $p2->{x}) ** 2 + ($p1->{y} - $p2->{y}) ** 2);
}
my %dot1 = (x => 5, y => 6);
my %dot2 = (x => 7, y => 8);
my $D = dist(\%dot1, \%dot2);
print "Result: $D\n";
Many problems here, I'm afraid.
First you, need to add use strict and use warnings to your code. This will point out many errors. Mainly places where you use #array[index] but should have used $array[index]. You also don't declare any of your variables.
Fixing all of that gives me this code:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
my %dot1 = ('x'=>5, 'y'=>6);
my %dot2 = ('x'=>7, 'y'=>8);
sub dist {
my (%hash1) = $_[0];
my (%hash2) = $_[1];
my $dist = (($_[0]{'x'}-$_[1]{'x'})**2 + ($_[0]{'y'}-$_[1]{'y'})**2)**0.5;
}
my $D = dist(\%dot1,\%dot2);
say $D;
This still doesn't work. I get:
$ perl twohash
Reference found where even-sized list expected at twohash line 11.
Reference found where even-sized list expected at twohash line 12.
2.82842712474619
The errors are where you are assigning the two arguments you pass into dist().
my (%hash1) = $_[0];
my (%hash2) = $_[1];
You are passing references to hashes, not the hashes themselves (And you're right to do that), but this means that you get scalars, not hashes, in the subroutine. So those lines need to be:
my ($hash1) = $_[0];
my ($hash2) = $_[1];
Making those changes, the code now works and gives the result "2.82842712474619".
I'll just point out one further strangeness in your code - you assign the parameters to the function to two lexical variables ($hash1 and $hash2) but you then ignore those variables and instead go directly to #_ for this data. I expect you actually want:
my $dist = (($hash1->{'x'}-$hash2->{'x'})**2 + ($hash1->{'y'}-$hash2->{'y'})**2)**0.5;
Note, I've changed $_[0]{'x'} to $hash1->{'x'} as you have a reference to a hash.
All in all, this code is a bit of a mess and I suggest you go back to the very earliest chapters of whatever book you are learning Perl from.
Could you please try this:
use Data::Dumper;
my %dot1 = ('x'=>5, 'y'=>6);
my %dot2 = ('x'=>7, 'y'=>8);
sub dist {
my %hash1 = #_[0];
my %hash2 = #_[1];
$dist = ((#_[0]->{'x'}-#_[1]->{'x'})**2 + (#_[0]->{'y'}-#_[1]->{'y'})**2)**0.5;
}
$D = dist(\%dot1,\%dot2);
print $D;

Attempt to delete readonly key from a restricted hash, when it is not restricted

I quite often arrange my subroutine entry like this:
sub mySub {
my ($self, %opts) = #_;
lock_keys(%opts, qw(count, name));
...
my $name = delete $opts{name};
$self->SUPER::mySub(%opts);
}
to allow calling the sub using named arguments like this:
$obj->mySub(count=>1, name=>'foobar');
The lock_keys guards against calling the sub with mis-spelled argument names.
The last couple of lines are another common idiom I use, where if I am writing a method that overrides a superclass, I might extract the arguments which are specific to the subclass and then chain a call to the subclass.
This worked fine in perl 5.8, but after upgrading to Centos 6 (which has perl 5.10.1) I started to see seemingly random errors like this:
Attempt to delete readonly key 'otherOption' from a restricted hash at xxx.pl line 9.
These errors do not happen all the time (even in the same subroutine) but they do seem to relate to the call chain that results in calling the sub which bombs out.
Also note that they do not happen on perl 5.16 (or at least not on ideone).
What is causing these errors in perl 5.10? According to the manpage for Hash::Util, delete() should still work after lock_keys. It is like the whole hash is getting locked somehow.
I found the answer to this even before posting on SO, but the workaround is not great so feel free to chime in with a better one.
This SSCCE exhibits the problem:
#!/usr/bin/perl
use strict;
use Hash::Util qw(lock_keys);
sub doSomething {
my ($a, $b, %opts) = #_;
lock_keys(%opts, qw(myOption, otherOption));
my $x = delete $opts{otherOption};
}
my %h = (
a=>1,
b=>2
);
foreach my $k (keys %h) {
doSomething(1, 2, otherOption=>$k);
}
It seems that the problem is related to the values passed in as values to the named argument hash (%opt in my example). If these values are copied from keys of a hash, as in the example above, it marks the values as read-only in such a way that it later prevents deleting keys from the hash.
In fact you can see this using Devel::Peek
$ perl -e'
use Devel::Peek;
my %x=(a=>1);
foreach my $x (keys %x) {
my %y = (x => $x);
Dump($x);
Dump(\%y);
}
'
SV = PV(0x22cfb78) at 0x22d1fd0
REFCNT = 2
FLAGS = (POK,FAKE,READONLY,pPOK)
PV = 0x22f8450 "a"
CUR = 1
LEN = 0
SV = RV(0x22eeb30) at 0x22eeb20
REFCNT = 1
FLAGS = (TEMP,ROK)
RV = 0x22f8880
SV = PVHV(0x22d7fb8) at 0x22f8880
REFCNT = 2
FLAGS = (PADMY,SHAREKEYS)
ARRAY = 0x22e99a0 (0:7, 1:1)
hash quality = 100.0%
KEYS = 1
FILL = 1
MAX = 7
RITER = -1
EITER = 0x0
Elt "x" HASH = 0x9303a5e5
SV = PV(0x22cfc88) at 0x22d1b98
REFCNT = 1
FLAGS = (POK,FAKE,READONLY,pPOK)
PV = 0x22f8450 "a"
CUR = 1
LEN = 0
Note that the FLAGS for the hash entry are "READONLY" and in fact the variable $x and the value of the corresponding value in %y are actually pointing at the same string (PV = 0x22f8450 in my example above). It seems that Perl 5.10 is trying hard to avoid copying strings, but in doing so has inadvertently locked the whole hash.
The workaround I am using is to force a string copy, like this:
foreach my $k (keys %h) {
my $j = "$k";
doSomething(1, 2, otherOption=>$j);
}
This seems an inefficient way to force a string copy, and in any case is easy to forget, so other answers containing better workarounds are welcome.

Randomizing a list using Rand in perl

Hi i currently am using the List::Util shuffle to randomize a array with CGI however I want to modify the code to use rand instead
here is my code
print "Content-type: text/html\n\n";
use List::Util qw(shuffle);
#haikuone = ('behind', 'the', 'red', 'barn');
#haikutwo = ('prairie', 'grasses', 'reclaiming');
#haikuthree = ('the', 'basketball', 'court');
#randomize1 = shuffle(#haikuone);
#randomize2 = shuffle(#haikutwo);
#randomize3 = shuffle(#haikuthree);
print "<html>\n";
print "<head><title>Haiku_Random</title></head>\n";
print "<body>\n";
print "<pre>\n";
print "RANDOM HAIKU (DISCLAIMER: NONSENSE MAY OCCUR)\n";
print "#randomize1\n";
print "#randomize2\n";
print "#randomize3\n";
How would i modify this code to use rand instead of List::Util
I dont think its much but a novice here
I'm trying to get this working
$haikuone = ('behind', 'the', 'red', 'barn');
$haikutwo = ('prairie', 'grasses', 'reclaiming');
$haikuthree = ('the', 'basketball', 'court');
#random1 = $line1[rand #haikuone];
#random2 = $line2[rand #haikutwo];
#random3 = $line3[rand #haikuthree];
print "RANDOM HAIKU (DISCLAIMER: NONSENSE MAY OCCUR)\n";
print "$line1\n";
Now when i do this
#!/usr/local/bin/perl
#haikuone = ('behind', 'the', 'red', 'barn');
#haikutwo = ('prairie', 'grasses', 'reclaiming');
#haikuthree = ('the', 'basketball', 'court');
#random1 = $line1[rand #haikuone];
#random2 = $line2[rand #haikutwo];
#random3 = $line3[rand #haikuthree];
print "RANDOM HAIKU (DISCLAIMER: NONSENSE MAY OCCUR)\n";
print "#haikuone\n";
It will print haikuone but it wont randomize it
sub fisher_yates_shuffle {
my $deck = shift; # $deck is a reference to an array
return unless #$deck; # must not be empty!
my $i = #$deck;
while (--$i) {
my $j = int rand ($i+1);
#$deck[$i,$j] = #$deck[$j,$i];
}
}
my #randomize1 = #haikuone;
fisher_yates_shuffle(\#randomize1);
print "#randomize1\n";
Always use use strict; use warnings;! You have the following code, but don't have any arrays named #haikuone, #haikutwo, #haikuthree, #line1, #line2 or #line3.
#random1 = $line1[rand #haikuone];
#random2 = $line2[rand #haikutwo];
#random3 = $line3[rand #haikuthree];
It's also really weird that use three arrays with one element each.
Hi i currently am using the List::Util shuffle to randomize a array
with CGI
This makes sense. List::Util::shuffle() is the best way to shuffle a list in Perl - whether or not you're writing a CGI program.
however I want to modify the code to use rand instead
This doesn't make sense. rand() doesn't shuffle a list. It just generates a random number.
It's a good idea to use rand() to extract a single random element from an array.
my $random_element = #array[rand #array];
But that's not what you're trying to do.
If you really want to use rand() then you need to incorporate its use in a function. There's a good function given in the Perl FAQ (in the answer to the question - "How do I shuffle an array randomly?" - so perhaps you should have taken a look at the FAQ before asking here) which looks like this:
sub fisher_yates_shuffle {
my $deck = shift; # $deck is a reference to an array
return unless #$deck; # must not be empty!
my $i = #$deck;
while (--$i) {
my $j = int rand ($i+1);
#$deck[$i,$j] = #$deck[$j,$i];
}
}
But note that this is implemented in Perl. The shuffle() function in List::Util is written in C, so it's going to be faster.
So, all in all, there's really no good reason for not using List::Util::shuffle().

Why do I get an error when I try to use the reptition assignment operator with an array?

#!/usr/bin/perl
use strict;
use warnings;
my #a = qw/a b c/;
(#a) x= 3;
print join(", ", #a), "\n";
I would expect the code above to print "a, b, c, a, b, c, a, b, c\n", but instead it dies with the message:
Can't modify private array in repeat (x) at z.pl line 7, near "3;"
This seems odd because the X <op>= Y are documented as being equivalent to X = X <op> Y, and the following code works as I expect it to:
#!/usr/bin/perl
use strict;
use warnings;
my #a = qw/a b c/;
(#a) = (#a) x 3;
print join(", ", #a), "\n";
Is this a bug in Perl or am I misunderstanding what should happen here?
My first thought was that it was a misunderstanding of some subtlety on Perl's part, namely that the parens around #a made it parse as an attempt to assign to a list. (The list itself, not normal list assignment.) That conclusion seems to be supported by perldiag:
Can't modify %s in %s
(F) You aren't allowed to assign to the item indicated, or otherwise try to
change it, such as with an auto-increment.
Apparently that's not the case, though. If it were this should have the same error:
($x) x= 3; # ok
More conclusively, this gives the same error:
#a x= 3; # Can't modify private array in repeat...
Ergo, definitely a bug. File it.
My guess is that Perl is not a language with full symbolic transformations. It tries to figure out what you mean. If you "list-ify" #a, by putting it in parens, it sort of loses what you wanted to assign it to.
Notice that this does not do what we want:
my #b = #a x 3; # we'll get scalar( #a ) --> '3' x 3 --> '333'
But, this does:
my #b = ( #a ) x 3;
As does:
( #a ) = ( #a ) x 3;
So it seems that when the expression actally appears on both sides Perl interprets them in different contexts. It knows that we're assigning something, so it tries to find out what we're assigning to.
I'd chalk it up to a bug, from a very seldom used syntax.
The problem is that you're trying to modify #a in place, which Perl evidently doesn't allow you to do. Your second example is doing something subtly different, which is to create a new array that consists of #a repeated three times, then overwriting #a with that value.
Arguably the first form should be transparently translated to the second form, but that isn't what actually happens. You could consider this a bug... file it in the appropriate places and see what happens.