Perl Fibonacci number program with array references? - perl

EDIT: SOLVED. See solution below.
I wrote the following Fibonacci number program for the first 10 numbers; however I can't get it to work. It keeps telling me that Use of uninitialized value $secondLast in addition (+) at fib.plx line 22. and it runs forever. I'm a beginner Perl programmer so I'm sure the error is very simple. Thanks.
#!/usr/bin/perl
use warnings;
use strict;
sub fib(\#$);
my #defaultNums = (1,1);
my $max = 10;
fib(#defaultNums,10);
sub fib(\#$)
{
my $nums_ref = $_[0];
my $max = $_[1];
foreach(#{$nums_ref})
{
print "$_, ";
}
print "\n";
my $last = pop (#{$nums_ref});
my $secondLast = pop (#{$nums_ref});
my $sum = $last + $secondLast;
push (#{$nums_ref}, $sum);
if( scalar #{$nums_ref} >= $max) { return; }
fib (#{$nums_ref},$max);
print "\n";
}
EDIT: SOLVED. See solution below.

A few notes on your program:
It is important that you avoid using subroutine prototypes unless you are absolutely sure about what you are doing. Prototypes are primarily intended for writing replacements for Perl built-in operators, and that is something that is rarely required. You should explicity pass a reference to an array by writing the call as fib(\#defaultNums, 10).
The first two values in the Fibonacci sequence are zero and one. You have seeded your sequence with the second and third values which, while it will work fine, isn't mathematically correct.
Because Perl is sensitive to context, you can remove the scalar call to compare the number of elements in the array. You can also used the if statement modifier to avoid a lot of noise, so your return line becomes return if #{$nums_ref} >= $max.
Your chosen solution - to replace the items popped from the array - is inefficient and counter-intuitive. Using list assignment and Perl's ability to index array elements from the end of the array lets you write my ($last, $secondLast) = #{$nums_ref}[-1, -2] which doesn't remove the elements and so they don't need to be replaced.
Here is quick rewrite of your program to show you what you've been missing!
use strict;
use warnings;
my #defaultNums = (0, 1);
fib(\#defaultNums, 10);
sub fib {
my ($nums_ref, $max) = #_;
print join(', ', #$nums_ref), "\n";
my ($last, $secondLast) = #{$nums_ref}[-1, -2];
my $sum = $last + $secondLast;
push #$nums_ref, $sum;
return if #{$nums_ref} >= $max;
fib($nums_ref, $max);
}
output
0, 1
0, 1, 1
0, 1, 1, 2
0, 1, 1, 2, 3
0, 1, 1, 2, 3, 5
0, 1, 1, 2, 3, 5, 8
0, 1, 1, 2, 3, 5, 8, 13
0, 1, 1, 2, 3, 5, 8, 13, 21

I figured out my mistake. I'm popping off of the array twice each time, so I need to remember to push those numbers back on before I push on $sum. Thanks anyways.

Related

Inexplicably a Perl local my(array) acts like a global variable, dirty on subsequent iterations

Simply want to know why the #b is acting like a global and has to be reset each time the subroutine is called. A my(variable) shouldn't last past the routine but this one is persistent. The first iteration works, but the subsequent ones would be corrupted without the for loop setting to 0.
Also, #b doesn't respond to a; Print Join(); #b=(0)x#$a; mapping; or any other calls to it, nothing besides a direct query of $b(#) and nothing else. Couldn't be the chink in the Perl armor, could it?
Yes, if I comment out the 1st iteration and the for loop, the 2nd iteration works but subsequent ones do not.
sub firstDupe {
my ($a) = #_;
my #b;
# need for next line inexplicable, #b acts like global, ideas?
my $l = scalar(#{$a})+1; for ($i=0; $i < $l; $i++){ $b{$i}='0'; }
for (#{$a}){
return int($_) if $b{$_}++;
}
return -1;
}
my #pb= (2, 1, 3, 5, 3, 2);
$val=&firstDupe(\#pb);
my $ret="\nRet: $val";
#pb= (2, 4, 3, 5, 1);
$val=&firstDupe(\#pb);
$ret = $ret.", $val";
#pb= (1);
$val=&firstDupe(\#pb);
$ret = $ret.", $val";
#pb= (2,2);
$val=&firstDupe(\#pb);
$ret = $ret.", $val";
#pb= (2,1);
$val=&firstDupe(\#pb);
$ret = $ret.", $val";
print "\n\n$ret";
print "\nkey: 3, -1, -1, 2, -1\n";
This is because you have $b{$i} -- that is, a hash %b, which is never declared (made lexical).
So it is created, right there, as a global variable.
This would have not been possible with use strict; in place.
May I recommend to always, always use this pragma, and even more so use warnings;.
Note that $a and $b are not advisable for variable names, since those particular names have a bit special standing, being used by sort.
Also, you don't need that & in front of your functions. It has very specific properties, which don't hurt here, but it is not needed and it shouldn't be there.

Unintended endless While loop - why?

I have an array of numeric values that's sorted. I also have a minimum and maximum value and want to remove any values from the array that are smaller than the minimum or bigger than the maximum value. I am getting an endless loop when my minimum value is smaller than the value of the first array element. Here's a minimal example of the code in question:
#!/usr/bin/perl
use strict;
use warnings;
my #array = ( 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 );
my $min_wert = 1;
my $max_wert = 13;
while ( $array[0] < $min_wert ) {
shift #array;
}
while ( $array[-1] > $max_wert ) {
pop #array;
}
print join( ' ', #array );
print "\n";
The problem is, this version works flawlessly, outputting
5 6 7 8 9 10 11 12 13
The first while is not entered in this case.
Dropping the same test case into my production code, I'm getting the following error message on the line with the shift-statement:
Use of uninitialized value in numeric lt (<) at
line 1130.
I then introduced a counter to try to figure out why the while-loop is even entered, and that completely removes the problem instead of giving me the opportunity for further diagnostics.
#werte_liste = sort {$a <=> $b} #werte_liste;
print join( ' ', #werte_liste );
print "\n";
print "Start: $start_index - Stop: $stop_index\n"
while ( $werte_liste[0] < $start_index ) {
print "In while-loop- why?\n";
shift #werte_liste;
}
while ( $werte_liste[-1] > $stop_index ) {
pop #werte_liste;
}
why do I enter that first while loop in this case? And second, is there a better solution to my specific problem (I'm not talking lots of values here, so readability of code is more important than efficiency).
I don't know why it works with your testcase but doesn't in your production code, but here's my guess:
Your array becomes empty. $array[0] < $min_wert is true if $array[0] is undef (which happens if the array is empty), and $min_wert > 0.
undef is basically treated as a 0 in numerical comparisons (it emits a warning).
You can check that the array still has elements with this:
while ( #array and $array[0] < $min_wert ) {
The other while loop probably has the same problem.
What happens when #werte_liste is empty?
For one, $werte_liste[0] will be undefined, and the expression $werte_liste[0] < $start_index will generate the Use of uninitialized value in numerlic lt ... warnings.
For another, $werte_liste[0] will evaluate to 0 for the < comparison. If $start_index is positive, then $werte_liste[0] < $start_index is true.
Finally, shift #werte_liste will have no effect on #werte_liste, #werte_liste will remain empty, and your while ... expression will repeat indefinitely.

Why perl does not tend to gobble up all the arguments that follow it?

Acording to doc list operators tend to gobble up all arguments that follow
#!/usr/bin/env perl
use warnings;
#sub test;
#sub My::test;
sub list {
print ">>#_<<\n";
}
sub new {
my $class = shift;
my $self = bless {}, 'main';
list 0, main::test $self, 1, 2, 3;
list 0, My::test $self, 1, 2, 3;
}
# sub test (#) { # it has not matter there is prototype or not
sub test {
print "test>>#_<<\n";
return;
}
new();
package My;
# sub test (#) { # it has not matter there is prototype or not
sub test {
print "My::test>>#_<<\n";
return;
}
In this example the output is:
test>>main=HASH(0x24a3160)<<
>>0 1 2 3<<
My::test>>main=HASH(0x24a3160)<<
>>0 1 2 3<<
Which means that list does not gobble up all the arguments that follow it
The expecting result is:
test>>main=HASH(0x1bea160) 1 2 3<<
>>0<<
My::test>>main=HASH(0x1bea160) 1 2 3<<
>>0<<
What a problem is?
I don't see why you expected the different result. For better understanding, I changed the tests to return t from main and m from My, turning the output to
test>>main=HASH(0x60003b650)<<
>>0 t 1 2 3<<
My::test>>main=HASH(0x60003b650)<<
>>0 m 1 2 3<<
Did you think test would eat all the arguments? You can't do that with methods, you need normal subroutines for that. Your syntax is equivalent to
list(0, $self->main::test(), 1, 2, 3);
Try changing your code to
list(0, main::test 1, 2, 3);
list(0, My::test 1, 2, 3);
but you'll have to move sub test declarations upwards, or predeclare the subs (plain sub test; with no block). That's why using parentheses with subroutines is a good practice.
Update: Moving sub test declarations upwards works for your code, too. The parser must know a subroutine to be able to parse it as such.

I am unable to to understand following Perl code

I have the following Perl code.
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
my #array = ( 3, 4, 1, 4, 7, 7, 4, 1, 3, 8 );
my %unordered;
#unordered{#array} = undef;
foreach my $key (keys %unordered) {
print "Unordered: $key\n";
}
my %seen;
my #ordered;
foreach my $element (#array) {
if ( not $seen{$element}++ ) {
push #ordered, $element;
}
}
In the last foreach code block, I am unable to understand this - in the first iteration, the expression not $seen{$element}++ evaluate to not 0 - true - so the if block execute. In the second iteration the expression not $seen{$element}++ should again evaluate to not 0 - true as the hash is empty. So, reading the scalar $seen{$element} will read 0 and not 0 will evaluate to true. So, the if block should execute again. But, the book says it stops after first iteration. Can anyone explain this?
In the second iteration the hash will no longer be empty, because the ++ operator will have put a 1 in there. In a third iteration the value will be 2 (which for the purposes of this program is the same as 1, it just means "seen at least once before").
At the end of your program %seen will contain the number of times each entry appears in your list.
if $a++ increments the value of $a (treating it as 0 if missing), and then returns the value before that increment to the comparison.
It is important to use the postfix operator, as if ++$a will not work here: It also places a 1 in your hash, but it returns the modified value (so 1 even for the first iteration).
The last foreach loop can be detailled as:
# loop on all elements of the array
foreach my $element (#array) {
# if the current element haven't been seen yet
if ( not exists $seen{$element} ) {
# add current element into ordered array
push #ordered, $element;
}
# Increment the number of time element have been seen
$seen{$element}++;
}
At the end, #ordered will contain:
(3, 4, 1, 7, 8)
A better name should be #unique instead of #ordered.
%seen will contain:
(3 => 2, 4 => 3, 1 => 2, 7 => 2, 8 => 1)

How to push data to a multidimensional array?

I want to create 10 one dimensional arrays , and put these 10 one dimensional arrays to another one dimensional array , and store some data to some specific index of array .
but the output what I expect should be
expect output real output
0 1
1 1
0 1
3 1
0 1
5 1
0 1
7 1
0 1
0 1
here is my code
#Hits = ();
# Create 10 one dimension array
for($i=0;$i<=9;$i++)
{
#Space = ();
push(#Hits,\#Space);
}
# Store some data to some index
push(#{$Hits[1]},1);
push(#{$Hits[3]},3);
push(#{$Hits[5]},5);
push(#{$Hits[7]},7);
# print the first element of 10 arrays
for($i=0;$i<=9;$i++)
{
print $Hits[$i]->[0];
print "\n";
}
thanks
The Problem is that you aren't properly declaring your variables. For every script, you should
use strict; use warnings;
This disallows common error sources, warns about iffy stuff, and forces you to properly declare all your variables.
By default, all undeclared variables are considered global. Therefore, in
for($i=0;$i<=9;$i++)
{
#Space = ();
push(#Hits,\#Space);
}
the #Space refers to the same array in each iteration. Ergo, all ten entries in #Hits are a reference to the same array. Let's inspect what #Hits actually is. We can do so with Data::Dumper or the Data::Dump module (the latter usually produces prettier output):
use Data::Dump; # use Data::Dumper;
dd \#Hits; # print Dumper \#Hits;
We get with Data::Dumper (easier to understand):
$VAR1 = [
[
1,
3,
5,
7
],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0]
];
So I said the solution would be declaring your variables. Specifically, we want lexical variables. These variables are only visible inside the block where they are declared. This makes reasoning about code much easier. We can declare a lexical variable like so:
my $foo = 123;
When we have a loop like
my #Hits;
for my $i (0 .. 9) {
my #Space;
push #Hits, \#Space;
}
then each time the my is executed, we get a new #Space. Oh, and I have used a foreach-loop, that iterates over the range 0 .. 9 with the (lexical) $i variable. I find this easier to understand than the C-style loops you used.
Because every element in #Hits now is a different arrayref, we get the expected data structure. As Data::Dump output:
[[], [1], [], [3], [], [5], [], [7], [], []]
When we now execute your loop that prints out the first value of each sub-array, then you may be suprised by the empty lines in between the numebers. This is because e.g. the first arrayref does not have an entry at index 0, and therefore returns the special undef value. When used as a string, this is the empty string. If you followed my advice and did the use warnings, you also get a message that you are printing an uninitialized value.
We can solve this by testing for definedness, and providing a zero otherwise. Since perl5 v10, we can use the defined-or operator // for this (on earlier perls, the || logical or has to do).
for my $i (0 .. 9) {
my $value = $Hits[$i][0] // 0;
print "$value\n";
}
There are a few other bits we can improve:
We don't have to manually create the #Space arrays; Perl does this behind the scenes when you dereference an array entry like #{ $Hits[$i] }. This is called autovivification.
Not only can we iterate over ranges, but also over arrays. This is much better than hardcoding indices.
Since v10, you can use the say feature. The say function is exactly like print but appends a newline at the end.
Here is how I'd have written that code:
#!/usr/bin/perl
use strict; use warnings; use feature 'say';
my #Hits;
for my $i (1, 3, 5, 7) {
push #{ $Hits[$i] }, $i;
}
for my $arrayref (#Hits) {
say $arrayref->[0] // 0;
}
Output:
0
1
0
3
0
5
0
7
(Note that we never initialized values at positions 8 and 9, so they are not shown. We could amend this by iterating over the slice #Hits[0 .. 9].)
I change your code like following:
#! /usr/bin/perl -w
#Hits = ();
push(#{$Hits[1]},1);
push(#{$Hits[3]},3);
push(#{$Hits[5]},5);
push(#{$Hits[7]},7);
#print the content of
for($i=0;$i<=9;$i++)
{
if (defined ($Hits[$i])) {
print "$Hits[$i][0]\n";
} else {
print "0\n";
}
}
It's incorrect to give the ref of #space to #Hits and that makes the wrong result. It's not necessary to initial #Hits in perl.
perl -e "use Data::Dump; #sf=(); push #{$sf[0]},"0"; push #{$sf[1]},"1"; dd \#sf;"
[[0], [1]]
or
perl -e "use Data::Dump; #sf=(); push #sf,["0"]; push #sf,["1"]; dd \#sf;"
[[0], [1]]