This is my response to The Weekly Challenge #193.
$n > 0
.
$n
.
Input: $n = 2
Output: 00, 11, 01, 10
Example 2:
Input: $n = 3
Output: 000, 001, 010, 100, 111, 110, 101, 011
#! /usr/bin/env raku
unit sub MAIN (Int $n where $n > 0);
my $decimal = 0; # [1]
my @binary; # [2]
loop # [3]
{
my $binary = $decimal.fmt('%0' ~ $n ~ 'b'); # [4]
last if $binary.chars > $n; # [5]
@binary.push: $binary; # [6]
$decimal++; # [7]
}
say @binary.join(", "); # [8]
[1] The loop variable (in decimal), from zero up to the limit.
[2] The binary numbers will end up here, as strings.
[3] An eternal loop. Note the exit clause in [5].
[4] Convert the loop value to binary (with fmt
), as a zero padded
string giving the total length as $n
).
See
docs.raku.org/routine/fmt for
more information about fmt
.
[5] exit the loop if we aquired too many digits in the binary representation.
[6] Add the (binary, zero padded) value to the list.
[7] The next decimal value.
[8] Print the list nicely.
Running it:
$ ./binary-string-loop 2
00, 01, 10, 11
$ ./binary-string-loop 3
000, 001, 010, 011, 100, 101, 110, 111
I have chosen to print the values sorted by value (increasing). The challenge printed them in a rather random order (e.g. the highest value).
It is certainly possible to make the program shorter. Here it is as a one liner, if you ignore the crash-bang and unit lines:
File: binary-string-map
#! /usr/bin/env raku
unit sub MAIN (Int $n where $n > 0);
say (0 .. (1 x $n).parse-base(2)).map( *.fmt('%0' ~ $n ~ 'b') ).join(", ");
### # 1 ########################## # 2 ######################### # 3 #######
[1]
Start with all the decimal values (from "0" to e.g. "8" (binary "1111")
if $n == 4, using the string repetition operator x
and parse-base
).
See
docs.raku.org/routine/x for more information about the string repetition
operator x
.
See
docs.raku.org/routine/parse-base
for more information about parse-base
.
[2] Convert to binary, zero padded.
[3] Join the values, and let the say
up front print the lot.
Running it gives the same result as first version:
$ ./binary-string-map 2
00, 01, 10, 11
$ ./binary-string-map 3
000, 001, 010, 011, 100, 101, 110, 111
@s
.
0
, i.e. a = 0, b = 1, ... z = 25
.
Input: @s = ("adc", "wzy", "abc")
Output: "abc"
Difference array for "adc" => [ d - a, c - d ]
=> [ 3 - 0, 2 - 3 ]
=> [ 3, -1 ]
Difference array for "wzy" => [ z - w, y - z ]
=> [ 25 - 22, 24 - 25 ]
=> [ 3, -1 ]
Difference array for "abc" => [ b - a, c - b ]
=> [ 1 - 0, 2 - 1 ]
=> [ 1, 1 ]
The difference array for "abc" is the odd one.
Example 2:
Input: @s = ("aaa", "bob", "ccc", "ddd")
Output: "bob"
Difference array for "aaa" => [ a - a, a - a ]
=> [ 0 - 0, 0 - 0 ]
=> [ 0, 0 ]
Difference array for "bob" => [ o - b, b - o ]
=> [ 14 - 1, 1 - 14 ]
=> [ 13, -13 ]
Difference array for "ccc" => [ c - c, c - c ]
=> [ 2 - 2, 2 - 2 ]
=> [ 0, 0 ]
Difference array for "ddd" => [ d - d, d - d ]
=> [ 3 - 3, 3 - 3 ]
=> [ 0, 0 ]
The difference array for "bob" is the odd one.
#! /usr/bin/env raku
unit sub MAIN (*@s where ( [==] @s>>.chars), :v(:$verbose)); # [1]
my %diff; # [2]
for @s -> $string # [3]
{
my @letters = $string.comb; # [4]
my @diff; # [5]
my $first; # [6]
my $second = @letters.shift; # [7]
while (@letters.elems) { # [8]
my $first = $second; # [9]
$second = @letters.shift; # [10]
my $diff = $second.ord - $first.ord; # [11]
@diff.push: $diff; # [12]
}
%diff{ @diff.join: " " }.push: $string; # [13]
say ": $string -> @diff[]" if $verbose;
}
say ":" ~ %diff.raku if $verbose;
say %diff.grep({ $_.value.elems == 1 }).map( *.value ).Str; # [14]
[1] Ensure that all the string lengths ( @s>>.chars
) are identical ([==]
).
Note the missing check on legal letters only. (The challenge does not actually say that we are limited
to lower case letters, though it implies it.)
[2] The difference array for each string will end up here.
[3] Iterate over each string.
[4] Get the individual letters.
[5] The difference values for the current string will end up here.
[6] The first value will be kept here,
[7] and the second one here. The initial state is messy, but will be corrected inside the loop.
[8] As long as we have more letters,
[9] Move the second one (from the previous iteration) up front, as the new first.
[10] Get a new second.
[11] Get the difference, using the unicode codepoints (from ord
).
See
docs.raku.org/routine/ord
for more information about ord
.
[12] Add that difference to the list.
[13] The difference string is the difference values added together (space separated). Note the use of
push
to add the value to a list - which we store in the hash.
[14] Print the string(s) that have a unique difference string (i.e. occur just once,
courtesy of the grep
). The map
gives us the value, and not the hash
pair (key, value)
Running it:
$ ./odd-string adc wzy abc
abc
$ ./odd-string aaa bob ccc ddd
bob
$ ./odd-string adc wzy abc 111
abc 111
With verbose mode:
$ ./odd-string -v aaa bob ccc ddd
: aaa -> 0 0
: bob -> 13 -13
: ccc -> 0 0
: ddd -> 0 0
:{"0 0" => $["aaa", "ccc", "ddd"], "13 -13" => $["bob"]}
bob
$ ./odd-string -v adc wzy abc 111
: adc -> 3 -1
: wzy -> 3 -1
: abc -> 1 1
: 111 -> 0 0
:{"0 0" => $[IntStr.new(111, "111")], "1 1" => $["abc"], "3 -1" => $["adc", "wzy"]}
abc 111
$ ./odd-string -v adc wzy abc 111 222
: adc -> 3 -1
: wzy -> 3 -1
: abc -> 1 1
: 111 -> 0 0
: 222 -> 0 0
:{"0 0" => $[IntStr.new(111, "111"), IntStr.new(222, "222")], "1 1" => $["abc"], "3 -1" => $["adc", "wzy"]}
abc
The two examples leave no room for confusion, but what if we do not have one string with an unique
difference string? As in my adc wzy abc 111
?
Or even worse, if we have something like these:
$ ./odd-string -v adc wzy abc def
: adc -> 3 -1
: wzy -> 3 -1
: abc -> 1 1
: def -> 1 1
:{"1 1" => $["abc", "def"], "3 -1" => $["adc", "wzy"]}
$ ./odd-string -v adc wzy abc def ghi
: adc -> 3 -1
: wzy -> 3 -1
: abc -> 1 1
: def -> 1 1
: ghi -> 1 1
:{"1 1" => $["abc", "def", "ghi"], "3 -1" => $["adc", "wzy"]}
My program does not handle these examples, as it is hard to decide what to do in these cases.
And that's it.