This is my response to the Perl Weekly Challenge #8.
I'll start with a program that prints the divisors (excluding the number itself) for a given number:
File: perfectdivisorssub MAIN ($number)
{
say "Divisors (excluding the number itself): " ~ properdivisors($number);
}
multi properdivisors (2) { return (1); } # [1]
multi properdivisors (Int $number where $number > 2) # [2]
{
return (1) if $number.isprime; # [3]
my @divisors = (1); # [4]
for 2 .. ($number 1) > $candidate # [5]
{
@divisors.push: $candidate if $number %% $candidate; # [5]
}
return @divisors;
}
[1] This variant is called for the value 2.
[2] This variant is called when the value is 3 or greater. (Note that integers less than 2, as well as nonintegers, give a runtime error.)
[3] Prime numbers don't have divisors, so avoid trying to compute them.
[4] The first divisor is always 1.
[5] Looping through the possible values, we add it to the list if it is a divisor
Testing it:
$ perl6 perfectdivisors 2
Divisors (excluding the number itself): 1
$ perl6 perfectdivisors 3
Divisors (excluding the number itself): 1
$ perl6 perfectdivisors 4
Divisors (excluding the number itself): 1 2
$ perl6 perfectdivisors 12
Divisors (excluding the number itself): 1 2 3 4 6
Extending (and renaming) the program with code to check if the number is a perfect number:
File: perfectnumberstestsub MAIN ($number)
{
say "Divisors (excluding the number itself): " ~ properdivisors($number);
say "Is the number perfect: " ~ isperfect($number);
}
multi properdivisors (2) { return (1); }
multi properdivisors (Int $number where $number > 2)
{
return (1) if $number.isprime;
my @divisors = (1);
for 2 .. ($number 1) > $candidate
{
@divisors.push: $candidate if $number %% $candidate;
}
return @divisors;
}
sub isperfect ($number)
{
return $number == properdivisors($number).sum; # [1]
}
[1] Checking if the number is a Perfect Number is easy. Just add the divisors and compare the sum with the number itself.
And finally as a program that prints the first 5 perfect numbers (or whatever other number we specify on the command line):
File: perfectnumberssub MAIN (Int $count where $count > 0 = 5) # [1]
{
my $numbers := gather # [2]
{
for 2..Inf # [3]
{
take $_ if isperfect($_); # [3]
}
}
say "The first $count perfect numbers: "
~ $numbers[0 .. $count 1].join(', ') ~ "."; # [4]
}
multi properdivisors (2) { return (1); }
multi properdivisors (Int $number where $number > 2)
{
return (1) if $number.isprime;
my @divisors = (1);
for 2 .. ($number 1) > $candidate
{
@divisors.push: $candidate if $number %% $candidate;
}
return @divisors;
}
sub isperfect ($number)
{
return $number == properdivisors($number).sum;
}
[1] A positive integer, with 5 as default value.
[2] You may have noticed (from my other articles) that I have a fondness for
«gather
»/«take
».
[3] Setting up the values.
[4] Fetching the first «$count» number of values.
Searching for the five first perfect numbers is extremely slow, as the fifth number is «33550336» (Source: wikipedia).
Count  Time  Values 
1  0,163s  6 
2  0,173s  6,28 
3  0,249  6,28,496 
4  11,919s  6,28,496,8128 
5  ???  6,28,496,8128,33550336 
It is actually so slow that I gave up after the program had been running for about one day. So I haven't actually verified that the program gives the correct answer.
multi properdivisors (Int $number where $number > 2)
{
return (1) if $number.isprime;
my @divisors = (1);
for 2 .. ($number / 2) > $candidate
{
@divisors.push: $candidate if $number %% $candidate;
}
return @divisors.sort;
}
Timing it with the value 4 gives us 17,649s, so this version is actually slower than the original one (adding about 50% to the time used).
It turns out that the problem is the upper limit for the Range in the «for» loop. If we coerce the value to an integer, the time is as low as 5,723s:
File: perfectnumbers2b (changes only) for 2 .. ($number / 2).Int > $candidate
Write a function, ‘center’, whose argument is a list of strings, which will be
lines of text. The function should insert spaces at the beginning of the lines of
text so that if they were printed, the text would be centered, and return the
modified lines.
For example,
should return the list:
because if these lines were printed, they would look like:

The procedure is quite simple, and looks like this:
sub center (@strings) # [1]
{
my $maxlength = @strings>>.chars.max; # [2]
return @strings.map({ .indent(($maxlength  .chars) /2) }); # [3]
}
[1] Pass the strings.
[2] Compute the length of the longest string (by computing all, and taking the largest value).
[3] If we indent by the max length minus the length of the actual string, we get right tabulated output. Half the value gives centered output.
See docs.perl6.org/routine/indent for more information about «indent».
Here it is, as a program. Specify the arguments on the command line.
File: centersub MAIN (*@strings)
{
.say for center(@strings);
}
sub center (@strings)
{
my $maxlength = @strings>>.chars.max;
return @strings.map({ .indent(($maxlength  .chars) / 2) });
}
Running it:
$ perl6 center "123" "111111111111111111111111111111" "111111" "1"
123
111111111111111111111111111111
111111
1
$ perl6 center "123" "111111111111111" "111111" "1"
123
111111111111111
111111
1
Note that a string with an even number of characters will be placed wrong in a mathematical sense, as we cannot indent by half a space:
$ perl6 center "123" "11" "1"
123
11
1
$ perl6 center "1234" "11" "1"
1234
11
1
And that's it.