I am unable to to understand following Perl code - perl

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)

Related

need to use pop function twice to remove last element from the array (perl)

I've just started to learn Perl and joined Euler project to practice coding. This is the first exercise I did. The task was: "If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000." My solution:
use strict;
use warnings;
my #numbers = (1..1000);
my $counter = 0;
my #all_array = ();
my $total = 0;
foreach $counter (#numbers) {
if (($numbers[$counter] % 3 == 0) or ($numbers[$counter] % 5 == 0)) {
push (#all_array, $numbers[$counter]);
}
}
pop (#all_array); #after that the last digit is still in place
pop (#all_array); # only now the number 1000 is removed
my $tot = eval join '+', #all_array; #returns correct value
print $tot;
The final element of the array is 1000. It seems as if it is followed by a space so to remove the number and get the correct result I have to use the pop function twice. The use of local $"='' changes nothing. Besides, I'm getting a message: Use of uninitialized value within #numbers in modulus (%) at C:\Users\Greg\Documents\perl\unt.pl line 10.
What am I doing wrong and how to fix it?
Let's go through your code:
#numbers is an array with the numbers from 1 to 1000
Why do you include 1000 in the list when the exercise says "less than N"?
the for loop
assigns each of the numbers to $counter, i.e. 1, 2, ...
you use $counter as index into #numbers
why do you do that when $counter is already the number you are looking for?
Perl arrays start at index 0, so you have an off-by-one error
you never check 1 because your first number will be $numbers[1] == 2 (OK, doesn't cause an incorrect result for the task at hand...)
you access one element behind the array, i.e. $numbers[1000] == undef
calculating with undef will cause a warning
undef % 3 == 0 is true, hence...
the first pop() will remove undef (from $counter == 1000)
the second pop() will remove 1000 (from $counter == 999)
then you use eval on a string 3 + 5 + 6 + ... a very inefficient way to do a sum :-)
Wouldn't it be just a simpler approach to calculate the sum while running over the numbers from 1 to N-1? F.ex.:
#!/usr/bin/perl
use strict;
use warnings;
foreach my $arg (#ARGV) {
my $sum = 0;
foreach my $number (1..$arg - 1) {
$sum += $number
if ($number % 3 == 0) || ($number % 5 == 0);
}
print "${arg}: ${sum}\n";
}
exit 0;
Test run:
$ perl dummy.pl 10 100 1000 10000
10: 23
100: 2318
1000: 233168
10000: 23331668

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.

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]]

Perl Fibonacci number program with array references?

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.

how does this work: map used with ternary hook operator and ()

In a previous question, seaworthy asked how to remove the first 5 elements from an array:
How do I remove the first five elements of an array?
Among several suggestions, friedo offered this:
my $cnt = 0;
#array = map { ++$cnt < 5 ? ( ) : $_ } #array;
I don't get the ( ) bit. Please could explain how this works to me, because I can't get my head around it?
I know that the ternary hook operator works like this:
(if something) ? (then do this) : (otherwise do this)
For example: $a=2; print ($a==2 ? 3 : 4) # this prints: 3
because we have: ($a==2 ? 3 : 4)
which means: (if $a is equal to 2) ? (then print 3) : (otherwise print 4)
so with friedo's code, first $cnt is increased to 1, then we have:
$cnt < 5 ? ( ) : $_
which means:
if $cnt is less than 5 ? then ( ) : otherwise $_
I can see how the $_ bit works because i sometimes use map like this:
#array = map { $_, "\n" } #array
This copies an element from #array, places the copy into $, then adds a \n newline, then it copies the value in $ back to #array (and it does this with all values in #array so basically it adds newlines to every element in #array)
therefore:
#array = map { if $cnt is less than 5 then ( ) otherwise $_ } #array
means something like:
#array = map { if $cnt is less than 5 then ( ) otherwise copy the element back to #array }
so clearly ( ) means something like 'get rid of it'
but i'm just not sure how it works. Please could you explain it?
In a map, each item from the array is passed into the code block (in $_) where it can be transformed into some other value. In other words, map transforms a list.
In this case, we want to throw away values where the count ($cnt) is less than 5. So how to we make a map block return "nothing" when that condition is true?
We can't say
my $cnt = 0; #array = map { ++$cnt < 5 ? undef : $_ } #array;
Because then we'd end up with an array that looks like
( undef, undef, undef, undef, undef, 6, 7, 8 ... )
which is not what we wanted.
But returning ( ) instead returns an empty list. Consider push #foo, ( ); or #bar = ( 1, 2, 3, ( ), 4, 5, 6 ); In each of these cases, the empty set of parens is a list of zero items, which doesn't have any affect on the arrays concerned.
The empty list is useful in ternaries where you need to either return a list item or nothing at all. It's also useful to force list context on an expression to get a count:
my $count = ( ) = $str =~ /\d/g;
Here, we put the regex in list context by assigning it to an empty list, giving us the count of digits in the string. Then we assign that empty list to $count.
Another frequent example of using lists in map is when you're transforming something into a hash. For example,
my %unique = map { $_ => 1 } #duplicates;
Here each single item in #duplicates get transformed into a two-element list that looks like ( 'foo' => 1 ) although it's not as obvious since no parens are involved. All the two-item lists then get built up into one big list of alternating keys and values which construct the hash. Let's say you wanted to make this hash but exclude some items. In this case we either need to return a key/value, or nothing. So that's a good chance to use an empty list:
my %filtered_unique = map { some_test( $_ ) ? ( ) : ( $_ => 1 ) } #duplicates;
I know I'm a bit late in the game here, but why not do something simple?
my #truncated = #array[5 .. $#array]
Apparantly you can return a list instead of an element, and map constructs the result by joining those lists and elements. () is just the empty list in this case. For more insight, copy-paste the example, and replace () by (1, 2, 3).