PERL WEEKLY CHALLENGE – 039

This is my ninth week participating into the weekly challenge.


Easy Challenge

A guest house had a policy that the light remain ON as long as the at least one guest is in the house. There is guest book which tracks all guest in/out time. Write a script to find out how long in minutes the light were ON.

1) Alex    IN: 09:10 OUT: 09:45
2) Arnold  IN: 09:15 OUT: 09:33
3) Bob     IN: 09:22 OUT: 09:55
4) Charlie IN: 09:25 OUT: 10:05
5) Steve   IN: 09:33 OUT: 10:01
6) Roger   IN: 09:44 OUT: 10:12
7) David   IN: 09:57 OUT: 10:23
8) Neil    IN: 10:01 OUT: 10:19
9) Chris   IN: 10:10 OUT: 11:00

I solved this by converting the hh::mm timestamp into absolute minutes and iterating through each minute that the light is on and storing that into a hash. The hash is sampled per minute and the last minute isn’t sampled.

For example: 10:10 – 10:11 will only store the absolute minute 610 into the hash and not 610 and 611.

In Raku, I pretty much did the same thing.

Perl 5 solution

#!/usr/bin/perl
# Test: ./ch1.pl
use strict;
use warnings;
use feature qw /say/;

my $minutes_on = calculate_lights_on();

say 'Lights on for: ' .
    $minutes_on . ' minutes';

# Calculate the minutes lights were on
sub calculate_lights_on {
	my %time_on; # Sample in minutes
	my $time_re = qr/\d{2}\:\d{2}/;

	while (my $line = <DATA>) {
		next unless $line =~
			/.*?($time_re).*?($time_re)/;

		# Get the time in absolute minutes
		my $t1 = absolute_minutes($1);
		my $t2 = absolute_minutes($2);

		# Populate the time on hash
		for my $minute ($t1 .. ($t2 - 1)) {
			$time_on{$minute} = 1;
		}
	}

	return scalar(keys %time_on);
}

# Convert to absolute mins.
sub absolute_minutes {
	my ($hh, $mm) = split(':', shift);
	return $hh * 60 + $mm;
}


__DATA__
1) Alex    IN: 09:10 OUT: 09:45
2) Arnold  IN: 09:15 OUT: 09:33
3) Bob     IN: 09:22 OUT: 09:55
4) Charlie IN: 09:25 OUT: 10:05
5) Steve   IN: 09:33 OUT: 10:01
6) Roger   IN: 09:44 OUT: 10:12
7) David   IN: 09:57 OUT: 10:23
8) Neil    IN: 10:01 OUT: 10:19
9) Chris   IN: 10:10 OUT: 11:00

Output

Lights on for: 110 minutes

Raku solution

# Test: perl6 ch1.p6
use v6.d;

sub MAIN() {
	my $minutes_on = calculate-lights-on();

	say 'Lights on for: ' ~
	    $minutes_on ~
	    ' minutes';
}

# Calculate the minutes lights were on
sub calculate-lights-on {
	my %time_on; # Sample in minutes
	my $time_re = /\d\d\:\d\d/;

	for data().lines -> $line {
		next unless $line ~~
			/.*?($time_re).*?($time_re)/;

		# Get the time in absolute minutes
		my $t1 = absolute-minutes($0);
		my $t2 = absolute-minutes($1);

		%time_on{$t1 .. ($t2 -1)} = 1;
	}

	return %time_on.elems;
}

# Convert to absolute mins.
sub absolute-minutes($hh_mm) {
	my ($hh, $mm) = $hh_mm.split(':');
	return $hh * 60 + $mm;
}

# The data
sub data {
	return q:to/END/;
1) Alex    IN: 09:10 OUT: 09:45
2) Arnold  IN: 09:15 OUT: 09:33
3) Bob     IN: 09:22 OUT: 09:55
4) Charlie IN: 09:25 OUT: 10:05
5) Steve   IN: 09:33 OUT: 10:01
6) Roger   IN: 09:44 OUT: 10:12
7) David   IN: 09:57 OUT: 10:23
8) Neil    IN: 10:01 OUT: 10:19
9) Chris   IN: 10:10 OUT: 11:00
END
}

Output

Lights on for: 110 minutes

Hard Challenge

Write a script to demonstrate Reverse Polish notation(RPN). Checkout the wiki page for more information about RPN.


For this challenge I just followed the algorithm shown below. I used a dispatch table for the operations and made it a bit more utf-8 friendly.

for each token in the postfix expression:
  if token is an operator:
    operand_2 ← pop from the stack
    operand_1 ← pop from the stack
    result ← evaluate token with operand_1 and operand_2
    push result back onto the stack
  else if token is an operand:
    push token onto the stack
result ← pop from the stack


Perl 5 solution

#!/usr/bin/perl
# test: ./ch2.pl "15 7 1 1 + − ÷ 3 × 2 1 1 + + −"
use strict;
use warnings;
use feature qw /say/;

say evaluate_stack($ARGV[0]);

# Evaluate the stack
sub evaluate_stack {
	my @tokens = split(/\s/, shift);
	my @stack;

	# Some utf-8 friendly operations
	my $operations = {
		'+' => \&add,
		'-' => \&subtract,
		'−' => \&subtract,
		'*' => \&multiply,
		'×' => \&multiply,
		'÷' => \&divide,
		'/' => \&divide,
	};

	for my $token (@tokens) {
		if ($operations->{$token}) {
			push @stack, $operations->{$token}->(\@stack);
		} elsif ($token =~ /\d+/) {
			push @stack, $token;
		}
	}

	return pop(@stack);
}

# Operations
sub add      { my $s = shift; return pop(@$s) + pop(@$s) }
sub subtract { my $s = shift; return - pop(@$s) + pop(@$s) }
sub multiply { my $s = shift; return pop(@$s) * pop(@$s) }
sub divide   { my $s = shift; return (1 / pop(@$s)) * pop(@$s) }

Test like this
./ch2.pl “15 7 1 1 + − ÷ 3 × 2 1 1 + + −”

Output
5

Raku solution

# Test: perl6 ./ch2.p6 "15 7 1 1 + − ÷ 3 × 2 1 1 + + −"
use v6.d;

sub MAIN (Str $tokens) {
	say evaluate-stack($tokens.split(/\s/));
}

# Evaluate the stack
sub evaluate-stack(@tokens) {
	my @stack;

	# Some utf-8 friendly operations
	my %operations = (
		'+' => &add,
		'-' => &subtract,
		'−' => &subtract,
		'*' => &multiply,
		'×' => &multiply,
		'÷' => &divide,
		'/' => &divide,
	);

	# Process each token
	for (@tokens) -> $token {
		if (%operations.{$token}) {
			push @stack, %operations.{$token}(@stack);
		} elsif ($token ~~ /\d+/) {
			push @stack, $token;
		}
	}

	# Return the answer
	return pop(@stack);
}

# Operations
sub add(@s)      { return @s.pop + @s.pop }
sub subtract(@s) { return - @s.pop + @s.pop }
sub multiply(@s) { return @s.pop * @s.pop }
sub divide(@s)   { return (1 / @s.pop) * @s.pop }

Test like this
perl6 ./ch2.p6 “15 7 1 1 + − ÷ 3 × 2 1 1 + + −”

Output
5

One thought on “PERL WEEKLY CHALLENGE – 039

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s