Time Alike
with Raku

by Arne Sommer

Time Alike with Raku

[372] Published 22. November 2025.

This is my response to The Weekly Challenge #348.

Challenge #348.1: String Alike

You are given a string of even length.

Write a script to find out whether the given string can be split into two halves of equal lengths, each with the same non-zero number of vowels.

Example 1:
Input: $str = "textbook"
Output: false

1st half: "text" (1 vowel)
2nd half: "book" (2 vowels)
Example 2:
Input: $str = "book"
Output: true

1st half: "bo" (1 vowel)
2nd half: "ok" (1 vowel)
Example 3:
Input: $str = "AbCdEfGh"
Output: true

1st half: "AbCd" (1 vowel)
2nd half: "EfGh" (1 vowel)
Example 4:
Input: $str = "rhythmmyth"
Output: false

1st half: "rhyth" (0 vowel)
2nd half: "mmyth" (0 vowel)
Example 5:
Input: $str = "UmpireeAudio"
Output: false

1st half: "Umpire" (3 vowels)
2nd half: "eAudio" (5 vowels)

The fourth example tells us that we should consider «Y» as a non-vowel. See e.g. en.wikipedia.org/wiki/Y for why (or when) it is, and why (or, again, when) it isn't.

File: string-alike
#! /usr/bin/env raku

unit sub MAIN ($str where $str.chars > 1 && $str.chars %% 2,  # [1]
               :y(:$y-is-a-vowel),                            # [2]
               :v(:$verbose));

my $first  = $str.substr(0, $str.chars / 2);                  # [3]
my $second = $str.substr($str.chars / 2);                     # [4]

my @vowels = $y-is-a-vowel                                    # [5]
               ?? <A E I O U Y a e i o u y>                   # [5a]
               !! <A E I O U a e i o u>;                      # [5b]

my @first  = $first.comb.grep:  * eq any @vowels;             # [6]
my @second = $second.comb.grep: * eq any @vowels;             # [6a]

if $verbose
{
  say ": First:  '$first' -> '{ @first.join }' size { @first.elems }";
  say ": Second: '$second' -> '{ @second.join }' size { @second.elems }";
}

say @first.elems == @second.elems > 0;                        # [7]

[1] A string with an even length, and at least one character (as zero is even).

[2] Use this command line option if you consider «Y» (and «y» to be pedantic) a vowel, and want the program do do the same.

[3] The first half of the input string.

[4] The second half.

[5] Use the vowel list including «Y» if you are so vowel-inclined.

[6] Convert the two halves into an array of individual characters, keeping the vowels only.

[7] Report success if the two vowel arrays have the same size, except if the size is zero (as given by the fourth example).

Running it:

$ ./string-alike textbook
False

$ ./string-alike book
True

$ ./string-alike AbCdEfGh
True

$ ./string-alike rhythmmyth
False

$ ./string-alike UmpireeAudio
False

Looking good.

With verbose mode:

$ ./string-alike -v textbook
: First:  'text' -> 'e' size 1
: Second: 'book' -> 'oo' size 2
False

$ ./string-alike -v book
: First:  'bo' -> 'o' size 1
: Second: 'ok' -> 'o' size 1
True

$ ./string-alike -v AbCdEfGh
: First:  'AbCd' -> 'A' size 1
: Second: 'EfGh' -> 'E' size 1
True

$ ./string-alike -v rhythmmyth
: First:  'rhyth' -> '' size 0
: Second: 'mmyth' -> '' size 0
False

$ ./string-alike -v UmpireeAudio
: First:  'Umpire' -> 'Uie' size 3
: Second: 'eAudio' -> 'eAuio' size 5
False

Let us revisit the fourth example, this time with «y» as a vowel:

$ ./string-alike -v -y rhythmmyth
: First:  'rhyth' -> 'y' size 1
: Second: 'mmyth' -> 'y' size 1
True

Challenge #348.2: Co(n)vert Time

You are given two strings, $source and $target, containing time in 24-hour time form.

Write a script to convert the source into target by performing one of the following operations:
  1. Add 1 minute
  2. Add 5 minutes
  3. Add 15 minutes
  4. Add 60 minutes
Find the total operations needed to get to the target.

Example 1:
Input: $source = "02:30"
       $target = "02:45"
Output: 1

Just one operation i.e. "Add 15 minutes".
Example 2:
Input: $source = "11:55"
       $target = "12:15"
Output: 2

Two operations i.e. "Add 15 minutes" followed by "Add 5 minutes".
Example 3:
Input: $source = "09:00"
       $target = "13:00"
Output: 4

