The Weekly Challenge - 269

The solutions below are not mine. This week, I saw outstanding solutions from James Curtis-Smith and Niels van Dijke. Therefore, I won't attempt to find better ones, if they exist (I did not found them yet).

TASK #1: Bitwise OR
You are given an array of positive integers, @ints.

Write a script to find out if it is possible to select two or more elements of the given array such that the bitwise OR of the selected elements has atlest one trailing zero in its binary representation.

#!/usr/bin/perl
use strict;
use warnings;

sub bitwise_or_james {
    1 < grep { ! ($_ & 1) } @_;
}

# TESTS

my (@ints);

# Example 1
@ints = (1, 2, 3, 4, 5);
print(bitwise_or_james(@ints)); # Output: 1

# Example 2
@ints = (2, 3, 8, 16);
print(bitwise_or_james(@ints)); # Output: 1

# Example 3
@ints = (1, 2, 5, 7, 9);
print(bitwise_or_james(@ints)); # Output: undef
Grep returns a list. But in scalar context the number of list elements. So addition of the keyword scalar can make the code more clear.
#!/usr/bin/perl
use strict;
use warnings;

sub bitwise_or_james {
    1 < scalar( grep { ! ($_ & 1) } @_ );
}
We can approach the problem differently: if we know that the bitwise OR of two or more even integers has at least one trailing zero, then can the problem be restated as 'are two or more elements even?'.
#!/usr/bin/perl
use strict;
use warnings;

sub bitwise_or_reinier {
    1 < scalar( grep { ($_ % 2 == 0) } @_ );
}
Grep can be slow in case of long lists. So Niels van Dijke's solution will be faster:
#!/usr/bin/perl
use strict;
use warnings;

sub bitwise_or_niels {
    my $c = 0;
    for (@_) {
        $c++ if ($_ & 1) == 0;
         return (1) if $c > 1;
    }
    return (0);
}

TASK #2: Distribute Elements
You are given an array of distinct integers, @ints.

Write a script to distribute the elements as described below:

1) Put the 1st element of the given array to a new array @arr1.
2) Put the 2nd element of the given array to a new array @arr2.

Once you have one element in each arrays, @arr1 and @arr2, then follow the rule below:

If the last element of the array @arr1 is greater than the last element of the array @arr2 then add the first element of the given array to @arr1 otherwise to the array @arr2.

When done distribution, return the concatenated arrays. @arr1 and @arr2.
#!/usr/bin/perl
use strict;
use warnings;

sub distribute_elements_niels {
    my @a = shift @_; 
    my @b = shift @_;
    for (@_) {
        $a[-1] < $b[-1] ? push(@b, $_) : push(@a, $_);
    }
    return (@a, @b);
}

# TESTS

my @ints;

# Example 1
@ints = (2, 1, 3, 4, 5);
print(distribute_elements_niels(@ints)); # Output: (2, 3, 4, 5, 1)

# Example 2
@ints = (3, 2, 4);
print(distribute_elements_niels(@ints)); # Output: (3, 4, 2)

# Example 3
@ints = (5, 4, 3, 8);
print(distribute_elements_niels(@ints)); # Output: (5, 3, 4, 8)