This is my response to the Perl Weekly Challenge #45.
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:
The code message would be as below:
Write a script that accepts a message from command line and prints the equivalent
coded message.
|
#! /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
#! /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
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.
|
#! /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
#! /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.