Perl Weekly Challenge 70: Character Swapping and Gray Code Sequence

These are some answers to the Week 70 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Task 1: Character Swapping

You are given a string $S of size $N.

You are also given swap count $C and offset $O such that $C >= 1, $O >= 1, $C <= $O and $C + $O <= $N.

Write a script to perform character swapping like below:

$S[ 1 % $N ] <=> $S[ (1 + $O) % $N ]
$S[ 2 % $N ] <=> $S[ (2 + $O) % $N ]
$S[ 3 % $N ] <=> $S[ (3 + $O) % $N ]
...
...
$S[ $C % $N ] <=> $S[ ($C + $O) % $N ]

Example 1:

Input:
    $S = 'perlandraku'
    $C = 3
    $O = 4

Character Swapping:
    swap 1: e <=> n = pnrlaedraku
    swap 2: r <=> d = pndlaerraku
    swap 3: l <=> r = pndraerlaku

Output:
    pndraerlaku

Character Swapping in Raku

To access the individual characters of a string, Raku offers two basic mechanisms: the substr function, or the ability to split a string into an array of individual characters, using the split or comb built-in routines.

However, since this task is fairly easy, I thought there would be more fun to create a postcircumfix [ ] operator for indexing strings with subscripts the way it is done, for example, in C or in Python. We even built this multi operator also for slices, used with ranges, although it is not used in this program. Note that the way it is defined, it is not possible to use it on the left-hand side of an assignment (in other words you can read the character at a given position in a string, but you cannot modify a character in the string).

use v6;
subset Non0 of Int where * > 0;

# For fun, indexing strings with subscripts
multi sub postcircumfix:<[ ]> (Str $s, Int $n) {
    substr-rw $s, $n, 1;
}
# Not used here, but more fun
multi sub postcircumfix:<[ ]> (Str $s, Range $r) {
    substr-rw $s, $r;
}

sub MAIN ($s is copy, Non0 $c = 3, Non0 $o = 4) {
    my $n = $s.chars;
    die "Invalid values" if $c + $o > $n;
    for 1..$c -> $i {
        my $tmp = $s[$i % $n];
        substr-rw($s, $i % $n, 1) = $s[($i + $o) % $n];
        substr-rw($s, ($i + $o) % $n, 1) = $tmp;
    }
    say $s
}

Running it using the default values displays the following output:

$ raku char-swap.raku PerlAndRaku
PndRAerlaku

Character Swapping in Perl

With Perl, we can’t really create a new operator as in Raku . So we’ll use the substr built-in.

use strict;
use warnings;
use feature qw /say/;

my ($s, $c, $o) = @ARGV;
my $n = length $s;
die "Invalid values" if $c < 1 or $o < 1 or $c + $o > $n;
for my $i (1..$c) {
    my $tmp = substr $s, $i % $n, 1;
    substr($s, $i % $n, 1) = substr $s, ($i + $o) % $n, 1;
    substr($s, ($i + $o) % $n, 1) = $tmp;
}
say $s;

This program displays the following output:

$ perl char-swap.pl PerlAndRaku 3 4
PndRAerlaku

Task 2: Gray Code Sequence

You are given an integer 2 <= $N <= 5.

Write a script to generate $N-bit gray code sequence.

2-bit Gray Code Sequence

[0, 1, 3, 2]

To generate the 3-bit Gray code sequence from the 2-bit Gray code sequence, follow the step below:

2-bit Gray Code sequence
[0, 1, 3, 2]

Binary form of the sequence
a) S1 = [00, 01, 11, 10]

Reverse of S1
b) S2 = [10, 11, 01, 00]

Prefix all entries of S1 with '0'
c) S1 = [000, 001, 011, 010]

Prefix all entries of S2 with '1'
d) S2 = [110, 111, 101, 100]

Concatenate S1 and S2 gives 3-bit Gray Code sequence
e) [000, 001, 011, 010, 110, 111, 101, 100]

3-bit Gray Code sequence
[0, 1, 3, 2, 6, 7, 5, 4]

Example:

Input: $N = 4

Output: [0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8]

I don’t see any compelling reason to start with a two-bit Gray code sequence, we can start with a one bit sequence: [0, 1], and thus make the program slightly more general.

Gray Codes in Raku

We just follow the steps described in the task specification.

use v6;
my @gray = [0], [0, 1]; # No need to have [1, 2, 4, 3] here

sub next-gray (Int $in) {
    my $fmt = "%0" ~ $in ~ "s"; # build the formatting string
    my @s1 = map { .fmt('%b').fmt($fmt) }, | @gray[$in];
    my @s2 = reverse map { '1' ~ $_ }, @s1;
    @s1 = map { '0' ~ $_ }, @s1;
    my @gray-seq = |@s1, |@s2;
    my @result = map { .parse-base(2) },  @gray-seq;
}

sub MAIN ($n where 1 <= * <= 5) { # we can start at 1
    for 1..^$n -> $i {
        @gray[$i+1] = next-gray $i;
    }
    say @gray[$n];
    say @gray;
}

This program displays the expected result:

$ raku gray.raku 4
[0 1 3 2 6 7 5 4 12 13 15 14 10 11 9 8]

I’m late and don’t have time to do the equivalent Perl program, although it should really not be a big deal to port the above program to Perl (except possibly the mildly difficult binary to decimal conversion, but I already know how to do that, having done that before with the unpack built-in).

Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on Sunday, August 2, 2020. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.