Perl Weekly Challenge: Week 258

Challenge 1:

Count Even Digits Number

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

Write a script to find out how many integers have even number of digits.

Example 1
Input: @ints = (10, 1, 111, 24, 1000)
Output: 3

There are 3 integers having even digits i.e. 10, 24 and 1000.
Example 2
Input: @ints = (111, 1, 11111)
Output: 0
Example 3
Input: @ints = (2, 8, 1024, 256)
Output: 1

Raku once again comes through with a one-liner.

@*ARGS.grep({$_.chars %% 2}).elems.say

(Full code on Github.)

Using the command-line arguments as input, we filter them with .grep(), finding the length of each one with .chars() and seeing if it is evenly divisible by two with the %% operator. Then we count how many we found with .elems() and output the result with .say().

The Perl version is slightly longer because we don't have %% and have to do an explicit comparison to 0 instead.

say scalar grep {(length $_) % 2 == 0} @ARGV

(Full code on Github.)

Challenge 2:

Sum of Values

You are given an array of integers, @ints and an integer $k.

Write a script to find the sum of values whose index binary representation has exactly $k number of 1-bit set.

Example 1
Input: @ints = (2, 5, 9, 11, 3), $k = 1
Output: 17

Binary representation of index 0 = 0
Binary representation of index 1 = 1
Binary representation of index 2 = 10
Binary representation of index 3 = 11
Binary representation of index 4 = 100

So the indices 1, 2 and 4 have total one 1-bit sets.
Therefore the sum, $ints[1] + $ints[2] + $ints[3] = 17
Example 2
Input: @ints = (2, 5, 9, 11, 3), $k = 2
Output: 11
Example 3
Input: @ints = (2, 5, 9, 11, 3), $k = 0
Output: 2

Raku can do this one (albeit very long) line but I've spread it out to make it more intelligable.

I'm assuming $k is the first command-line argument and @ints is all the rest.

We start by getting the indices of each element in @ints with what has become one of my favorite methods of late, .keys().

@ints
.keys

Then each index is converted to binary via .map() and .base(2)

.map({ $_.base(2) })

Now we can find which indices meet the specs' criterion. We do this with a call to .grep() which...

.grep({

...splits the index into its' constituent digits with .comb(),,,

    $_
    .comb

...finds the digits which are 1's with another .grep()...

    .grep({ $_ == 1 })

... counts how many 1's there were with .elems() and compares that number to $k.

    .elems == $k
})

Now we know which indices we need, we can find the values of their respective elements by transforming them with .map(). We have to prefix 0b to each index in order for it to be recognized as a binary number literal.

.map({ @ints["0b$_"] })

Then we add up all the values with .sum()...

.sum

...and print the result.

.say;

(Full code on Github.)

Perl works the same way although the visual order of the operations is reversed. And we have to provide our own sum() function.

say sum(

Unfortunately, it seems that Perl does not allow you to use a binary literal as an array index so it has to first be converted back into base 10 with the badly named oct().

map { $ints[oct "0b$_"] }
grep { scalar (grep { $_ == 1 } (split //, $_)) == $k } 

We don't have .base() either but sprintf('%b') can do the conversion for us.

map { sprintf '%b', $_ } keys @ints
);

(Full code on Github.)