This is my response to The Weekly Challenge #171.
20 Abundant Odd Numbers
.
According to wikipedia,
A number n for which the sum of divisors σ(n) > 2n, or, equivalently, the sum of proper divisors (or aliquot sum) s(n) > n.945
is the first Abundant Odd Number
.
Sum of divisors:
1 + 3 + 5 + 7 + 9 + 15 + 21 + 27 + 35 + 45 + 63 + 105 + 135 + 189 + 315
= 975
#! /usr/bin/env raku
unit sub MAIN (Int :c(:$count) where $count > 0 = 20, :v(:$verbose)); # [1]
my $abundant := (1 .. Inf).grep( *.&is-abundant ); # [2]
say $abundant[^$count].join(", "); # [3]
sub divisors ($number, :$not-self, :$not-one) # [4]
{
my @divisors;
for ($not-one ?? 2 !! 1) .. $number/2 -> $candidate
{
@divisors.push: $candidate if $number %% $candidate;
}
@divisors.push: $number unless $not-self;
say ": $number -> @divisors[] = { @divisors.sum }" if $verbose;
return @divisors;
}
sub is-abundant ($number) # [5]
{
return $number % 2 && divisors($number, :not-self).sum > $number; # [6]
# return $number % 2 && divisors($number).sum > $number * 2; # [7]
}
[1] Specify another number of values to print, if the default 20 does not suit you.
[2] Start with all the integers, and use grep
to get only
those that we want. Note the special .&
calling syntax allowing us to
pretend that a procedure is a method.
See
docs.raku.org/language/operators#methodop_.& for more information
about the special procedure invocation syntax .&
.
[3] Print the required number of values from the sequence.
[4] This procedure should be familiar by now. See the second half of An Abundance of Numbers with Raku and Perl for details.
[5] Is the number abundant?
[6] Using the «Proper divisors» definition.
[7] Or you can use the «Divisors» definition.
Running it:
$ ./abundant-number
945, 1575, 2205, 2835, 3465, 4095, 4725, 5355, 5775, 5985, 6435, 6615, 6825, \
7245, 7425, 7875, 8085, 8415, 8505, 8925
Looking good.
You can use verbose mode to check the result manually, if you are so inclined. Here is a selection of the output:
$ ./abundant-number -v -c=1
: 1 -> = 0
: 3 -> 1 = 1
: 5 -> 1 = 1
: 7 -> 1 = 1
: 9 -> 1 3 = 4
: 11 -> 1 = 1
: 13 -> 1 = 1
…
: 935 -> 1 5 11 17 55 85 187 = 361
: 937 -> 1 = 1
: 939 -> 1 3 313 = 317
: 941 -> 1 = 1
: 943 -> 1 23 41 = 65
: 945 -> 1 3 5 7 9 15 21 27 35 45 63 105 135 189 315 = 975
945
sub compose($f, $g)
which takes in two parameters $f
and
$g
as subroutine refs and returns subroutine ref i.e.
compose($f, $g)->($x) = $f->($g->($x))
.
$f = (one or more parameters function)
$g = (one or more parameters function)
$h = compose($f, $g)
$f->($g->($x,$y, ..)) == $h->($x, $y, ..) for any $x, $y, ...
Let us start with the f
and g
functions:
#! /usr/bin/env raku
my $f = sub f ($val) # [1]
{
return $val * 10;
}
my $g = sub g ($val) # [1]
{
return $val + 1;
}
[1] Two ordinary procedures, with one input value and one (deterministic) output
value. The trick (so to speak) is the fact that the sub
keyword returns
a reference to the procedure, and we assign that value to a variable for each one.
See
docs.raku.org/syntax/sub more information
about sub
.
If you want to call a function, you can either use the name (i.e. the normal way):
my $b = f(12);
Or the reference (if you have one):
my $b = $f(12);
The «compose» procedure is easy:
File: first-class-function (partial)
sub compose(&f, &g) # [2]
{
return sub ($arg) # [3]
{
&f(&g($arg)); # [4]
};
}
[2] The procedure takes two arguments, of the callable type (courtesy of the &
(Callable) sigil). Calling it with other argument types will fail. The generic $
sigil works just fine, but without the Callable check.
[3] An anonymous procedure (i.e. without name), taking one input parameter (in $arg
).
The return value is a reference to this procedure.
[4] The procedure itself will - when executed - call the inner procedure &g($arg)
first, and then the outer &f
with the return value of the inner one.
And finally, executing the code:
File: first-class-function (the rest)
my $h = compose($f, $g); # [5]
say $h(12); # [6]
say $h(13); # [7]
my $m = compose($g, $f); # [8]
say $m(12); # [9]
say $m(13);
[5] Feel free to use the &
(Callable) sigil here (i.e. &h
instead of
$h
), if that makes you feel better.
[6] Call the procedure(s).
[7] Ditto, with another input value.
[8] Changing the order of the procedures does matter.
[9] The same procedures, reordered, and the same input - but a different result.
Running it:
File: first-class-function (partial)
$ ./first-class-function
130
140
121
131
Looking good.
And that's it.