Raku
Members

by Arne Sommer

Raku Members

[242] Published 24. June 2023.

This is my response to The Weekly Challenge #222.

Challenge #222.1: Matching Members

You are given a list of positive integers, @ints.

Write a script to find the total matching members after sorting the list increasing order.

Example 1:
Input: @ints = (1, 1, 4, 2, 1, 3)
Output: 3

Original list: (1, 1, 4, 2, 1, 2)
Sorted list  : (1, 1, 1, 2, 3, 4)

Compare the two lists, we found 3 matching members (1, 1, 2).
Example 2:
Input: @ints = (5, 1, 2, 3, 4)
Output: 0

Original list: (5, 1, 2, 3, 4)
Sorted list  : (1, 2, 3, 4, 5)

Compare the two lists, we found 0 matching members.
Example 3:
Input: @ints = (1, 2, 3, 4, 5)
Output: 5

Original list: (1, 2, 3, 4, 5)
Sorted list  : (1, 2, 3, 4, 5)

Compare the two lists, we found 5 matching members.
File: matching-members
#! /usr/bin/env raku

unit sub MAIN (*@ints where @ints.elems > 1 && all(@ints) ~~ Int
                        && all(@ints) > 0);                # [1]

say (@ints Z @ints.sort).grep( { $_[0] eq $_[1] }).elems;  # [2]
# D  # A ############### # B ##################### # C ## 

[1] At least two elements, all of them integers, and greater than zero.

[2] We start with the list itself and a sorted copy, merging them with the Z (zip) operator [A]. The result is a list with pair objects; one element from the original list, and one from the sorted one. Then we use grep to keep the pairs where the two values are equal [B]. Then we count the number of elements (i.e. pairs) in the resulting list [C] and print that number [D].

See docs.raku.org/routine/Z for more information about the infix zip operator Z.

Running it:

$ ./matching-members 1 1 4 2 1 3
3

$ ./matching-members 5 1 2 3 4
0

$ ./matching-members 1 2 3 4 5
5

Looking good.

Challenge #222.2: Last Member

You are given an array of positive integers, @ints.

Write a script to find the last member if found otherwise return 0. Each turn pick 2 biggest members (x, y) then decide based on the following conditions, continue this until you are left with 1 member or none.

  1. if x == y then remove both members
  2. if x != y then remove both members and add new member (y-x)
Example 1:
Input: @ints = (2, 7, 4, 1, 8, 1)
Output: 1

Step 1: pick 7 and 8, we remove both and add new member 1 => (2, 4, 1, 1, 1).
Step 2: pick 2 and 4, we remove both and add new member 2 => (2, 1, 1, 1).
Step 3: pick 2 and 1, we remove both and add new member 1 => (1, 1, 1).
Step 4: pick 1 and 1, we remove both => (1).
Example 2:
Input: @ints = (1)
Output: 1
Example 3:
Input: @ints = (1, 1)
Output: 0

Step 1: pick 1 and 1, we remove both and we left with none.
File: last-member
#! /usr/bin/env raku

unit sub MAIN (*@ints where @ints.elems > 0 && all(@ints) ~~ Int
                            && all(@ints) > 0,                     # [1]
               :v(:$verbose));

@ints = @ints>>.Int.sort.reverse;                                  # [2]

while @ints.elems > 1                                              # [3]
{
  say ":Sorted: @ints[]" if $verbose;
  
  my $y    = @ints.shift;                                          # [4]
  my $x    = @ints.shift;                                          # [4]
  my $diff = $y - $x;                                              # [5]

  say ":List:   @ints[] { $diff ?? "| add $diff (source: $y - $x)" \
      !! "| no add (source $y == $x)" }" if $verbose;

  if $diff                                                         # [6]
  {
    @ints.push: $diff;                                             # [6a]
    @ints = @ints.sort.reverse;                                    # [6b]
  }
}

say @ints.elems ?? @ints[0] !! 0;                                  # [7]

[1] At least 1 element, all of them must be positve integers.

[2] Coerce the values to integers (from the undecided IntStr that we get from using the command line), just to make the verbose mode output looks nice. We sort the values with the highest one first.

[3] As long as there are at least two elements left,

[4] Get the two highest values.

[5] Get the difference. Note the order we got the values in [4], ensuring a non-negative difference here.

[6] Do we have a difference? If so, add the difference to the list [6a], and resort the list [6b] - so that it always is sorted when we start a new iteration (in [3]).

[7] If we have elements in the list (just one, really), print that value. If not, print 0.

Running it:

$ ./last-member 2 7 4 1 8 1
1

$ ./last-member 1
1

$ ./last-member 1 1
0

Looking good.

With verbose mode:

$ ./last-member -v 2 7 4 1 8 1
:Sorted: 8 7 4 2 1 1
:List:   4 2 1 1 | add 1 (source: 8 - 7)
:Sorted: 4 2 1 1 1
:List:   1 1 1 | add 2 (source: 4 - 2)
:Sorted: 2 1 1 1
:List:   1 1 | add 1 (source: 2 - 1)
:Sorted: 1 1 1
:List:   1 | no add (source 1 == 1)
1

$ ./last-member -v 1
1

$ ./last-member -v 1 1
:Sorted: 1 1
:List:    | no add (source 1 == 1)
0

And that's it.