Esthetic Sylvester
with Raku

by Arne Sommer

Esthetic Sylvester with Raku

[192] Published 17. July 2022.

This is my response to The Weekly Challenge #173.

Challenge #173.1: Esthetic Number

You are given a positive integer, $n.

Write a script to find out if the given number is Esthetic Number.

An esthetic number is a positive integer where every adjacent digit differs from its neighbour by 1.

For Example:
5456 is an esthetic number as |5 - 4| = |4 - 5| = |5 - 6| = 1
120 is not an esthetic numner as |1 - 2| != |2 - 0| != 1
File: esthetic-number
#! /usr/bin/env raku

unit sub MAIN (Int $n where $n > 0));  # [1]

say + is-esthetic($n);                 # [2]

sub is-esthetic ($number)              # [2a]
{
  my @digits = $n.comb;                # [3]

  my $current = @digits.shift;         # [4]

  while @digits.elems                  # [5]
  {
    return False
      if all ($current != @digits[0] + 1), ($current != @digits[0] - 1); # [6]

    $current = @digits.shift;          # [7]
  }

  return True;                         # [8]
}

[1] Ensure a positive integer.

[2] Print «1» if the number is esthetic, and «0» if not. I have delegated the computation to a procedure [2a] to make reuse easier. Note the Numeric Coercion Prefix Operator + used to get a numeric value instead of the True/False returned by the procedure.

See docs.raku.org/routine/+ for more information about the Numeric Coercion Prefix Operator +.

[3] Get the individual digits.

[4] Get the first digit.

[5] As long as there are more digits,

[6] The first != checks that the current digit is not one less than the next one, and the second one that it is not one more than it. If both are true (set up with an all junction, we do not have an esthetic number (and returns False).

See docs.raku.org/routine/all for more information about the all Junction.

[7] Move along, one more digit.

[8] We have reached the end, without failing. Then we have an esthetic number.

Running it:

$ ./esthetic-number 5456
1
$ ./esthetic-number 120
0

Looking good.

Note that one-digit numbers are considered esthetic by my implementation:

$ ./esthetic-number 1
1

Challenge #173.2: Sylvester’s sequence

Write a script to generate first 10 members of Sylvester's sequence. For more informations, please refer to the wikipedia page.

Output:
2
3
7
43
1807
3263443
10650056950807
113423713055421844361000443
12864938683278671740537145998360961546653259485195807
16550664732451996419846819544443918001751315270637749784185138876653\
  5868639572406808911988131737645185443

This sequence popped up in part 7 of my Centenary Sequences with Raku article, published in November 2020, as sequence #085:

(my $sum = 2, { my $c = $sum +1; $sum *= $c; $c } ... Inf)              # 085

It was presented without explanation, but I'll do it this time:

(my $sum = 2, { my $c = $sum +1; $sum *= $c; $c } ... Inf)
# 1 ########  # 2 ############################### # 3 #### 

[1] This (everything before the comma) is the first value in the sequence. We declare a variable $sum which is local to the sequence. The result of the assignment is the value itself, which then is the first value.

[2] We set up a local variable $c, and use that one to change the global (from the point of view of this block) $sum. The generated value for the sequence is the last one in the block - which is the single $c.

[3] This tells Raku to use the block to the left [2] to genereate values ad infinitum.

I used it like this:

> say (my $sum = 2, { my $c = $sum +1; $sum *= $c; $c } ... Inf)[^8];
(2 3 7 43 1807 3263443 10650056950807 113423713055421844361000443)

Wrapping it up as a program is easy:

File: sylvesters-sequence
#! /usr/bin/env raku

unit sub MAIN (Int $count where $count > 0 = 10);

my $ss := (my $sum = 2, { my $c = $sum +1; $sum *= $c; $c } ... Inf);

say $ss[^$count].join("\n");   # [1]

[1] The join adds a newline between each value, and the say adds one after the last one - giving the output as specified by the challenge.

Running it:

$ ./sylvesters-sequence
2
3
7
43
1807
3263443
10650056950807
113423713055421844361000443
12864938683278671740537145998360961546653259485195807
16550664732451996419846819544443918001751315270637749784185138876653\
  5868639572406808911988131737645185443

Looking good.

Curious about the eleventh value?

$ ./sylvesters-sequence 11
2
3
7
43
1807
3263443
10650056950807
113423713055421844361000443
12864938683278671740537145998360961546653259485195807
16550664732451996419846819544443918001751315270637749784185138876653\
  5868639572406808911988131737645185443
27392450308603031423410234291674686281194364367580914627947367941608\
  692026226993634332118404582438634929548737283992369758487974306317\
  730580753883429460344956410077034761330476016739454649828385541500\
  213920807

The number of digits in each value increases at an alarming rate, after the first three values. Here is a Sylvester length sequence, giving us the number of digits instead of the actual values:

File: sylvesters-length-sequence
#! /usr/bin/env raku

unit sub MAIN (Int $count where $count > 0 = 10);

my $ss := (my $sum = 2, { my $c = $sum +1; $sum *= $c; $c } ... Inf);

say $ss[^$count].map( *.chars ).join(", ");  # [1]

[1] Note the map we have inserted here, giving the number of characters (i.e. digits), instead of the actual value. Doing this in the sequence would require a major rewrite. (I have chosen to print a comma separated list this time, as the values are much smaller.)

Running it:

$ ./sylvesters-length-sequence 12
1, 1, 1, 2, 4, 7, 14, 27, 53, 105, 209, 417

The pattern (from «4») seems to be «double the value and then subtract 1».

Generating some more values falsifies that assumption:

$ ./sylvesters-length-sequence 15
1, 1, 1, 2, 4, 7, 14, 27, 53, 105, 209, 417, 834, 1668, 3336

And that's it.