Perl explicit package in nested loops - perl

I declared this array:
my #array
And in this code block...
for (my $i=0; $i<$j; $i++) {
do {
// stdout operations
} while (! ($arr != 1 ));
}
The error is specifically in the } while (! ($arr != 1 )); line.
Here's the specific error:
Global symbol "$arr" requires explicit package name at exer4bernal.pl line 71.
Why do I have this problem in 2 levels of nested loops? I never had this in only 1 level. What should I change to fix this? Thanks!

What you are seeing is totally unrelated to nesting of loop. What Perl is trying to tell you is that it doesn't know about the $arr variable. Did you mean #array or $#array?

Normally, you shouldn't be using do...while blocks. What is $arr? Where is that value declared? Where is it changed in your while loop? What is $j?
Actually, what are you trying to do with a double loop? This is usually considered bad programming because when you move from processing x elements to y elements, you increase your processing time by y2- (x * y);
Maybe this is more what you mean?
for my $index ( (0..$#array) ) {
next if $array[$index] = 1;
...
}
Note I got rid of that ugly C Style for loop and replaced it with one that's easier to understand.

Related

C-style loops vs Perl loops (in Perl)

I feel like there is something I don't get about perl's looping mechanism.
It was my understanding that
for my $j (0 .. $#arr){...}
was functionally equivalent to:
for(my $i=0; $i<=$#arr; $i++){..}
However, in my code there seems to be some slight differences in the way they operate. specifically, the time in which they decide when to terminate. for example:
assume #arr is initialized with one variable in it.
These two blocks should do the same thing right?
for my $i (0 .. $#arr)
{
if(some condition that happens to be true)
{
push(#arr, $value);
}
}
and
for (my $i=0; $i<=$#arr; $i++)
{
if(some condition that happens to be true)
{
push(#arr, $value);
}
}
In execution however, even though a new value gets pushed in both cases, the first loop will stop after only one iteration.
Is this supposed to happen? if so why?
EDIT: Thank you for all of your answers, I am aware I can accomplish the same thing with other looping mechanisms. when I asked if there was a another syntax, I was specifically talking about using for. Obviously there isn't. as the syntax to do what I want is already achieved with the c style. I was only asking because I was told to avoid the c style but i still like my for loops.
$i<=$#arr is evaluated before each loop while (0 .. $#arr) is evaluated once before any loop.
As such, the first code doesn't "see" the changes to the #arr size.
Is there another syntax I can use that would force the evaluation after each iteration? (besides using c-style)
for (my $i=0; $i<=$#arr; $i++) {
...
}
is just another way of writing
my $i=0;
while ($i<=$#arr) {
...
} continue {
$i++;
}
(Except the scope of $i is slightly different.)
An alternative would be the do-while construct, although it is a little ungainly.
my $i;
do {
push #arr, $value if condition;
} while ( $i++ < #arr );

Is there a cleaner way to conditionally 'last' out of this Perl loop?

Not really knowing Perl, I have been enhancing a Perl script with help from a friendly search engine.
I find that I need to break out of a loop while setting a flag if a condition comes true:
foreach my $element (#array) {
if($costlyCondition) {
$flag = 1;
last;
}
}
I know that the nicer way to use 'last' is something like this:
foreach my $element (#array) {
last if ($costlyCondition);
}
Of course, this means that while I can enjoy the syntactic sugar, I cannot set my flag inside the loop, which means I need to evaluate $costlyCondition once again outside.
Is there a cleaner way to do this?
you can use a do {...} block:
do {$flag = 1; last} if $costlyCondition
you can use the , operator to join the statements:
$flag = 1, last if $costlyCondition;
you can do the same with the logical && operator:
(($flag = 1) && last) if $costlyCondition;
or even the lower priority and:
(($flag = 1) and last) if $costlyCondition;
at the end of the day, there's no real reason to do any of these. They all do exactly the same as your original code. If your original code works and is legible, leave it like it is.
I agree with Nathan, that while neat looking code is neat, sometimes a readable version is better. Just for the hell of it, though, here's a horrible version:
last if $flag = $costly_condition;
Note the use of assignment = instead of equality ==. The assignment will return whatever value is in $costly_condition.
This of course will not make $flag = 1, but whatever $costly_condition is. But, since that needs to be true, so will $flag. To remedy that, you can - as Zaid mentioned in the comments - use:
last if $flag = !! $costly_condition;
As mentioned, pretty horrible solutions, but they do work.
One thought is to do the loop in a subroutine that returns different values depending on the exit point.
my $flag = check_elements(\#array);
# later...
sub check_elements {
my $arrayref = shift;
for my $ele (#$arrayref) {
return 1 if $costly_condition;
}
return 0;
}
This is possible, but highly not recommended: such tricks decrease readability of your code.
foreach my $element (#array) {
$flag = 1 and last if $costlyCondition;
}

Is for(1) always the same as do in perl?

for(1){
print 1;
}
do {
print 1;
}
Is it true?
Or is there any special case these two doesn't equal?
One difference is that for(1) sets $_ to the value of 1, as well:
for(1){
print $_; # prints 1
}
Also, do returns the value of the last command in the sequence:
my $x = do { 1 }; # $x = 1
my $y = for(1){ 1 }; # invalid
You might really be looking for just plain curlies.
{
print 1;
}
It has the following benefits:
Creates a lexical scope (like for (1) and do {}).
You can use next, last and redo in them (like for (1)).
It doesn't mask $_ (like do {}).
But
It can only used where a statement is expected (like for (1), but unlike do {}).
Therefore, { ... } makes more sense than for (1) { ... }, and do { ... } is useful when you want to return a value.
About the same.
You can next, last and redo a for loop, but a do is not a loop--including as part of a do-while "loop". So in a non-trivial block, you couldn't be sure. However, this will work:
do {{
...
}};
Also do will not automatically set $_ to each member of the list, the way a bare for loop will.
No. They have different compilation properties and have different effects. They are similar in only one dimension, that being that the code they introduce will not be looped over -- something they have in common with other constructs, including bare blocks and (sub {...})->().
Here's an obvious difference: for (LIST) BLOCK is a loop, whereas do BLOCK is an expression. This means that
for (1) {
say "Blurgh"
} unless 1;
doesn't compile, whereas
do {
say "Blurgh"
} unless 1;
does.

Mysterious "uninitialized value in an array" in algorithm for Perl challenge

Currently learning Perl, and trying to solve a little challenge to find the sum of even terms from the first 4,000,000 Fibonacci terms. I have created a Fibonacci array that seems to work, and then tried different methods to throw out the odd-valued terms, and continually run into an error when trying to sum my resulting array, getting reports of:
Use of uninitialized value in addition (+) at prob2_3.plx line 23
Here is what I have:
#!/usr/bin/perl -w
# prob2_2.plx
use warnings;
use strict;
my #fib; my $i; my $t; my $n;
#fib = (1, 2);
for ($i=2; $i<4000000; $i++) {
my $new= ( $fib[$i-1] + $fib[$i-2] );
push #fib, $new;}
for ($t=3; $t<4000000; $t++) {
if (($fib[$t] % 2) != 0 ) {
delete $fib[$t]; } }
my $total = 0;
for ($n=1; $n<$#fib; $n++) {
$total += $fib[($n+1)];}
print $total;
The warning means you are adding undef to something. delete $fib[$t]; is a bad way of doing $fib[$t] = undef;, which you later add to $total.
You have at least one other error:
The first two Fibonacci numbers are 0 and 1, not 1 and 2.
You have a major problem:
The 4,000,000th Fib number is going to be extremely large, much too large to fit in a double.
For reference purposes,
10,000th has 2090 digits: 20793608237133498072112648988642836825087036094015903119682945866528501423455686648927456034305226515591757343297190158010624794267250973176133810179902738038231789748346235556483191431591924532394420028067810320408724414693462849062668387083308048250920654493340878733226377580847446324873797603734794648258113858631550404081017260381202919943892370942852601647398213554479081823593715429566945149312993664846779090437799284773675379284270660175134664833266377698642012106891355791141872776934080803504956794094648292880566056364718187662668970758537383352677420835574155945658542003634765324541006121012446785689171494803262408602693091211601973938229446636049901531963286159699077880427720289235539329671877182915643419079186525118678856821600897520171070499437657067342400871083908811800976259727431820539554256869460815355918458253398234382360435762759823179896116748424269545924633204614137992850814352018738480923581553988990897151469406131695614497783720743461373756218685106856826090696339815490921253714537241866911604250597353747823733268178182198509240226955826416016690084749816072843582488613184829905383150180047844353751554201573833105521980998123833253261228689824051777846588461079790807828367132384798451794011076569057522158680378961532160858387223882974380483931929541222100800313580688585002598879566463221427820448492565073106595808837401648996423563386109782045634122467872921845606409174360635618216883812562321664442822952537577492715365321134204530686742435454505103269768144370118494906390254934942358904031509877369722437053383165360388595116980245927935225901537634925654872380877183008301074569444002426436414756905094535072804764684492105680024739914490555904391369218696387092918189246157103450387050229300603241611410707453960080170928277951834763216705242485820801423866526633816082921442883095463259080471819329201710147828025221385656340207489796317663278872207607791034431700112753558813478888727503825389066823098683355695718137867882982111710796422706778536913192342733364556727928018953989153106047379741280794091639429908796650294603536651238230626
20,000th has 4180 digits: 1564344347109763849734765364072743458162050946855915883181245417404580803852433819127477934504143316103671237797087184052487157589846395314335101792632666883301188491698850377253383735812017943059782268835280360618754466932406192674904182868594738499500415166599602737300793712012046275485369495600019495004126039595217556097603510836899682827827626851274417838565958464881549888154511565687715162081527027421167926710592169405764372872023265791851279526521097739802047796738013885512616267273220024096214780132567479711643567372517808245262560562426651659391013837988476506124649092538307827326285964637268328029765707984607120961599796336714632362497169952413163370558311283612961033588836334352432860332222878648950508154331165678617373097939647648015552782638392654938551724289386017566932982065441392025369213734676739845068956966278536757235977421127565055467060906533383001625925978595472181091151062798507286798754728450358266089744616465914255799764431508559485853637841082521780322710748029546001980460990695999087046617731317608498316428164179967150350939374702201821818895349621858954893061034598954341939850973673870946183079728029105624782161827626661367017673681922257604178810154438462080217794489109678386881153826838075832058191153133704042628156419344516917867369755345135618986917642004521509538436204298618130363401395547933177643760161135638357088649014469358006518300404036431113143777969391584246934245800739809135619744598808977628245309941537928439431608665523308894967310600529498446943933665468406306292762942409786097847875240014036353917928156220446650579514092031254308059314931618726692376640987446459276331196950780063664171751110087644649773058213117640640085100552927878404516279461437503857017398937097042607258059612257878307007002086913210922626760728342901272768408974906007921227446242552261362505471751722906558235533709070548109789519920405521647836164156675304784097782435865165640401897107828859121831521126567446611716077075769257072773697947064329836969249852382976202348037425889031090020976240691949742160088733357875561841760194799534815496104106903184713919847662253483806138312440578732122855388348848736018217032877013531004653902335692761900988709302797685265501972628217528866551995479526195626503247164073793787381643388365618488630255600890924552511767690989186316859159306438477097458585889829326938198129884953178437411315486719927412151054551726325421747462698125767761987300812744880048122138953746796038485281452086680809803469350470844184375258620810652745992631459076192613797545486775651410699327289089628593588395142531659083933746399666161863597357735290387376161440280731398703030590410957840047591721635117677190494658658256770952605314604687704388833897300447300322491720569722311756874534871145435101596346787454258165870310592717473670917638475152605474446188958081898150393481484970581519902582271877141251593259282483539345792009117894084860435326938689664322383123823631494470354941767039585133484331342468806167901166928052638999423311570618981137348891538818027216596300491989181231598151123614651043205656474490923109982595235880446420678700336717534914381729578113169753046083981752465156933790288020841880688083888166659362896648911608716373579944854235997384986302902608821566689026676371268703303207406827737925274781301986480762462594420398637607893961010824979395439225300832931626540179218558345947558472159906873998923767432504278838419479068093778976997276416592421223235719653905071392295735398272851826350645605643470417155719500185143594804374322010189545136205568856276559806316789533450612097900180399440915139647060459321993254566103255011590902408116018722996267956826555434955409390951728022815209412027248353062982911544674007147249326697275010788100666958314965810320432736615962898175585320993128871046552842068867557341007383399180807449030159797672605530835244157256109268527578172314358255179589605335375414082046575557122636364391407861922824529441261003866098066404526541912783214030236752423547997110159548536582622929575859635210831021463323632502412193578592457118234067116894159316798758933206918936334540039454055299101076302263831614132510576874528929742319396129011617501
Even the 10,000th is too large for a double, and the 20,000th is double the size of the 10,000th, so imagine how large the 4,000,000th will be!
Stylistic issues:
my $i; for ($i=2; $i<4000000; $i++)
is much harder to read than
for my $i (2..$N-1)
with the following at the top to avoid having to repeat the number everywhere:
my $N = 4_000_000;
As if the fact that the 4 millionth Fibonacci number is more than 10^835950 isn't a big enough problem, this is not very good:
for ($t=3; $t<4000000; $t++) {
if (($fib[$t] % 2) != 0 ) {
delete $fib[$t]; } }
my $total = 0;
for ($n=1; $n<$#fib; $n++) {
$total += $fib[($n+1)];}
Why are you walking through the list twice here? Much better would be to combine the two loops into one. You want the sum of the odd terms, so sum the odd terms. Don't delete the odd terms (stylistically very bad) and then walk over the list again, relying on the fact that undef has a numerical value of 0 (but only with a warning).
And mn, the formatting of that code is very, very ugly. Eventually you will write code that someone else needs to read or maintain. My motto: Imagine that the person who will maintain your code is a psychopath who knows where you live.
As ikegami points out, your uninitialized problem is assuming delete removes elements from an array, when in fact it just sets them to undef (unless they are at the end of the array).
Given the storage requirements of the larger Fibonacci numbers, you don't want them in an array at all; fortunately, there's no need to keep them around for this problem.
I would do it like this (takes many minutes to run):
use strict;
use warnings;
use Math::BigInt 'lib' => 'GMP';
my $fib_A = Math::BigInt->new(0);
my $fib_B = Math::BigInt->new(1);
my $sum = Math::BigInt->new(0);
# get the next 3999998
for (1..(4000000-2)) {
my $next = $fib_A + $fib_B;
$sum += $next if $next % 2 == 0;
($fib_A, $fib_B) = ($fib_B, $next);
}
print "The sum is $sum\n";

Perl: Can I access two different levels of an array of hashes at the same time by using indices?

I'm completely new to Perl and I need to write a program that clusters found matches if they are at a certain distance from each other. So I got an array of hashes containing on each level the begin position, the end position and the number of matches present in a cluster(1 in the beginning).
If I want to know if the distance between two matches is ok, I do Begin2-End1
my $DEBUG = 1;
my #hitsarray =();
my ($beginarray,$endarray,$aantalarray);
my $hit = { BEGIN => $beginarray, EIND => $endarray, MATCHES => $aantalarray, };
for (my $k = 0;$k <= $#beginarray;$k++)
{
print $beginarray[$k],"\t",$endarray[$k],"\t",$aantalarray[$k],"\n" if ($DEBUG);
$hit = ();
$hit->{BEGIN} = $beginarray[$k];
$hit->{END} = $endarray[$k];
$hit->{MATCHES} = $aantalarray[$k];
push (#hitsarray,$hit);
}
for ( my $m = 0; $m <= $#hitsarray; $m++)
{
while($hitsarray[$m+1]{BEGIN} - $hitsarray[$m]{END} < 5 && $hitsarray[$m+1]{BEGIN} - $hitsarray[$m]{END} > 3)
{
$hitsarray[$m]{END} = $hitsarray[$m+1]{EIND};
$hitsarray[$m]{MATCHES} +=1;
delete $hitsarray[$m+1];
print $beginarray[$m],"\t",$endarray[$m],"\t",$aantalarray[$m],"\n" if ($DEBUG);
}
}
But it doesn't work! My pc gets in a loop and states "Use of uninitialized value in subtraction (-) at script line 55."
It probably has to do something with using references but I don't really understand those..
I also tried a simpler structure with two non-connected arrays but I've got the same problem;
How do you use elements from different lines (and from different arrays) for subtraction?
Any help is totally welcome!!
I know that this might not seem to be the most helpful, but your code is so wrong that there is not a single problem or a single correction. Here are some of the problems.
put use warnings; use strict; at the top of your script.
$beginarray, $endarray and $aantalarray are all scalars, not arrays. You might want them to be references to arrays, but they aren't because you never assign them. NOTE: when you do $beginarray[$m] that is referencing an array variable called #beginarray which is the same name but actually a different variable from $beginarray which is a scalar.
You aren't showing us everything if you are having a problem on line 55
this, $hit = (); actually just sets the SCALAR variable $hit to 0 because that is the length of the array ().
$#beginarray is going to be -1 because #beginarray is not declared. Even if you changed your code to declare it, it still has no data so the first look won't run.
delete $hitsarray[$m+1] will remove that value from the array, but that index will just be empty, the items above it will not move in the array. To remove an item from an array you need either to grep into a new array or splice the existing array.
You need to make a much smaller example or working with arrays to figure out what you are doing wrong.
for ( my $m = 0; $m <= $#hitsarray; $m++)
{
while($hitsarray[$m+1]{BEGIN} - $hitsarray[$m]{END} < 5 && $hitsarray[$m+1]{BEGIN} - $hitsarray[$m]{END} > 3)
Here you are using element $m+1, which is beyond the end of the array on the final for iteration. Perhaps your for loop should say $m < $#hitsarray.