Square Dumper with Raku

by Arne Sommer

Square Dumper with Raku

[54] Published 30. January 2020.

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

Challenge #45.1: Square Secret Code

The square secret code mechanism first removes any space from the original message. Then it lays down the message in a row of 8 columns. The coded message is then obtained by reading down the columns going left to right.

For example, the message is “The quick brown fox jumps over the lazy dog”.

Then the message would be laid out as below:
thequick
brownfox
jumpsove
rthelazy
dog
The code message would be as below:

tbjrd hruto eomhg qwpe unsl ifoa covz kxey
Write a script that accepts a message from command line and prints the equivalent coded message.

File: square-secret-code
#! /usr/bin/env raku

unit sub MAIN ($string is copy         # [1]
                  = "The quick brown fox jumps over the lazy dog",
               :$verbose);             # [3]

$string ~~ tr/" "//;                   # [2]

say ": { $string.lc }" if $verbose;    # [3]

my @a = $string.lc.comb;               # [4]

for 0 .. 7 -> $word                    # [5]                        
{
  my $index = $word;                   # [6]
  loop                                 # [7]
  {
    @a[$index]:exists                  # [8]
     ?? print  @a[$index]              # [8a]
     !! ( print " "; last);            # [8b]

   $index += 8;                        # [9]
  }
}

say "";                                # [10]

[1] The default text, if not specified. Note «is copy» so that we can change it.

[2] Remove all spaces in the variable. I have quotes the space so that it is taken literally

[3] Print the string so far, if we have enabled verbose mode.

[4] Lower case the string, and split it into individual characters. I find arrays easier to work with than substrings.

[5] We have 8 words to compute (corresponding to the columns in the laid out message in challenge), with indices 0 to 7.

[6] Start with the column number as index,

[7] Iterate forever.

[8] Do we have an element with the given index? If yes, print the character. If not, print a space (before starting with the next word) and exit the loop.

[9] Add 8 to the index, so that we get the next character in the next iteration.

[10] Add a newline.

Running it:

$ raku square-secret-code --verbose
: thequickbrownfoxjumpsoverthelazydog 
tbjrd hruto eomhg qwpe unsl ifoa covz kxey 

Looking good.

Note that the algorithm does not round trip:

$ raku square-secret-code "tbjrd hruto eomhg qwpe unsl ifoa covz kxey"
ttwfx bopoe jeeay rouc dmno hhsv rglz uqik 

Perl Bonus

The program is longer in Perl, but not very much:

File: square-secret-code-perl
#! /usr/bin/env perl

my $string = $ARGV[0] || "The quick brown fox jumps over the lazy dog";
                                 # [1]
$string =~ tr/ //d;

my @a = split(//, lc $string);   # [2]

@a.shift;                        # [2a]
@a.pop;                          # [2b]

for my $word (0 .. 7)
{
  my $index = $word;
  while (1)
  {
    defined $a[$index]
      ? print $a[$index]
      : print(" ") && last;      # [3]

   $index += 8;
  }
}

print "\n";

[1] Verbose mode had to go, as optionally named parameters isn't readily available.

[2] Perl doesn't have Raku's handy «comb», but «split» on an empty pattern does almost the same. Almost, as it adds empty elements before and after the real content. We get rid if the first one with «shift» [2a], and the last one with «pop» [2b].

[3] Bundling a block (either a real one with «{» and «}» or just grouping it with «(» and «)» as I did in Raku, is not possible in Perl. So I had to merge the two expressions with «&&». This again caused parens around the quotes space to prevent presedence problems.

And it works:

$ perl square-secret-code-perl
tbjrd hruto eomhg qwpe unsl ifoa covz kxey 

Challenge #45.2: Source Dumper

Write a script that dumps its own source code. For example, say, the script name is ch-2.pl then the following command should return nothing.

$ perl ch-2.pl | diff - ch-2.pl

File: source-dumper
#! /usr/bin/env raku

print $?FILE.IO.slurp;      # [2]

[1] The «$?FILE» (read only) compile time variable holds the name of the current program file (as a string). We invoke «.IO» on it to get an «IO»-object, as we can apply «slurp» on that object to get the entire file in one go, The slurped text have the newlines intact, so we use «print» to display it as is.

Running it:

$ raku source-dumper 
#! /usr/bin/env raku

print $?FILE.IO.slurp;
$ raku source-dumper | diff - source-dumper 

Perl Bonus

The program is much longer in pure Perl (i.e. without the help of a module as «File::Slurp»):

#! /usr/bin/env perl

use strict;
use warnings;

my $file = $0;

if (open(my $fh, $file))
{
  while (my $row = <$fh>)
  {
    print $row;
  }
  close $fh;
}

I have dropped the encoding parameter, so the text is read as ISO Latin-1 (also called ISO-8859-1) and not Unicode (as in Raku).

if (open(my $fh, '<:encoding(UTF-8)', $file))

But this does not matter here, as we do not have any fancy characters (as an American would say) in the file that could cause mayhem.

It is better to use a module. «File::Slurp» is from 2003, so I chose «File::Slurper» from 2014 instead.

File: source-dumper-perl-module
#! /usr/bin/env perl

use File::Slurper 'read_text';

use strict;
use warnings;

print read_text($0);

Both versions work as expected:

$ raku source-dumper-perl | diff - source-dumper-perl
$ raku source-dumper-perl-module | diff - source-dumper-perl-module

And that's it.