Perl Weekly Challenge: Week 323

Challenge 1:

Increment Decrement

You are given a list of operations.

Write a script to return the final value after performing the given operations in order. The initial value is always 0.

Possible Operations:
++x or x++: increment by 1
--x or x--: decrement by 1
Example 1
Input: @operations = ("--x", "x++", "x++")
Output: 1

Operation "--x" =>  0 - 1 => -1
Operation "x++" => -1 + 1 =>  0
Operation "x++" =>  0 + 1 =>  1
Example 2
Input: @operations = ("x++", "++x", "x++")
Output: 3
Example 3
Input: @operations = ("x++", "++x", "--x", "x--")
Output: 0

Operation "x++" => 0 + 1 => 1
Operation "++x" => 1 + 1 => 2
Operation "--x" => 2 - 1 => 1
Operation "x--" => 1 - 1 => 0

All we need to do for this one is to check if an operation contains ++ or --. The position doesn't matter nor, at least for the examples, does the name of the variable which is being incremented/decremented.

The total is initially set to 0.

my $total = 0;

For each operation, we check for increment or decrement with regular expressions and $total is adjusted accordingly.

for @operations -> $operation {
    if $operation ~~ /\+\+/ {
        $total++;
    }
    if $operation ~~ /\-\-/ {
        $total--;
    }
}

The final value of $total is printed.

say $total;

(Full code on Github.)

The Perl version works the same way.

my $total = 0;

for my $operation (@operations) {
    if ($operation =~ /\+\+/) {
        $total++;
    }
    if ($operation =~ /\-\-/) {
        $total--;
    }
}

say $total;

(Full code on Github.)

Challenge 2:

Tax Amount

You are given an income amount and tax brackets.

Write a script to calculate the total tax amount.

Example 1
Input: $income = 10, @tax = ([3, 50], [7, 10], [12,25])
Output: 2.65

1st tax bracket upto  3, tax is 50%.
2nd tax bracket upto  7, tax is 10%.
3rd tax bracket upto 12, tax is 25%.

Total Tax => (3 * 50/100) + (4 * 10/100) + (3 * 25/100)
          => 1.50 + 0.40 + 0.75
          => 2.65
Example 2
Input: $income = 2, @tax = ([1, 0], [4, 25], [5,50])
Output: 0.25

Total Tax => (1 * 0/100) + (1 * 25/100)
          => 0 + 0.25
          => 0.25
Example 3
Input: $income = 0, @tax = ([2, 50])
Output: 0

Sometimes, the hardest part of doing these problems is finding a good way to get the input into the script. I have chosen to use a series of command-line arguments where the first is the income and the rest are tax rate information as strings containing the bracket boundary and the taxation percentage seperated by white space. So for example 1: 10 "3 50" "7 10" "12 25"

First, we set up a variable to track how much of $income has been taxed.

my $taxed = 0;

Next we take the @tax information and convert it into a Hash where the keys are bracket boundaries, and the values are the percentages. This can be done very handily with a .map() which splits each rate string into two with .words() and converts it into a key-value Pair with [=>].

my %taxes = @tax.map({ [=>] $_.words });

We also need to keep track of the total amount of tax levied so far.

my $total = 0;

Now for each bracket (sorted in ascending numeric order...)

for %taxes.keys.sort({$^a <=> $^b }) -> $bracket {

...If the amount that has been taxed is greater or equal to $income we can stop processing.

    if $taxed >= $income {
        last;
    }

Otherwise the amount to tax is calculated. If the bracket is greater than $income, it is the $income minus the amount already taxed otherwise it is the bracket boundary minus the amount already taxed.

    my $tax = ($bracket > $income ?? $income !! $bracket) - $taxed;

In either case this amount is added to $taxed.

    $taxed += $tax;

And how tax has to be paid on this amount is calculated and added to $total.

    $total += $tax * (%taxes{$bracket} / 100);
}

Finally we print $total.

say $total;

(Full code on Github.)

In retrospect I should have made the variable names more descriptive but the algorithm is pretty good I think.

For Perl, the solution is much the same.

my $taxed = 0;

Expect perhaps this line which is slightly less concise and elegant as its' Raku equivalent.

my %taxes = map { my ($k, $v) = split /\s+/, $_; $k => $v } @tax;

my $total = 0;

for my $bracket ( sort { $a <=> $b } keys %taxes ) {
    if ($taxed >= $income) {
        last;
    }

    my $tax = ($bracket > $income ? $income : $bracket) - $taxed;
    $taxed += $tax;
    $total += $tax * ($taxes{$bracket} / 100);
}

say $total;

(Full code on Github.)