What does 'foreach(1..4)' mean in Perl? - perl

In Perl, what does foreach(1..4) mean? Specifically the two periods. I've been having trouble finding an answer.

In Perl, 1..4 produces 1 2 3 4 so you're looping through all the numbers from 1 to 4, assigning each value in turn to $_.

.. is one of the Range Operators

It is known as the range operator.
It creates a range between the two operands...

foreach(1..4)
It means your loop runs from 1 to 4
or
creates a range between the two operands
Example equals to
my $min = 999;
foreach $item (1, 2, 3, 4)
{
$min = $item if $min > $item;
}
print "Min = $min\n"; # expects Min = 1

Related

Project Euler Number 1 perl

I am a beginner coder and I have started doing the Project Euler stuff from projecteuler.net, and am on problem #1. The problem is:
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.
I have the following code too:
#!/usr/bin/perl
use strict;
use warnings;
my $a;
my $b;
my $c = 0;
my $d = 0;
my $e;
for ($a= 0; $a < 1000; $a += 3 ) {
$d = $d + $a;
}
for ($b= 0; $b < 1000; $b += 5 ) {
$c = $c + $b;
}
$e = $c + $d;
printf "$e \n";
And my output is this:
266333
But I know for a fact that is wrong. Maybe it's going over common answers that 5 and 3 share, like 15 or 45.
Whatever it is, suggestions?
If two loops are involed, they both need some conditions inside to skip some iteration. Perhaps you could also think about an alternative solution using only one loop:
for (my $n = 1; $n < 1000; $n++) {
if ( ... ) { # <--*
$d += $n;
}
}
It could be easier and less code repeatition.
Now we only need to figure out the key expression for that if statement. :-)
Maybe it's going over common answers that 5 and 3 share, like 15 or 45
Yes that's exactly what's happening. You will need to prevent common multiples from being added to the total sum twice. Perhaps adding a conditional statement in one of your loops. (I'd rather not give out the exact answer and let you figure out how to do it)
I think you're doing too much in one stage. Don't add the numbers as you go. Instead, get a list of the numbers that you're interested in and add them all up at the end.
Oh, and here's a little hint. If you're trying to store a list of things and you want no duplications - then the keys of a hash work great for that.

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

Is it ok to use conditional splice?

Let's consider simple Perl code:
my #x = ( 1, 5, 9);
for my $i ( 0 .. $#x ) {
splice( #x, $i, 1 ) if ( $x[$i] >= 5 );
}
print "#x";
Output is not correct, 1 9 but there must be 1
If we run code with -w flag it prints warning
Use of uninitialized value within #x in numeric ge (>=) at splice.pl line 5.
So, it's not good practise to use conditional splice and better to push result in new variable?
The problem isn't your use of conditional splice per se, it's your loop. The most obvious problem, and the one that causes your warning, is that you're running off of the end of the array. for my $i ( 0 .. $#x ) sets the iteration endpoint to $#x before the loop starts, but after you splice one or more elements out, the last index of the array will be smaller. You could fix that using a C-style for loop, instead of the range-style loop, but I don't recommend it — keep reading.
The next problem is that after you splice an element out of the array, you continue the loop with $i one higher... but because you spliced an element out of the array, the next element that you haven't seen yet is in $x[$i], not $x[$i+1]. You say "Output is correct, 1 9", but shouldn't 9 have been removed, since it's more than 5? You could fix this using redo after splice to go through the loop again without incrementing $i, but I don't recommend that either.
So it is possible to fix your loop which uses splice in place so that it will work correctly, but the result would be pretty complicated. Unless there's a compelling reason to do it differently, I would recommend using simply
#x = grep { $_ < 5 } #x;
There's no problem with assigning the result to the same array as the source, and there is no loop management or other housekeeping for you to do.

what is meaning of $# in perl?

Consider the following code:
my #candidates = get_candidates($marker);
CANDIDATE:
for my $i (0..$#candidates) {
next CANDIDATE if open_region($i);
$candidates[$i] = $incumbent{ $candidates[$i]{region} };
}
What is meaning $# in line 3?
It is a value of last index on array (in your case it is last index on candidates).
Since candidates is an array, $#candidates is the largest index (number of elements - 1)
For example:
my #x = (4,5,6);
print $#x;
will print 2 since that is the largest index.
Note that if the array is empty, $#candidates will be -1
EDIT: from perldoc perlvar:
$# is also used as sigil, which, when prepended on the name of
an array, gives the index of the last element in that array.
my #array = ("a", "b", "c");
my $last_index = $#array; # $last_index is 2
for my $i (0 .. $#array) {
print "The value of index $i is $array[$i]\n";
}
This means array_size - 1. It is the same as (scalar #array) - 1.
In perl ,we have several ways to get an array size ,such as print #arr,print scalar (#arr) ,print $#arr+1 and so on.No reason ,just use it.You will get familiar with some default usage in perl during your further contact with perl .Unlike C++/java ,perl use a lot of
special expression to simplify our coding , but sometimes it always make us more confused.

How do I increment a value with leading zeroes in Perl?

It's the same question as this one, but using Perl!
I would like to iterate over a value with just one leading zero.
The equivalent in shell would be:
for i in $(seq -w 01 99) ; do echo $i ; done
Since the leading zero is significant, presumably you want to use these as strings, not numbers. In that case, there is a different solution that does not involve sprintf:
for my $i ("00" .. "99") {
print "$i\n";
}
Try something like this:
foreach (1 .. 99) {
$s = sprintf("%02d",$_);
print "$s\n";
}
The .. is called the Range Operator and can do different things depending on its context. We're using it here in a list context so it counts up by ones from the left value to the right value. So here's a simpler example of it being used; this code:
#list = 1 .. 10;
print "#list";
has this output:
1 2 3 4 5 6 7 8 9 10
The sprintf function allows us to format output. The format string %02d is broken down as follows:
% - start of the format string
0 - use leading zeroes
2 - at least two characters wide
d - format value as a signed integer.
So %02d is what turns 2 into 02.
printf("%02d\n",$_) foreach (1..20)
print foreach ("001" .. "099")
foreach $i (1..99) {printf "%02d\n", $i;}
I would consider to use sprinft to format $i according to your requirements. E.g. printf '<%06s>', 12; prints <000012>.
Check Perl doc about sprinft in case you are unsure.
Well, if we're golfing, why not:
say for "01".."99"`
(assuming you're using 5.10 and have done a use 5.010 at the top of your program, of course.)
And if you do it straight from the shell, it'd be:
perl -E "say for '01'..'99'"