Misunderstanding of incrementation - perl

In Perl, I have this following code:
my $val = "0";
for(my $z = 0; $z <= 14; $z++)
{
++$val;
if($val == 9) {
$val = "A";
}
print $val;
}
it prints:
1 2 3 4 5 6 7 8 A B 1 2 3 4 5
yet it's supposed to continue from B to C, from C to D and so on, what is the logic behind this?

warnings would have given you a warning message like:
Argument "B" isn't numeric in numeric eq (==)
use warnings;
use strict;
my $val = "0";
for(my $z = 0; $z <= 14; $z++)
{
++$val;
if($val eq '9') { # <------------------
$val = "A";
}
print $val;
}

To quote perlop:
If you increment a variable that is numeric, or that has ever been used in a numeric context, you get a normal increment. If, however, the variable has been used in only string contexts since it was set, and has a value that is not the empty string and matches the pattern /^[a-zA-Z]*[0-9]*\z/, the increment is done as a string, preserving each character within its range, with carry... (emphasis added)
$val == 9 is a numeric context. So it prints A (you just set it), and then you get the magic increment to B (it hasn't been used in a numeric context yet), but then you hit the == (using it in a numeric context), so when you get to ++$val again B is treated as a number (0) and increments to 1.
You could use eq to make a string comparison, thus preserving the magic increment, but you could also just say:
print 1 .. 8, 'A' .. 'F';

Related

Perl: Where am I going wrong with postfix/prefix precedence?

The following fragment shows a simple n-perline output procedure.
Two cases are shown, one using prefix ++, the other postfix ++, in a boolean expression.
Since '++' has higher precedence than '==', I expected the results to be the same, but they are not: one does 5 per line, the other 6.
use English;
my #arr = (1,2,3,4,5,6,7,8,9,8,7,6);
my $perline = 5;
my $ndone = 0;
for(#arr) {
print " $ARG";
if(++$ndone == $perline) {
$ndone = 0;
print "\n";
}
}
print "\n---\n";
my $perline = 5;
my $ndone = 0;
for(#arr) {
print " $ARG";
if($ndone++ == $perline) {
$ndone = 0;
print "\n";
}
}
Output:
1 2 3 4 5
6 7 8 9 8
7 6
---
1 2 3 4 5 6
7 8 9 8 7 6
This is not about precedence of operations but about what prefix and postfix ++ return. From perldoc perlop:
"++" and "--" work as in C. That is, if placed before a variable, they
increment or decrement the variable by one before returning the value, and
if placed after, increment or decrement after returning the value.
Essentially you could define these as functions:
sub prefix_plusplus {
$_[0] = $_[0] + 1; # increment value
return $_[0]; # returns value after increment
}
sub postfix_plusplus {
my $before = $_[0];
$_[0] = $_[0] + 1; # increment value
return $before; # returns value before increment
}
my $x = my $y = 5;
printf "%d,%d\n", prefix_plusplus($x), postfix_plusplus($y); # 6,5
printf "%d,%d\n", $x, $y; # 6,6
# and same thing with the ++ operand
$x = $y = 5;
printf "%d,%d\n", ++$x, $y++; # 6,5
printf "%d,%d\n", $x, $y; # 6,6

Perl: Create a binary number and convert it into hex

I want to create a binary number from the given user input.
Input - Array of number
Output - Binary number
A binary number should be created such that it has one on all the places which has been given as input.
In the given case input is 1, 3, and 7 so my binary no should be 1000101, so it has 1's on 1, 3 and 7 places from left.
#x = [ 1, 3, 7 ];
$z = 0;
for( $i = 0; $i < 10; $i++ ){
foreach $elem ( #x ){
if( $elem == $i ){
join( "", $z, 1 );
}
else{
join( "", $z, 0 );
}
}
}
print "Value of z: $z";
After execution, I am getting the value of z as 0.
I need to convert this binary to hexadecimal.
Is there some function which converts binary to hexadecimal?
[ ] creates an array and returns a reference to that array, so you are assigning a single scalar to (poorly named) #x.
You are also misusing join. Always use use strict; use warnings qw( all );! It would have caught this error.
Fixed:
my #bits = ( 1, 3, 7 );
my $num = 0;
$num |= 1 << $_ for #bits;
# 76543210
printf("0b%b\n", $num); # 0b10001010
printf("0x%X\n", $num); # 0x8A
It seems that you want 0b1000101, so we need to correct the indexes.
my #bits_plus_1 = ( 1, 3, 7 );
my $num = 0;
$num |= 1 << ( $_ - 1 ) for #bits_plus_1;
# 6543210
printf("0b%b\n", $num); # 0b1000101
printf("0x%X\n", $num); # 0x45
A few problems:
#x = [ 1, 3, 7 ]; is not an array of three integers. It's an array containing a single array reference. What you want is round brackets, not square brackets: #x = ( 1, 3, 7 );
The string returned by join is not assigned to $z
But even then your code is buggy:
it appends a bit at the end of $z, not the beginning
there's a trailing zero that has no business being there.

Matching a zero with String::Substitution

I am trying to match a digit with String::Substitution; it works fine if the digit is not zero. If the digit is zero it substitutes the empty string instead of the digit. For example:
use strict;
use warnings;
use Data::Dump;
use String::Substitution;
my #data = qw(0 1);
for (#data) {
my $str = $_;
my $regex = qr/(\d)/;
my $replace = '$1';
my $result_str = String::Substitution::gsub_copy($str, $regex, $replace);
my #m = $str =~ /$regex/g;
dd $result_str;
dd #m;
}
The output is:
""
0
1
1
expected output would be:
0
0
1
1
To avoid "uninitialized" warnings, version 1.001 of the module attempts to convert undefined placeholders into empty strings. However, it erroneously uses a truth test rather an defined test to determine which values to replace with an empty string.
map { ($$_) || '' } ( 1 .. $#- )
That code needs to be changed to
map { defined($$_) ? $$_ : '' } ( 1 .. $#- )
A bug report has been submitted.

Why we can not use "le" in for loop and can use in "if condition"

I've made this program as a beginner. Need to clarify something!
Why I m not able to use "le" in a for loop given below but I'm able to use it in "if condition". What is the reason behind that. ?
print "Type the number upto series should run\n";
my $var;
$var = int<STDIN>;
chomp $var;
my ($i, $t);
my $x = 0;
my $y = 1;
if($var eq 1) {
print "\nYour Series is : $x\n";
} elsif($var eq 2){
print "\nYour Series is : $x $y\n";
} elsif($var ge 2) {
print "Your Series is : $x $y ";
for($i = 1; $i le $var - 2; $i++) {
# Why the condition not working when I'm using "le"
# but it does work when I'm using "<="
$t = $x + $y;
$x = $y;
$y = $t;
print "$t ";
}
print "\n";
} else {
print "Error: Enter a valid postive integer\n";
}
You are free to use le and <= as you like. But you should be aware that they are completely different operators.
Numeric comparision operators are
== != < <= > >= <=>
The equivalent string comparision operators are
eq ne lt le gt ge cmp
Strings and numbers are converted to each other as needed. This means for example that
3 ge 20 # true: 3 is string-greater than 20
11 le 2 # true: 11 is string-less-or-equal than 2
because lexicographic ordering compares character by character. Using the numeric operators when you want to treat the contents of your $variables as numbers is therefore preferable and will yield the correct results.
Note that Perl translates between strings and numbers invisibly. It is advisable to use warnings, so that you get a helpful message when a string can't represent a number (e.g. "a").
The correct answer has been given, that ge does string comparison, where for example 11 is considered less than 2. The solution is to use numerical comparison, == and >=.
I thought I would help demonstrate the problem you are having. Consider this demonstration of how the default sort works:
$ perl -le 'print for sort 1 .. 10'
1
10
2
3
4
5
6
7
8
9
As you can see, it considers 10 lower than 2, and that is because of string comparison, which is the default mode for sort. This is how the default sorting routine looks under the hood:
sort { $a cmp $b }
cmp belongs to the string comparison operators, like eq, le, and ge. These operators are described here (cmp is below that).
For the sort routine to do what we expect in the above example, we would have to use a numeric comparison operator, which is the "spaceship operator" <=>:
sort { $a <=> $b }
In your case, you can try out your problem with this one-liner:
$ perl -nlwe 'print $_ ge 2 ? "Greater or equal" : "Lesser"'
1
Lesser
2
Greater or equal
3
Greater or equal
11
Lesser
When you compare numbers with eq, le, gt..... etc; they first be converted to string. And strings will be checkd for alphabatical order, so "11" will be less then "2" here.
So you should be using ==,<=,>= ...etc when comparing numbers.
I thought you may like to see a more Perl-like program that produces the Fibonnaci series like yours.
use strict;
use warnings;
print "Type the length of the series\n";
chomp(my $length = <>);
unless ($length and $length !~ /\D/ and $length > 0) {
print "Error: Enter a positive integer\n";
}
print "\n";
my #series = (0, 1);
while (#series < $length) {
push #series, $series[-2] + $series[-1];
}
print "#series[0..$length-1]\n";

How should I use Perl's scalar range operator?

What is the scalar ".." operator typical usage? Is it only selecting blocks of text?
Interesting example by myself:
sub get_next {
print scalar($$..!$$), "\n";
}
get_next for 1 .. 5; # prints numbers from 1 to 5
get_next for 1 .. 5; # prints numbers from 6 to 10
People hardly seem to know about it based on questions here, but, yes, I'd say typical usage is selecting blocks of text, either with
while (<>) {
print if /BEGIN/ .. /END/;
}
or
while (<>) {
print if 3 .. 5; # prints lines 3 through 5
}
The latter is syntactic sugar for checking against the input line-number ($.)
... if $. == 3 .. $. == 5;
which suggests the weird-looking
#! /usr/bin/perl -l
for ($_ = 1; $_ <= 10; ++$_) {
print if $_ == 4 .. $_ == 7;
}
The above program's output is
4
5
6
7
If you have some sort of bracketing condition, test for it in subs:
for (...) {
do_foo($x,$y,$z) if begin($x) .. end($z);
}
Outside of perl -e you really shouldn't. It is esoteric and funky. See my post not even 24hrs ago about it about how it maintains state with calling context. This stung me in a real world application, because I was trying to be clever and found what I thought was a good use-case for it.
Here's a place where you need to be very careful about unintentional use of the scalar range operator: subroutine returns.
sub range {
my $start = shift;
my $end = shift;
return $start .. $end;
}
#foo = range( 1, 5 ); # ( 1, 2, 3, 4, 5 )
$foo = range( 1, 5 ); # False or maybe true. Who knows.
If you call this subroutine in scalar context, you'll be in for a surprise.
After being bitten by some variant of this problem, I now always make sure I assign a list return into an array, thereby getting array-like context behaviors for my subs. If I need other context specific behavior (very rarely) I use wantarray.
sub range {
my $start = shift;
my $end = shift;
my #result = $start .. $end;
return #result;
}
another use is simple counters like this:
perl -e 'foreach ( 1 .. 100 ){print"$_\n"}'