DEV Community

Lance Wicks
Lance Wicks

Posted on • Originally published at lancewicks.com on

#Perl Weekly Challenge #74

Hello once again; here is another post about the Perl Weekly Challenge. This time I have done both challenges in Perl 5.

I livecoded both:

So what did Task 1 look like:

package Majority;

use strict;
use warnings;

use List::MoreUtils 'frequency';

sub element {
# Majority element in the list is the one that appears more than floor(size_of_list/2).
    my ( $self, @A ) = @_;
    my $majority_element = -1;

    my $floor = @A / 2;

    my %frequencies = frequency @A;

    for my $element ( @A ) {
        if ( $frequencies{$element} > $floor ) {
            $majority_element = $element;
            last;
        }
    }

    return $majority_element;
}

1;
Enter fullscreen mode Exit fullscreen mode

So pretty simple module; I used List::MoreUtils to get the frequencies and from there it was pretty simple. If you watch the live coding you will see that I struggled a little with the documentation. As Mohammad Anwar was watching the result was he raised a PR on the module to change the POD to make it clearer for people like me! 🙂

For the second challenge, my module looks like this:

package FNR;

use strict;
use warnings;

use List::MoreUtils 'frequency';

sub from_string {
    my ( $self, $string ) = @_;
    my $sequence = '';

    my $end = length $string;

    for my $offset ( 1 .. $end ) {
        my $chars = substr $string, 0, $offset;
        $sequence .= $self->_fnr($chars);
    }

    return $sequence;
}

sub _fnr {
    my ( $self, $chars ) = @_;
    my @characters = split '', $chars;
    my %frequencies = frequency( @characters );

    for my $key ( reverse @characters ) {
        return $key if $frequencies{$key} == 1;
    }

    return '#';
}

1;
Enter fullscreen mode Exit fullscreen mode

In this one, I enjoyed breaking the task down into two methods.

The first method did the string manipulation; looping around the substrings. The second “private” method being called from the first which again used the frequency function from List::MoreUtils to determine the actual FNR.

I was happy with the TDD approaches I took with both. Here is the test file for the second task:

use Test2::V0 -target => 'FNR';

subtest 'from_string()' => sub {
    subtest 'Example 1' => sub {
        note "Input: ‘ababc’";
        note "Output: ‘abb#c’";
        is $CLASS->from_string('ababc'), 'abb#c',
            'The first example is correct';
    };

    subtest 'Example 2' => sub {
        note "Input: ‘xyzzyx’";
        note "Output: ‘xyzyx#’";

        is $CLASS->from_string('xyzzyx'), 'xyzyx#',
            'The second example is correct';

    };
};

subtest '_fnr()' => sub {
    my %tests = (
        'a' => 'a',
        'ab' => 'b',
        'aba' => 'b',
        'abab' => '#',
        'ababc' => 'c'
    );

    while ( my ( $input, $expected ) = each %tests ) {
        is $CLASS->_fnr($input), $expected, "$input should return $expected";
    }
};

done_testing;

Enter fullscreen mode Exit fullscreen mode

The second subtest I wrote in a style where I had my inputs and expected outputs in a hash, then loop around them and repeat the test. It’s a mixed blessing. I mainly prefer manual tests and to have input, expected and got all together. But in this case it seemed to make sense.

Like I said above; I was happy that I solved this task by

  1. Solving the looping around the string
  2. Solving the FNR part

Doing part one first helped me explore the problem without worrying about the solution. The examples in the challenge were helpful as it showed the loops. So as you see in the video; I was able to replicate that.

Solving the fnr part was interesting as I hit the “flakey tests” problem. I had tests that passed some of the time. Why? Because I was looping around the keys of the hash… and hashes have random order. This was interesting as there was one situation in the test where it would fail. Changing to looping around an array (which has a fixed order) solved the problem.

Having the tests running repeatedly via the linux watch command saved me from potential problems. If I had run them manually; or only tested the final script; then I might have missed the edge case. A lesson there for future Lance; testing repeatedly even after you have a passing test is a good thing.

Anyway… please do checkout the twitch or youtube videos. The solutions are in the PerlWeeklyChallenge git repo if you want to check them out in detail: https://github.com/lancew/perlweeklychallenge-club/tree/branch–1/challenge-074/lance-wicks/perl

Discussion (0)