Especially Even
with Raku

by Arne Sommer

Especially Even with Raku

[215] Published 18. December 2022.

This is my response to The Weekly Challenge #195.

Challenge #195.1: Special Integers

You are given a positive integer, $n > 0.

Write a script to print the count of all special integers between 1 and $n.

An integer is special when all of its digits are unique.

Example 1:
Input: $n = 15
Output: 14 as except 11 all other integers between 1 and 15 are spcial.
Example 2:
Input: $n = 35
Output: 32 as except 11, 22, 33 all others are special.

This resembles last week's Frequency Equalizer (#194.2), with calculation of character frequencies within a string. The Bag approach is equally suitable here:

File: special-integers
unit sub MAIN (Int $n where $n > 0, :v(:$verbose)); # [1]

my $count = 0;                                      # [2]

for 1 .. $n -> $candidate                           # [3]
{
  my $special = is-special($candidate);             # [4]
  $count++ if $special;                             # [6]
  say ": $candidate { "special" if $special }" if $verbose;
}

say $count;                                         # [7]

sub is-special ($integer)                           # [5]
{
  return all($integer.comb.Bag.values) == 1;        # [5]
}

[1] Ensure that we get a positive integer.

[2] We are going to count the special integers, one at a time.

[3] Iterate over the values in the range (1 - upper limit).

[4] Is it a special integer?

[5] Turn the number into a list of single characters (with comb). Then turn that list into a Bag, a hash like structure where the values in the input list is the keys, and the frequency of each element is the value. Then we use an all junction to ensure that all the frequencies (with values) are equal to 1.

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

See docs.raku.org/routine/all for more information about the all Junction.

[6] Add to the counter, if it is special.

[7] Print the count.

Running it:

$ ./special-integers 15
14

$ ./special-integers 35
32

Looking good.

With verbose mode:

$ ./special-integers -v 15
: 1 special
: 2 special
: 3 special
: 4 special
: 5 special
: 6 special
: 7 special
: 8 special
: 9 special
: 10 special
: 11 
: 12 special
: 13 special
: 14 special
: 15 special
14

$ ./special-integers -v 35
: 1 special
: 2 special
: 3 special
: 4 special
: 5 special
: 6 special
: 7 special
: 8 special
: 9 special
: 10 special
: 11 
: 12 special
: 13 special
: 14 special
: 15 special
: 16 special
: 17 special
: 18 special
: 19 special
: 20 special
: 21 special
: 22 
: 23 special
: 24 special
: 25 special
: 26 special
: 27 special
: 28 special
: 29 special
: 30 special
: 31 special
: 32 special
: 33 
: 34 special
: 35 special
32

Challenge #195.2: Most Frequent Even

You are given a list of numbers, @list.

Write a script to find most frequent even numbers in the list. In case you get more than one even numbers then return the smallest even integer. For all other case, return -1.

Example 1:
Input: @list = (1,1,2,6,2)
Output: 2 as there are only 2 even numbers 2 and 6 and of those 2
  appears the most.
Example 2:
Input: @list = (1,3,5,7)
Output: -1 since no even numbers found in the list
Example 3:
Input: @list = (6,4,4,6,1)
Output: 4 since there are only two even numbers 4 and 6. They both
  appears the equal number of times, so pick the smallest.
File: most-frequent-even
#! /usr/bin/env raku

unit sub MAIN (*@list where @list.elems > 0       # [1]
  && all(@list) ~~ /^<[1..9]><[0..9]>*$/, :v(:$verbose));

my @even = @list.grep: * %% 2;                    # [2]

if @even.elems                                    # [3]
{
  my $bag    = @even.Bag;                         # [4]
  my $max    = $bag.values.max;                   # [5]
  my $res    = $bag.grep({ $_.value == $max });   # [6]
  my @sorted = $res>>.key.sort;                   # [7]

  if $verbose
  {
    say ":Even: @even[]";                         # [4a]
    say ":Max: $max";                             # [5a]
    say ":Sorted: @sorted[]";                     # [7a]
  }

  say @sorted.first;                              # [8]
}
else
{
  say ":No even integers" if $verbose;
  say -1;                                         # [9]
}

[1] Ensure at least 1 element, and that all of them are positive integers.

[2] Get the even integers, using grep and the divisibilty operator %%.

See docs.raku.org/routine/%% for more information about the Divisibility Operator %%.

[3] Do we have any even elements at all?

[4] Turn the values into a Bag.

[5] Get the highest frequency (the value part of the Bag).

[6] Get the Bag entries with the highest frequency (as calculated in [5]). There may be more than one.

[7] Get all the keys (i.e. integers from @list), applied individually on each Bag entry (a Pair) with >>.key, in sorted order.

[8] The first one in either the only one, or the lowest value.

[9] No even elements? Print «-1» and we are done.

Running it:

$ ./most-frequent-even 1 1 2 6 2
2

$ ./most-frequent-even 1 3 5 7 
-1

$ ./most-frequent-even 6 4 4 6 1
4

Looking good.

With verbose mode:

$ ./most-frequent-even -v 1 1 2 6 2
:Even: 2 6 2
:Max: 2
:Sorted: 2
2

$ ./most-frequent-even -v 1 3 5 7 
:No even integers
-1

$ ./most-frequent-even -v 6 4 4 6 1
:Even: 6 4 4 6
:Max: 2
:Sorted: 4 6
4

And that's it.