At Sleep at Last
with Raku

by Arne Sommer

At Sleep at Last with Raku

[158] Published 12. December 2021.

This is my response to the Perl Weekly Challenge #142.

Challenge #142.1: Divisor Last Digit

You are given positive integers, $m and $n.

Write a script to find total count of divisors of $m having last digit $n.

Example 1:
Input: $m = 24, $n = 2
Output: 2

The divisors of 24 are 1, 2, 3, 4, 6, 8 and 12.
There are only 2 divisors having last digit 2 are 2 and 12.
Example 2:
Input: $m = 30, $n = 5
Output: 2

The divisors of 30 are 1, 2, 3, 5, 6, 10 and 15.
There are only 2 divisors having last digit 5 are 5 and 15.

The «diviors» procedure, last used a week ago (Challenge #141.1: Number Divisors), comes in handy yet again:

File: dld
#! /usr/bin/env raku

subset PosInt of Int where * >= 1;             # [1]
subset Digit  of Int where * == any(0..9);     # [2]

unit sub MAIN (PosInt $m, Digit $n, :v(:$verbose));

my @divisors = divisors($m);                   # [3]
my @result   = @divisors.grep({ /$n$/ }) ;     # [4]

say ": - Ending with $n: { @result.join(", ") }" if $verbose;

say @result.elems;

sub divisors ($number, :$not-self, :$not-one)  # [3]
{
  my @divisors;
  
  for ($not-one ?? 2 !! 1) .. $number/2 -> $candidate
  {
    @divisors.push: $candidate if $number %% $candidate;
  }
  
  @divisors.push: $number unless $not-self;

  say ": $number has { @divisors.elems } divisors: { @divisors.join(", ") }"
    if $verbose;                               # [3a]

  return @divisors;
}

[1] Ensure positive integers.

[2] A single digit is better than the «positive integers» specified by the challenge, Note that zero is allowed here, whereas the challenge excluded it.

[3] Get the divisors. Note the slightly altered verbose output (in [3a]) compared to the previous version.

[4] Only use the numbers ending with the specified digit.

Running it:

$ ./dld 24 2
2

$ ./dld 30 5
2

With verbose mode:

$ ./dld -v 24 2
: 24 has 8 divisors: 1, 2, 3, 4, 6, 8, 12, 24
: - Ending with 2: 2, 12
2

$ ./dld -v 30 5
: 30 has 8 divisors: 1, 2, 3, 5, 6, 10, 15, 30
: - Ending with 5: 5, 15
2

Challenge #142.2: Sleep Sort

Another joke sort similar to JortSort suggested by champion Adam Russell.

You are given a list of numbers.

Write a script to implement Sleep Sort. For more information, please checkout this post.

The linked to page talks about milliseconds, so let us have a go at that:

File: sleep-sort-milliseconds
#! /usr/bin/env raku

unit sub MAIN (*@values where @values.elems > 0, :v(:$verbose));   # [1]

my @promises;                                                      # [5]

for @values -> $value                                              # [2]
{
  next unless $value ~~ Numeric;                                   # [3]
  @promises.push: Promise.in($value / 1000).then: { say $value; }; # [4][5]
}

await @promises;                                                   # [5a]

[1] Ensure at least one element. Note the missing type check, as we do that in the loop instead.

[2] For each value,

[3] Skip it if it is not numeric.

[4] Set up a Promise to run in (after) the specified number of seconds. The input is in milliseconds, so we divide by 1000 to get the number of seconds. The result is that the code block in when is executed after the specified time. Not necessarily at the time, but sometime after it.

[5] The Promise calls returns a Promise object, that we can use to e.g. inspect the state. We have to collect them, as done here, so that we can ensure that that the program does not finish before they are executed (or kept, as it is called) - as future promises dies at program termination. We do this wait with the await keyword.

See docs.raku.org/routine/await for more information about await.

Running it:

$ ./sleep-sort-milliseconds 5 4 3 1 2 0.9 
1
0.9
5
3
2
4

That was not quite as we could hope for. The problem is that milliseconds are too quick, and/or Raku too slow.

Let us have a go at the more relaxing seconds instead:

File: sleep-sort
#! /usr/bin/env raku

unit sub MAIN (*@values where @values.elems > 0, :v(:$verbose));

my @promises;

for @values -> $value
{
  next unless $value ~~ Numeric;
  @promises.push: Promise.in($value).then: { say $value; };
}

await @promises;

Running it:

$ ./sleep-sort 5 4 3 1 2 0.9 
0.9
1
2
3
4
5

That is more like it.

Note that the program will wait as long as it takes, if left alone. A very large value will give a program that will run for a long time. E.g.:

$ ./sleep-sort 5 4 3 1 2 0.9 1234567890 

The program will go on (doing nothing much) for slightly less than 40 years. Feel free to check for yourself…

And that's it.