One Final
with Raku

by Arne Sommer

One Final with Raku

[347] Published 11. June 2025.

This is my response to The Weekly Challenge #325.

Challenge #325.1: Consecutive One

You are given a binary array containing only 0 or/and 1.

Write a script to find out the maximum consecutive 1 in the given array.

Example 1:
Input: @binary = (0, 1, 1, 0, 1, 1, 1)
Output: 3
Example 2:
Input: @binary = (0, 0, 0, 0)
Output: 0
Example 3:
Input: @binary = (1, 0, 1, 0, 1, 1)
Output: 2
File: consecutive-one
#! /usr/bin/env raku

subset BinaryDigit of Int where * eq any(0,1);    # [1]

unit sub MAIN (*@binary where @binary.elems >= 1  # [1b]
                 && all(@binary) ~~ BinaryDigit,  # [1a]
               :v(:$verbose));

my @ones = @binary.join.split(/0+/);              # [2]

say ": One strings: { @ones.join(",") }" if $verbose;

say @ones.max.chars;                              # [3]

[1] We define a custom type for binary digits with subset, and apply that to the input [1a] - of which there must be at least one [1b].

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

[2] We do not care about the zeroes, so can split the string we get with join (on the array) on the one or more zeroes regex /0+/. That gives us a list of strings with consecutive 1s.

[3] We are looking for the longest string in the array from [2]. That is also the highest value, so I use max to get it. The challenge wanted the number of digits, so we use chars to get that.

Running it:

$ ./consecutive-one 0 1 1 0 1 1 1
3

$ ./consecutive-one 0 0 0 0
0

$ ./consecutive-one 1 0 1 0 1 1
2

Looking good.

With verbose mode:

$ ./consecutive-one -v 0 1 1 0 1 1 1
: One strings: ,11,111
3

$ ./consecutive-one -v 0 0 0 0
: One strings: ,
0

$ ./consecutive-one -v 1 0 1 0 1 1
: One strings: 1,1,11
2

Note that the first comma in the first example is caused by the first zero in the input. That leads to an undefined value up front in the result, thus giving us the strange comma. It does not matter for the algorithm.

Note that the single comma in the second example has the same cause as the one above. This one also does not matter for the algorithm.

Challenge #325.2: Final Price

You are given an array of item prices.

Write a script to find out the final price of each items in the given array.

There is a special discount scheme going on. If there’s an item with a lower or equal price later in the list, you get a discount equal to that later price (the first one you find in order).

Example 1:
Input: @prices = (8, 4, 6, 2, 3)
Output: (4, 2, 4, 2, 3)

Item 0:
The item price is 8.
The first time that has price <= current item price is 4.
Final price = 8 - 4 => 4

Item 1:
The item price is 4.
The first time that has price <= current item price is 2.
Final price = 4 - 2 => 2

Item 2:
The item price is 6.
The first time that has price <= current item price is 2.
Final price = 6 - 2 => 4

Item 3:
The item price is 2.
No item has price <= current item price, no discount.
Final price = 2

Item 4:
The item price is 3.
Since it is the last item, so no discount.
Final price = 3
Example 2:
Input: @prices = (1, 2, 3, 4, 5)
Output: (1, 2, 3, 4, 5)
Example 3:
Input: @prices = (7, 1, 1, 5)
Output: (6, 0, 1, 5)

Item 0:
The item price is 7.
The first time that has price <= current item price is 1.
Final price = 7 - 1 => 6

Item 1:
The item price is 1.
The first time that has price <= current item price is 1.
Final price = 1 - 1 => 0

Item 2:
The item price is 1.
No item has price <= current item price, so no discount.
Final price = 1

Item 3:
The item price is 5.
Since it is the last item, so no discount.
Final price = 5
File: final-price
#! /usr/bin/env raku

unit sub MAIN (*@prices where @prices.elems >= 1             # [1]
	         && all(@prices) ~~ UInt,                    # [1a]
               :v(:$verbose));

my @final;                                                   # [2]

for ^@prices.elems -> $i                                     # [3]
{
  my $price = @prices[$i];                                   # [4]
  my $lower = @prices[$i+1 .. Inf].grep(* <= $price).first;  # [5]

  if defined $lower                                          # [6]
  {
    my $new = $price - $lower;                               # [6a]
    
    say ": Price: $price - lower $lower = $new" if $verbose;
    
    @final.push: $new;                                       # [6b]
  }
  else                                                       # [7]
  {
    say ": Price: $price -> no lower = $price" if $verbose;

    @final.push: $price;                                     # [7a]
  }  
}

say @final;

[1] A slurpy array with at least one element, all of which must be unsigned inters [1a].

See docs.raku.org/type/UInt for more information about the UInt type.

[2] The discounted prices will end up here.

[3] Iterate over all the price indices to process.

[4] Get the current price.

[5] Get all the prices to the right of it (the current price), then use grep to keep only those that are equal or lower to it (the current price), and finally use first to get the first (or leftmost) one.

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

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

[6] Do we have a lower or equal number (i.e. a discount base). If so, compute the discounted value [6a] and add it to the new price array.

[7] No discount? Use the price itself (undiscounted) [7a].

Running it:

$ ./final-price 8 4 6 2 3
[4 2 4 2 3]

$ ./final-price 1 2 3 4 5
[1 2 3 4 5]

$ ./final-price 7 1 1 5
[6 0 1 5]

Looking good.

With verbose mode:

$ ./final-price -v 8 4 6 2 3
: Price: 8 - lower 4 = 4
: Price: 4 - lower 2 = 2
: Price: 6 - lower 2 = 4
: Price: 2 -> no lower = 2
: Price: 3 -> no lower = 3
[4 2 4 2 3]

$ ./final-price -v 1 2 3 4 5
: Price: 1 -> no lower = 1
: Price: 2 -> no lower = 2
: Price: 3 -> no lower = 3
: Price: 4 -> no lower = 4
: Price: 5 -> no lower = 5
[1 2 3 4 5]

$ ./final-price -v 7 1 1 5
: Price: 7 - lower 1 = 6
: Price: 1 - lower 1 = 0
: Price: 1 -> no lower = 1
: Price: 5 -> no lower = 5
[6 0 1 5]

And that's it.