Perl weekly challenge 204

Monotonic Array

Task

You are given an array of integers. Write a script to find out if the given array is Monotonic. Print 1 if it is otherwise 0.

An array is Monotonic if it is either monotone increasing or decreasing.

Solution

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env raku

use v6.d;

sub order-pairs(Int @list where @list.elems > 1 --> List) {
    (@list[1..*] Z<=> @list[0..*-1]).list;
}

sub is-monotonic(Order @comparisons --> Bool) {
    my Junction $junc = @comparisons.any;
    (More == $junc and Less == $junc) ?? False !! True;
}

sub monotonic-array(Int @elements --> Int) {
    my Order @orders = order-pairs(@elements);
    return is-monotonic(@orders) ?? 1 !! 0;
}

#| Run test cases
multi sub MAIN('test') {
    use Test;
    plan 3;

    is monotonic-array(Array[Int].new([1, 2, 2, 3])), 1, 'works for (1, 2, 2, 3)';
    is monotonic-array(Array[Int].new([1, 3, 2])), 0, 'works for (1, 3, 2)';
    is monotonic-array(Array[Int].new([6, 5, 5, 4])), 1, 'works for (6, 5, 5, 4)';
}

#| Take user provided list like 1 2 2 3
multi sub MAIN(*@elements where all(@elements) ~~ /^<[+-]>?<[0..9]>+$/) {
    my Int @int_elements = @elements;
    monotonic-array(@int_elements) ?? say 1 !! say 0;
}

Discussion

I prefer a functional approach so we create a list of Rakus Order Types:

1
2
3
sub order-pairs(Int @list where @list.elems > 1 --> List) {
    (@list[1..*] Z<=> @list[0..*-1]).list;
}

For this we combine the zip operator as a metaoperator by combining it with the comparison operator. The first example creates the following list:

1
(More, Same, More)

Now we have to check that the list does not contain a More and a Less element. I created the is-monotonic function which does this using quite a few of Raku’s features:

1
2
3
4
sub is-monotonic(Order @comparisons --> Bool) {
    my Junction $comp-junc = @comparisons.any;
    (More == $comp-junc and Less == $comp-junc) ?? False !! True;
}

On the one hand we use the fact that the last statement is automatically used as a return value so we can leave out the return keyword. Next is the ternary operator. As we only return a boolean value depending on one condition, it keeps the code compact but still clear. The condition makes use of the any to create a junction. This way we don’t have to iterate the list manually checking each element.

1
2
3
sub order-pairs(Int @list where @list.elems > 1 --> List) {
    (@list[1..*] Z<=> @list[0..*-1]).list;
}
Related
Raku · Perl