Mountain Separator
with Raku

by Arne Sommer

Mountain Separator with Raku

[379] Published 6. January 2025.

This is my response to The Weekly Challenge #355.

#355.1 Thousand Separator You are given a positive integer, $int.

Write a script to add thousand separator, , and return as string.

Example 1:
Input: $int = 123
Output: "123"
Example 2:
Input: $int = 1234
Output: "1,234"
Example 3:
Input: $int = 1000000
Output: "1,000,000"
Example 4:
Input: $int = 1
Output: "1"
Example 5:
Input: $int = 12345
Output: "12,345"
File: thousand-separator
#! /usr/bin/env raku

unit sub MAIN (Int $int where $int > 0);  # [1]

$int.flip.comb.rotor(3, :partial)>>.join.join(",").flip.say;
#    [2]  [3]  [4] ############# [5] ### [6] #####  [7] [8]

[1] Ensure a positive integer.

[2] Reverse the string with flip. (And not with reverse, as the that one works on arrays. Reversing a string (which in this case is an array with one element) does not actually do anything.)

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

[3] Split the string into an array of individual digits.

[4] Use rotor to group three and three digits in subarrays. (This is from the end, remember, courtesy of flip.) The :partial adverb tells the function to return the remainder digits as a final subarray.

E.g.

> say <1 2 3 4 5 6 7 8>.rotor(3);           # -> ((1 2 3) (4 5 6))
> say <1 2 3 4 5 6 7 8>.rotor(3, :partial); # -> ((1 2 3) (4 5 6) (7 8))

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

[5] Join each subarray into a string for each one.

[6] Join the strings, separated by a comma.

[7] Flip the string.

[8] And print it.

Running it:

$ ./thousand-separator 123
123

$ ./thousand-separator 1234
1,234

$ ./thousand-separator 1000000
1,000,000

$ ./thousand-separator 1
1

$ ./thousand-separator 12345
12,345

Looking good.

No verbose mode for this one.

#355.2 Mountain Array You are given an array of integers, @ints.

Write a script to return true if the given array is a valid mountain array.

An array is mountain if and only if:  
1) arr.length >= 3
and
2) There exists some i with 0 < i < arr.length - 1 such that:
  arr[0] < arr[1]     < ... < arr[i - 1] < arr[i]
  arr[i] > arr[i + 1] > ... > arr[arr.length - 1]
Example 1:
Input: @ints = (1, 2, 3, 4, 5)
Output: false
Example 2:
Input: @ints = (0, 2, 4, 6, 4, 2, 0)
Output: true
Example 3:
Input: @ints = (5, 4, 3, 2, 1)
Output: false
Example 4:
Input: @ints = (1, 3, 5, 5, 4, 2)
Output: false
Example 5:
Input: @ints = (1, 3, 2)
Output: true
File: mountain-array
#! /usr/bin/env raku

unit sub MAIN (*@ints where @ints.elems >= 3 && all(@ints) ~~ Int,  # [1]
               :a(:$show-all),                                      # [2]
               :v(:$verbose) = $show-all);                          # [2a]

my $is-ok = False;                                                  # [3]

for 1 .. @ints.end -1 -> $i                                         # [4]
{
  my @left  = @ints[0..$i];                                         # [5]
  my @right = @ints[$i..*];                                         # [6]
  my $ok    = ( [<] @left ) && ( [>] @right );                      # [7]

  say ": i:$i : @left[] <-> @right[] = $ok" if $verbose;

  if $ok                                                            # [8]
  {
    $is-ok = True;                                                  # [8a]
    last unless $show-all;                                          # [8b]
  }
}

say $is-ok;                                                         # [9]

[1] All the values must be integers, and there must be at least 3 of them.

[2] Use the «-a» command line option to show all the possible partitions (mountain tops), even after a successful match. This option enables verbose mode as well, courtesy of the default value on the latter [2a].

[3] Assume failure by default.

[4] Iterate over the starting indices.

[5] Get the left mountain side,

[6] and the right one. Note that the (potential) top is included in both.

[7] Use the Reduction Metaoperator [] with < on the left and > on the right to ensure that the values are sorted in increasing (left) and decreasing (right) order.

See docs.raku.org/language/operators#Reduction_metaoperators for more information about the Reduction Metaoperator [].

[8] Do we have a mountain array? If so, set the flag [8a] and exit the loop (unless we used the «-a» command line option).

[9] Print the Boolean result.

Running it:

$ ./mountain-array 1 2 3 4 5
False

$ ./mountain-array 0 2 4 6 4 2 0
True

$ ./mountain-array 5 4 3 2 1
False

$ ./mountain-array 1 3 5 5 4 2
False

$ ./mountain-array 1 3 2
True

Looking good.

With verbose mode:

$ ./mountain-array -v 1 2 3 4 5
: i:1 : 1 2 <-> 2 3 4 5 = False
: i:2 : 1 2 3 <-> 3 4 5 = False
: i:3 : 1 2 3 4 <-> 4 5 = False
False

$ ./mountain-array -v 0 2 4 6 4 2 0
: i:1 : 0 2 <-> 2 4 6 4 2 0 = False
: i:2 : 0 2 4 <-> 4 6 4 2 0 = False
: i:3 : 0 2 4 6 <-> 6 4 2 0 = True
True

$ ./mountain-array -v 5 4 3 2 1
: i:1 : 5 4 <-> 4 3 2 1 = False
: i:2 : 5 4 3 <-> 3 2 1 = False
: i:3 : 5 4 3 2 <-> 2 1 = False
False

$ ./mountain-array -v 1 3 5 5 4 2
: i:1 : 1 3 <-> 3 5 5 4 2 = False
: i:2 : 1 3 5 <-> 5 5 4 2 = False
: i:3 : 1 3 5 5 <-> 5 4 2 = False
: i:4 : 1 3 5 5 4 <-> 4 2 = False
False

$ ./mountain-array -v 1 3 2
: i:1 : 1 3 <-> 3 2 = True
True

Only the second example gets more verbose output if we use the «-a» command line option. Note that the new information is pretty useless...

$ ./mountain-array -v 0 2 4 6 4 2 0
: i:1 : 0 2 <-> 2 4 6 4 2 0 = False
: i:2 : 0 2 4 <-> 4 6 4 2 0 = False
: i:3 : 0 2 4 6 <-> 6 4 2 0 = True
True

$ ./mountain-array -a 0 2 4 6 4 2 0
: i:1 : 0 2 <-> 2 4 6 4 2 0 = False
: i:2 : 0 2 4 <-> 4 6 4 2 0 = False
: i:3 : 0 2 4 6 <-> 6 4 2 0 = True
: i:4 : 0 2 4 6 4 <-> 4 2 0 = False
: i:5 : 0 2 4 6 4 2 <-> 2 0 = False
True

And that's it.