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

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

Related

to change the value of an element of an array by using the index ref in perl

#!/bin/usr/perl -w
use strict;
print "Enter your input filename for original sample data values: \n";
chomp($data=<STDIN>);
print "Enter your input filename for adjustment values\n";
chomp($adj=<STDIN>) ;
print "Enter your output filename for resultant adjusted new sample data \n";
chomp($new=<STDIN>);
open(R1,"$data") or die("error");
open(R2,"$adj") or die ("error");
open(WW,"+>$new") or die ("error");
while( ($line1=(<R1>)) && ($line2=(<R2>)) )
{
$l1=$line1;
#arr1= split(" ",$l1);
$l2=$line2;
#arr2= split(" ",$l2);
$l= ( scalar#arr1);
$p= (scalar#arr2);
for ( $i = 0; $i <= $l; $i++ ){
for ( $j =($i+1); $j <= $l; $j++ ){
if ($arr1[$i]< $arr1[$j]){
$a = $arr1[$i] + ($arr2[$i]/2);
$b = $arr1[$j] - ($arr2[$i]/2);
push ( $arr1[$i]->$a , $arr1[$j]->$b);
}
elsif ( $arr1[$i]= $arr1[$j]){
$a = $arr1[$i];
$b = $arr1[$j];
push ($arr1[$i]->$a,$arr1[$j]->$b);
}
else{
$a = $arr1[$i]-($arr2[$i]/2);
$b = $arr1[$j]+ ($arr2[$i]/2);
push ($arr1[$i]->$a,$arr1[$j]->$b);
}
}
}
$l1 = scalar#arr1;
for ($k = 0; $k <= $l1 ; $k++)
{
if (($k % 10) != 0){
print WW "$arr1[$k]";
print WW "\t" ;
}
else {
print WW "\n";
print WW "$arr1[$k]";
print WW "\t";
}
}
}
close(R1);
close(R2);
close(WW);
exit;
when i am running this prog. i am getting an error that "not an ARRAY reference at line 29".
how can i create the reference to my first array #arr1 ??? so that it stores the changed values of the element at the particular index after running the iteration.
input :
#array1
1 2 3 4 5 6 7 8 9 10
#array2
1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2
desired output
#array1
15 1.5 2 3 6 4 11.5 5 5.5
Well, I'm not getting the answer you say you're looking for, but what it appears you're trying to do is to store the value of $a into the $i'th index of array #arr1 and the value of $b into the $jth index of #arr1. I have hoisted the assignment code out of the if branches since it will be the same for all three cases. I have also fixed a subtle error you had in your conditions. You had
elsif ( $arr1[$i]= $arr1[$j]){
but you surely meant to do an equality comparison rather than an assignment here:
elsif ( $arr1[$i] == $arr1[$j]){
So here is the modified section. As I say, it still doesn't print out what you say the desired result is, and I'm not sure whether it's because your computation is wrong or your printing is wrong (I couldn't figure out any obvious transform from your inputs to your desired output), but this should at least put you in the right direction:
for ( $i = 0; $i <= $l; $i++ ){
for ( $j =($i+1); $j <= $l; $j++ ){
if ($arr1[$i]< $arr1[$j]){
$a = $arr1[$i] + ($arr2[$i]/2);
$b = $arr1[$j] - ($arr2[$i]/2);
# push ( $arr1[$i]->$a , $arr1[$j]->$b);
}
elsif ( $arr1[$i] == $arr1[$j]){
$a = $arr1[$i];
$b = $arr1[$j];
# push ($arr1[$i]->$a,$arr1[$j]->$b);
}
else{
$a = $arr1[$i]-($arr2[$i]/2);
$b = $arr1[$j]+ ($arr2[$i]/2);
# push ($arr1[$i]->$a,$arr1[$j]->$b);
}
$arr1[$i] = $a;
$arr1[$j] = $b;
}
}

Cloning a generator in perl (Coro::Generator package)

I would like to clone a generator (Coro::Generator) state in Perl. For example: in the bellow code I have a generator which iterates over an array:
use Coro::Generator;
sub iterate_array {
my #arr = #{ $_[0] };
generator {
foreach my $value (#arr) {
# Each time function is called it returns the next value in the array
yield $value;
}
yield undef;
};
}
my #a = (1,2,3,4);
my $iterator = iterate_array(\#a);
print $iterator->(); # output: 1
So the output is 1. If called again it will be 2 (2nd item in array).
I now want to clone the state of the iterator. Something like:
my $clone = clone($iterator); # Clone iterator
print $iterator->(); # output: 2
print $clone->(); # output: 2
Both $clone and $iterator produce the same results as they are clones of eachother.
I have tried using the Storable package, i.e.:
use Storable 'dclone';
my $clone = dclone($iterator);
But this gives an error:
Can't store CODE items at ...
Any help much appreciated.
The Coro::Generator module doesn't anticipate cloning an iterator:
The module itself has a rather hackish feel
Calculating the next generated item might have side effects. It is therefore not generally possible to “split” the iterator.
However, we can do this “cloning” ourself by caching the results of the iterator:
sub clone_iter {
my ($n, $iter) = #_;
my #caches;
my $make_subiterator = sub {
# each subiterator has its own cache of not-yet seen items
my $cache = [];
push #caches, $cache;
return sub {
# If *our* cache is empty…
if (not #$cache) {
my $result = $iter->();
# add this result to *all* caches
push #$_, \$result for #caches;
}
return ${ shift #$cache };
};
};
return map { $make_subiterator->() } 1 .. $n;
}
my $i = 1;
my $iter = sub { $i++ }; # just for testing
my ($iter_a, $iter_b, $iter_c) = clone_iter(3, $iter);
say join ' ', map { $iter_a->() } 1 .. 3;
say join ' ', map { $iter_b->() } 1 .. 7;
say join ' ', map { $iter_a->() } 4 .. 10;
say join ' ', map { $iter_c->() } 1 .. 7;
Sample output:
1 2 3
1 2 3 4 5 6 7
4 5 6 7 8 9 10
1 2 3 4 5 6 7

Using perl, given an array of any size, how do I randomly pick 1/4 of the list

For clarification, if I had a list of 8 elements, i would want to randomly pick 2. If I had a list of 20 elements, I would want to randomly pick 5. I would also like to assure (though not needed) that two elements don't touch, i.e. if possible not the 3 and then 4 element. Rather, 3 and 5 would be nicer.
The simplest solution:
Shuffle the list
select the 1st quarter.
Example implementation:
use List::Util qw/shuffle/;
my #nums = 1..20;
my #pick = (shuffle #nums)[0 .. 0.25 * $#nums];
say "#pick";
Example output: 10 2 18 3 19.
Your additional restriction “no neighboring numbers” actually makes this less random, and should be avoided if you want actual randomness. To avoid that two neighboring elements are included in the output, I would iteratively splice unwanted elements out of the list:
my #nums = 1..20;
my $size = 0.25 * #nums;
my #pick;
while (#pick < $size) {
my $i = int rand #nums;
push #pick, my $num = $nums[$i];
# check and remove neighbours
my $len = 1;
$len++ if $i < $#nums and $num + 1 == $nums[$i + 1];
$len++, $i-- if 0 < $i and $num - 1 == $nums[$i - 1];
splice #nums, $i, $len;
}
say "#pick";
use strict;
use warnings;
sub randsel {
my ($fact, $i, #r) = (1.0, 0);
while (#r * 4 < #_) {
if (not grep { $_ == $i } #r) {
$fact = 1.0;
# make $fact = 0.0 if you really don't want
# consecutive elements
$fact = 0.1 if grep { abs($i - $_) == 1 } #r;
push(#r, $i) if (rand() < 0.25 * $fact);
}
$i = ($i + 1) % #_;
}
return map { $_[$_] } sort { $a <=> $b } #r;
}
my #l;
$l[$_] = $_ for (0..19);
print join(" ", randsel(#l)), "\n";

Perl to count current value based on next value

Currently I'm learning Perl and gnuplot. I would like to know how to count certain value based on the next value. For example I have a text file consist of:
#ID(X) Y
1 1
3 9
5 11
The output should show the value of the unknown ID as well. So, the output should show:
#ID(X) Y
1 1
2 5
3 9
4 10
5 11
The Y of ID#2 is based on the following:
((2-3)/(1-3))*1 + ((2-1)/(3-1))*9 which is linear algebra
Y2=((X2-X3)/(X1-X3))*Y1 + ((X2-X1)/(X3-X1)) * Y3
Same goes to ID#5
Currently I have this code,
#! /usr/bin/perl -w
use strict;
my $prev_id = 0;
my $prev_val = 0;
my $next_id;
my $next_val;
while (<>)
{
my ($id, $val) = split;
for (my $i = $prev_id + 1; $i < $next_id; $i++)
{
$val = (($id - $next_id) / ($prev_id - $next_id)) * $prev_val + (($id - $prev_id) / ($next_id - $prev_id)) * $next_val;
printf ("%d %s\n", $i, $val);
}
printf ("%d %s\n", $id, $val);
($prev_val, $prev_id) = ($val, $id);
($next_val, $next_id) = ($prev_val, $prev_id);
}
Your formula seems more complicated than I would expect, given that you are always dealing with integer spacings of 1.
You did not say whether you want to fill gaps for multiple consecutive missing values, but let's assume you want to.
What you do is read in the first line, and say that's the current one and you output it. Now you read the next line, and if its ID is not the expected one, you fill the gaps with simple linear interpolation...
Pseudocode
(currID, currY) = readline()
outputvals( currID, currY )
while lines remain do
(nextID, nextY) = readline()
gap = nextID - currID
for i = 1 to gap
id = currID + i
y = currY + (nextY - currY) * i / gap
outputvals( id, y )
end
(currID, currY) = (nextID, nextY)
end
Sorry for the non-Perl code. It's just that I haven't been using Perl for ages, and can't remember half of the syntax. =) The concepts here are pretty easy to translate into code though.
Using an array may be the way to go. This will also make your data available for further manipulation.
** Caveat: will not work for multiple consecutive missing values of y; see #paddy's answer.
#!/usr/bin/perl
use strict;
use warnings;
my #coordinates;
while (<DATA>) {
my ($x, $y) = split;
$coordinates[$x] = $y;
}
# note that the for loop starts on index 1 here ...
for my $x (1 .. $#coordinates) {
if (! $coordinates[$x]) {
$coordinates[$x] = (($x - ($x + 1)) / (($x - 1) - ($x + 1)))
* $coordinates[$x - 1]
+ (($x - ($x - 1)) / (($x + 1) - ($x - 1)))
* $coordinates[$x + 1];
}
print "$x - $coordinates[$x]\n";
}
__DATA__
1 1
3 9
5 11
You indicated your problem is getting the next value. The key isn't to look ahead, it's to look behind.
my $prev = get first value;
my ($prev_a, $prev_b) = parse($prev);
my $this = get second value;
my ($this_a, $this_b) = parse($this);
while ($next = get next value) {
my ($next_a, $next_b) = parse($next);
...
$prev = $this; $prev_a = $this_a; $prev_b = $this_b;
$this = $next; $this_a = $next_a; $this_b = $next_b;
}
#! /usr/bin/perl -w
use strict;
my #in = (1,9,11);
my #out;
for (my $i = 0; $i<$#in; $i++) {
my $j = $i*2;
my $X1 = $i;
my $X2 = $i+1;
my $X3 = $i+2;
my $Y1 = $in[$i];
my $Y3 = $in[$i+1];
my $Y2 = $Y1*(($X2-$X3)/($X1-$X3))
+ $Y3*(($X2-$X1)/($X3-$X1));
$out[$j] = $in[$i];
$out[$j+1] = $Y2;
}
$out[$#in*2] = $in[$#in];
print (join " ",#out);

Misunderstanding of incrementation

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';