The Nine Billion Names of God
Loops

by Arne Sommer

The Nine Billion Names of God with Raku - Part 4: Loops

[300.4] Published 4. August 2024.

[ Index | Introduction | Base | Recursion | Loops | Summary | RakuConf ]

Note that we cannot change the maximum word length in this program. We have 9 hard coded loops, one for each letter.

File: generate-words-loops
#! /usr/bin/env raku

unit sub MAIN (UInt :a(:$alphabet) where 1 <= $alphabet <= 35 = 0,
               UInt :l(:$limit)    where $limit > 0 = 9_000_000_000,
                    :v(:$verbose));

my $count      = 0;
my $last-word  = "";
my @alphabet   = (1..9, 'A'..'Z').flat.[^$alphabet];

say ": Alphabet: { @alphabet.join(", ") }" if $verbose;

ALPHABET:                                    # [1] 
for @alphabet -> $first                      # [2]
{
  register($first);                          # [3]
  
  for @alphabet -> $second
  {
    my $two = $first ~ $second;              # [4]

    register($two);

    for @alphabet -> $third
    {
      my $three = $two ~ $third;

      register($three);
    
      for @alphabet -> $fourth
      {
        my $four = $three ~ $fourth;

        next if $four ~~ /(.) {} :my $c=$0; <?after $c ** 4> /;  # [5]

        register($four);

        for @alphabet -> $fifth
        {
          my $five = $four ~ $fifth;

          next if $five ~~ /(.) {} :my $c=$0; <?after $c ** 4> /;
	  
          register($five);

          for @alphabet -> $sixth
          {
            my $six = $five ~ $sixth;

            next if $six ~~ /(.) {} :my $c=$0; <?after $c ** 4> /;

            register($six); 
	    
            for @alphabet -> $seventh
            {
              my $seven = $six ~ $seventh;

              next if $seven ~~ /(.) {} :my $c=$0; <?after $c ** 4> /;

              register($seven);

              for @alphabet -> $eighth
              {
                my $eight = $seven ~ $eighth;

                next if $eight ~~ /(.) {} :my $c=$0; <?after $c ** 4> /;

                register($eight); 

                for @alphabet -> $ninth
                {
                  my $nine = $eight ~ $ninth;

                  next if $nine ~~ /(.) {} :my $c=$0; <?after $c ** 4> /;

                  register($nine);
                }
              }
            }
          }
        }
      }
    }
  }
}

sub register ($word)                          # [6]
{
  $count++;                                   # [7]
  $last-word = $word;                         # [8]
  say ": Word: $word (#$count)" if $verbose;
  last ALPHABET if $count >= $limit;          # [9]
}

say "$count words (last '$last-word' with length { $last-word.chars } \
     stopped at  target: { (1000000 * $count/$limit).Int / 10000 }%)";

[1] Use this label to exit the whole loop in one go (when we have reached the target number of words; see [9]).

[2] Iterate over the first character in the new word,

[3] and register it (see [6]).

[4] We build up the word by appending the current letter to the word from the prior loop.

[5] Now we have four characters in the word, the shortest word length where and the succession rule can apply.

[6] Register the word;

[7] • increase the counter.

[8] • save the word (for the final summary line).

[9] • we can exit a loop, from within a procedure called from the loop. This is really nice, and/or magical...

We get the exact same order as in the recursive version:

$ ./generate-words-loops -a=10 -l=15 -v
: Alphabet: 1, 2, 3, 4, 5, 6, 7, 8, 9, A
: Word: 1 (#1)
: Word: 11 (#2)
: Word: 111 (#3)
: Word: 1112 (#4)
: Word: 11121 (#5)
: Word: 111211 (#6)
: Word: 1112111 (#7)
: Word: 11121112.. (#8)
: Word: 111211121 (#9)
: Word: 111211122 (#10)
: Word: 111211123 (#11)
: Word: 111211124 (#12)
: Word: 111211125 (#13)
: Word: 111211126 (#14)
: Word: 111211127 (#15)
15 words (last '111211127' with length 9 stopped at  target: 100%)

We will discuss the efficiency (time usage) of the different programs in the next part.

[ Index | Introduction | Base | Recursion | Loops | Summary | RakuConf ]