Four operations of "Add 60 minutes".
Example 4:
Input: $source = "23:45"
       $target = "00:30"
Output: 3

Three operations of "Add 15 minutes".
Example 5:
Input: $source = "14:20"
       $target = "15:25"
Output: 2

Two operations, one "Add 60 minutes" and one "Add 5 minutes".

It should be obvious that the task is to use as few operations as possible.

File: convert-time
#! /usr/bin/env raku

subset HHMM where * ~~ /^ <[012]><[0..9]> ":" <[0..5]><[0..9]> $/;   # [1]

unit sub MAIN (HHMM $source, HHMM $target,                           # [2]
               :v(:$verbose));

my ($source-h, $source-m) = $source.split(":");                      # [3]
my ($target-h, $target-m) = $target.split(":");                      # [3a]

my $source-minutes = $source-h * 60 + $source-m;                     # [4]
my $target-minutes = $target-h * 60 + $target-m;                     # [4a]

die "Source > 23:59" if $source-minutes > 1439;                      # [5]
die "Target > 23:59" if $target-minutes > 1439;                      # [5a]

my $delta = $target-minutes - $source-minutes;                       # [6]

$delta += 60 * 24 if $delta < 0;                                     # [7]

my @polymod = $delta.polymod(5, 3, 4);                               # [8]

if $verbose
{
  say ": Source $source = $source-minutes min past midnight";
  say ": Target $target = $target-minutes min past midnight";
  say ": Target is the next day, adding 24x60 min = \
    { $target-minutes + 60*24 } min"
      if $target-minutes - $source-minutes < 0;
								     
  say ": Delta $delta min";
  say ": Add @polymod[0]x 1 min";                                   # [8a]
  say ": Add @polymod[1]x 5 min";
  say ": Add @polymod[2]x 15 min";
  say ": Add @polymod[3]x 60 min";
}

say @polymod.sum;                                                   # [9]

[1] A custom type for the time values. Note that «29:59» is allowed, but we will take care of such nonsense later (in [5]).

[2] Apply the custom type on the two time values.

[3] Split the two time values into the hour and minutes parts.

[4] Get the time in minutes (past midnight).

[5] We do not allow values higher than «23:59», or 1439 in minutes.

[6] Get the difference between the two times.

[7] Add a day (24 hour) to the target if it is before the source, as it is reasonable to assume that it is the next day. The fourth example does assume this to be the case.

[8] Use polymod to get the number of each time slice needed to get to the target value. The first argument in the call (5) is used to get the minutes into 5 minute chunks. The remainder from this integer division is the number of 1 minute slices. We divide the number of 5 minute slices by 3 to get the number of 15 minute slices, and that again by 4 to get the number of 60 minute slices. See the verbose mode code (in [8a]) for an explanation of how the result should be parsed [8a].

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

[9] The number of operations is the sum of all the operations.

Running it:

$ ./convert-time "02:30" "02:45"
1

$ ./convert-time "11:55" "12:15"
2

$ ./convert-time "09:00" "13:00"
4

$ ./convert-time "23:45" "00:30"
3

$ ./convert-time "14:20" "15:25"
2

Looking good.

With verbose mode:

$ ./convert-time -v "02:30" "02:45"
: Source 02:30 = 150 min past midnight
: Target 02:45 = 165 min past midnight
: Delta 15 min
: Add 0x 1 min
: Add 0x 5 min
: Add 1x 15 min
: Add 0x 60 min
1

$ ./convert-time -v "11:55" "12:15"
: Source 11:55 = 715 min past midnight
: Target 12:15 = 735 min past midnight
: Delta 20 min
: Add 0x 1 min
: Add 1x 5 min
: Add 1x 15 min
: Add 0x 60 min
2

$ ./convert-time -v "09:00" "13:00"
: Source 09:00 = 540 min past midnight
: Target 13:00 = 780 min past midnight
: Delta 240 min
: Add 0x 1 min
: Add 0x 5 min
: Add 0x 15 min
: Add 4x 60 min
4

$ ./convert-time -v "23:45" "00:30"
: Source 23:45 = 1425 min past midnight
: Target 00:30 = 30 min past midnight
: Target is the next day, adding 24x60 min = 1470 min
: Delta 45 min
: Add 0x 1 min
: Add 0x 5 min
: Add 3x 15 min
: Add 0x 60 min
3

$ ./convert-time -v "14:20" "15:25"
: Source 14:20 = 860 min past midnight
: Target 15:25 = 925 min past midnight
: Delta 65 min
: Add 0x 1 min
: Add 1x 5 min
: Add 0x 15 min
: Add 1x 60 min
2

And that's it.