Reverse Broken Again
with Raku

by Arne Sommer

Reverse Broken Again with Raku

[365] Published 4. October 2025.

This is my response to The Weekly Challenge #341.

Challenge #341.1: Broken Keyboard

You are given a string containing English letters only and also you are given broken keys.

Write a script to return the total words in the given sentence can be typed completely.

Example 1:
Input: $str = 'Hello World', @keys = ('d')
Output: 1

With broken key 'd', we can only type the word 'Hello'.
Example 2:
Input: $str = 'apple banana cherry', @keys = ('a', 'e')
Output: 0
Example 3:
Input: $str = 'Coding is fun', @keys = ()
Output: 3

No keys broken.
Example 4:
Input: $str = 'The Weekly Challenge', @keys = ('a','b')
Output: 2
Example 5:
Input: $str = 'Perl and Python', @keys = ('p')
Output: 1

It follows from the examples that spaces should be allowed in the strings, even though thay cannot in any way be considered English letters.

File: broken-keyboard
#! /usr/bin/env raku

unit sub MAIN (:s(:$str)  where $str ~~ /^ <[a..z A..Z \s]>+ $/,   # [1]
               :k(:$keys) where $keys ~~ /^ <[a..z]>* $/,          # [2]
               :v(:$verbose));

my @words     = $str.lc.words;                                     # [3]
my @keys      = $keys.lc.comb;                                     # [4]
my @typeable  = @words.grep({ .contains(none(@keys)) });           # [5]

die "Duplicate keys" if @keys.repeated;                            # [6]

if $verbose
{
  say ": Words: { @words.map({ "'$_'" }).join(", ") }";
  say ": Keys: { @keys.map({ "'$_'" }).join(", ") }";
  say ": Typeable words: { @typeable.map({ "'$_'" }).join(", ") }";
}

say @typeable.elems;                                              # [7]

[1] A named argument for the string, containing English letters (upper- as well as lowercase) and spaces. With at least one character.

[2] Another named argument; this one for the broken keys - as a string. It can have zero length, to satisfy the third example.

[3] Convert the string into lowercase (to satisfy the fifth example) with lc, and get the words with words.

See docs.raku.org/routine/lc for more information about lc.

See docs.raku.org/routine/words for more information about words.

[4] Convert the key string to lowercase, and split them into an array of individual characters with comb.

See docs.raku.org/routine/comb for more information about comb.

[5] Get the typeable words; those that does not contain any of the broken keys - or «contains none of the broken keys» as the program expresses it.

See docs.raku.org/routine/contains for more information about contains.

[6] Terminate the program if we have duplicate keys.

See docs.raku.org/routine/repeated for more information about repeated.

[7] Print the number of typeable words.

Running it:

$ ./broken-keyboard -s='Hello World' -k=d
1

$ ./broken-keyboard -s='apple banana cherry' -k=ae
0

$ ./broken-keyboard -s='Coding is fun' -k=''
3

$ ./broken-keyboard -s='The Weekly Challenge' -k=ab
2

$ ./broken-keyboard -s='Perl and Python' -k=p
1

Looking good.

With verbose mode:

$ ./broken-keyboard -v -s='Hello World' -k=d
: Words: 'hello', 'world'
: Keys: 'd'
: Typeable words: 'hello'
1

$ ./broken-keyboard -v -s='apple banana cherry' -k=ae
: Words: 'apple', 'banana', 'cherry'
: Keys: 'a', 'e'
: Typeable words: 
0

$ ./broken-keyboard -v -s='Coding is fun' -k=''
: Words: 'coding', 'is', 'fun'
: Keys: 
: Typeable words: 'coding', 'is', 'fun'
3

$ ./broken-keyboard -v -s='The Weekly Challenge' -k=ab
: Words: 'the', 'weekly', 'challenge'
: Keys: 'a', 'b'
: Typeable words: 'the', 'weekly'
2

