This is my response to The Weekly Challenge #283.
@ints
, where every elements
appears more than once except one element.
Input: @ints = (3, 3, 1)
Output: 1
Example 2:
Input: @ints = (3, 2, 4, 2, 4)
Output: 3
Example 3:
Input: @ints = (1)
Output: 1
Example 4:
Input: @ints = (4, 3, 1, 1, 1, 4)
Output: 3
#! /usr/bin/env raku
unit sub MAIN (*@ints where all(@ints) ~~ Int && @ints.elems > 0,
:v(:$verbose)); # [1]
my $bag = @ints>>.Int.Bag; # [2]
say ": Bag { $bag.raku }" if $verbose;
say $bag.grep(*.value == 1)>>.key.sort.join(","); # [3]
[1] Ensure that we get integers only, and at least one of them.
[2] Turn the input into a Bag
, a hash like structure
where the unique values are the keys and their frequency are the
values.
See docs.raku.org/routine/Bag for more information about Bag
.
[3] We use grep
to get the entries with a frequency of 1, then
we extract the keys from the entries, then we sort the result so that
we get the same order each time we run this. And finally we pritty
print the result.
Note that this program supports multiple unique entries, and not just one as prescribed.
Running it:
$ ./unique-number 3 3 1
1
$ ./unique-number 3 2 4 2 4
3
$ ./unique-number 1
1
$ ./unique-number 4 3 1 1 1 4
3
Looking good.
With verbose mode:
$ ./unique-number -v 3 3 1
: Bag (1=>1,3=>2).Bag
1
$ ./unique-number -v 3 2 4 2 4
: Bag (4=>2,2=>2,3=>1).Bag
3
$ ./unique-number -v 1
: Bag (1=>1).Bag
1
$ ./unique-number -v 4 3 1 1 1 4
: Bag (3=>1,1=>3,4=>2).Bag
3
Multiple unique entries do work out:
$ ./unique-number 1 2 3 4 5 5
1,2,3,4
$ ./unique-number -v 1 2 3 4 5 5
: Bag (4=>1,2=>1,5=>2,3=>1,1=>1).Bag
1,2,3,4
@ints
.
true
if for every index i
in the
range 0 <= i < size of array
, the digit i
occurs exactly
the $ints[$i]
times in the given array otherwise return false
.
Input: @ints = (1, 2, 1, 0)
Ouput: true
$ints[0] = 1, the digit 0 occurs exactly 1 time.
$ints[1] = 2, the digit 1 occurs exactly 2 times.
$ints[2] = 1, the digit 2 occurs exactly 1 time.
$ints[3] = 0, the digit 3 occurs 0 time.
Example 2:
Input: @ints = (0, 3, 0)
Ouput: false
$ints[0] = 0, the digit 0 occurs 2 times rather than 0 time.
$ints[1] = 3, the digit 1 occurs 0 time rather than 3 times.
$ints[2] = 0, the digit 2 occurs exactly 0 time.
Note that 0 (zero) is not a positive integer. The challenge should have used «non-negative integers».
File: digit-count-value
#! /usr/bin/env raku
unit sub MAIN (*@ints where all(@ints) ~~ UInt && @ints.elems > 0, # [1]
:a(:$all), # [1a]
:v(:$verbose) = $all); # [1b]
my $bag = @ints>>.Int.Bag; # [2]
my $status = True; # [3]
say ": Bag: { $bag.raku }" if $verbose;
for ^@ints.elems -> $index # [4]
{
my $val = @ints[$index]; # [5]
my $count = $bag{$index}; # [6]
my $is-ok = $val == $count; # [7]
say ": \@ints[{ $index }] = { $val }, the digit { $index } occurs \
{ $count } time(s) | { $is-ok ?? "OK" !! "Not OK" }" if $verbose;
unless $is-ok # [8]
{
$status = False; # [8a]
last unless $all; # [8b]
}
}
say $status; # [9]
[1] Ensure that all the values are unsigned integers (the UInt
type), and that we got at least one of them. Note the default value for
the «verbose» argument, enabling it automatically if we use «all».
[2] Another Bag.
[3] Success so far.
[4] Iterate over the indices.
[5] Get the value,
[6] and the count (from the Bag
).
[7] Do the value and count match up?.
[8] If not, register it as a failure [8a]. Exit the loop unless we have specified «all» [8b].
[9] Print the Boolean value.
Running it:
$ ./digit-count-value 1 2 1 0
True
$ ./digit-count-value 0 3 0
False
Looking good.
With verbose mode:
$ ./digit-count-value -v 1 2 1 0
: Bag: (1=>2,0=>1,2=>1).Bag
: @ints[0] = 1, the digit 0 occurs 1 time(s) | OK
: @ints[1] = 2, the digit 1 occurs 2 time(s) | OK
: @ints[2] = 1, the digit 2 occurs 1 time(s) | OK
: @ints[3] = 0, the digit 3 occurs 0 time(s) | OK
True
$ ./digit-count-value -v 0 3 0
: Bag: (3=>1,0=>2).Bag
: @ints[0] = 0, the digit 0 occurs 2 time(s) | Not OK
False
«All» mode does not exit on the first failure, but goes on to the bitter end:
$ ./digit-count-value -a 1 2 1 0
: Bag: (2=>1,1=>2,0=>1).Bag
: @ints[0] = 1, the digit 0 occurs 1 time(s) | OK
: @ints[1] = 2, the digit 1 occurs 2 time(s) | OK
: @ints[2] = 1, the digit 2 occurs 1 time(s) | OK
: @ints[3] = 0, the digit 3 occurs 0 time(s) | OK
True
$ ./digit-count-value -a 0 3 0
: Bag: (3=>1,0=>2).Bag
: @ints[0] = 0, the digit 0 occurs 2 time(s) | Not OK
: @ints[1] = 3, the digit 1 occurs 0 time(s) | Not OK
: @ints[2] = 0, the digit 2 occurs 0 time(s) | OK
False
And that's it.