$ ./broken-keyboard -v -s='Perl and Python' -k=p
: Words: 'perl', 'and', 'python'
: Keys: 'p'
: Typeable words: 'and'
1

Challenge #341.2: Reverse Prefix

You are given a string, $str and a character in the given string, $char.

Write a script to reverse the prefix upto the first occurrence of the given $char in the given string $str and return the new string.

Example 1:
Input: $str = "programming", $char = "g"
Output: "gorpramming"

Reverse of prefix "prog" is "gorp".
Example 2:
Input: $str = "hello", $char = "h"
Output: "hello"
Example 3:
Input: $str = "abcdefghij", $char = "h"
Output: "hgfedcbaij"
Example 4:
Input: $str = "reverse", $char = "s"
Output: "srevere"
Example 5:
Input: $str = "perl", $char = "r"
Output: "repl"
File: reverse-prefix
#! /usr/bin/env raku

unit sub MAIN (:s($str)  where $str.chars > 0,    # [1]
               :c($char) where $char.chars == 1,  # [2]
               :v(:$verbose));

my $index = $str.index($char);                    # [3]

if $index                                         # [4]
{
  my $pre     = $str.substr(0, $index +1);        # [5]
  my $flipped = $pre.flip;                        # [6]
  my $post    = substr($index +1);                # [7]

  if $verbose
  {
    say ": Index: $index";
    say ": Pre: '$pre'";
    say ": Flipped: '$flipped'";
    say ": Post: '$post'"; 
  }

  say $flipped ~ $post;                          # [8]
}
else                                             # [9]
{
  say $str;                                      # [9a]
}

[1] A named argument for the string, with at least one character.

[2] A named argument for the single character. The examples imply letters only, and lowercase to boot, but the challenge text does not say anything about this. So I have chosen to allow anything.

[3] Use index to look for the first occurence of the character in the string. The return value is the index, or NIL if not found.

See docs.raku.org/routine/index for more information about index.

Note that NIL and index 0 will both fail to trigger the if test in [4]. The former is as intended, but the latter is a potential problem. This can be remedied by using if $index.defined insted. But this is not an issue in this program, as a match at index 0 means that we should flip the very first character. That does not accomplish anyhthing, thus the missing defined actually makes sense here.

[4] Have we found the character (at index 1 or later; as discussed above).

[5] Get the part of the string upto (and including) the character.

[6] Reverse that string with flip. (Note that reverse works on arrays, and will not do anything on this one-element array.)

See docs.raku.org/routine/flip for more information about flip.

[7] Get the string after the flipped part, possibly an empty string if the flipped part gobbled up the entire string.

[8] Print the flipped part and the non-flipped remainder.

[9] No match (from [4])? Print the original string.

Note that [8] and [9] could have been combined. But then we would have to move the declarations in [6] and [7] out of the if-block.

Running it:

$ ./reverse-prefix -s="programming" -c=g
gorpramming

$ ./reverse-prefix -s="hello" -c=h
hello

$ ./reverse-prefix -s="abcdefghij" -c=h
hgfedcbaij

$ ./reverse-prefix -s="reverse" -c=s
srevere

$ ./reverse-prefix -s="perl" -c=r
repl

Looking good.

With verbose mode:

$ ./reverse-prefix -v -s="programming" -c=g
: Index: 3
: Pre: 'prog'
: Flipped: 'gorp'
: Post: 'ramming'
gorpramming

$ ./reverse-prefix -v -s="hello" -c=h
hello

$ ./reverse-prefix -v -s="abcdefghij" -c=h
: Index: 7
: Pre: 'abcdefgh'
: Flipped: 'hgfedcba'
: Post: 'ij'
hgfedcbaij

$ ./reverse-prefix -v -s="reverse" -c=s
: Index: 5
: Pre: 'revers'
: Flipped: 'srever'
: Post: 'e'
srevere

$ ./reverse-prefix -v -s="perl" -c=r
: Index: 2
: Pre: 'per'
: Flipped: 'rep'
: Post: 'l'
repl

And that's it.