Avatar of 4d47
0
0
Genius
0
1

4d47's solution

to Clock in the Perl 6 Track

Instructions
Test suite
Solution

Implement a clock that handles times without dates.

You should be able to add and subtract minutes to it.

Two clocks that represent the same time should be equal to each other.

Resources

Remember to check out the Perl 6 documentation and resources pages for information, tips, and examples if you get stuck.

Running the tests

There is a test suite and module included with the exercise. The test suite (a file with the extension .t) will attempt to run routines from the module (a file with the extension .pm6). Add/modify routines in the module so that the tests will pass! You can view the test data by executing the command perl6 --doc *.t (* being the name of the test suite), and run the test suite for the exercise by executing the command prove . --exec=perl6 in the exercise directory. You can also add the -v flag e.g. prove . --exec=perl6 -v to display all tests, including any optional tests marked as 'TODO'.

Source

Pairing session with Erin Drummond https://twitter.com/ebdrummond

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

clock.t

#!/usr/bin/env perl6
use v6;
use Test;
use JSON::Fast;
use lib $?FILE.IO.dirname;
use Clock;
plan 53;

my Version:D $version = v3;

if Clock.^ver !~~ $version {
  warn "\nExercise version mismatch. Further tests may fail!"
    ~ "\nClock is {Clock.^ver.gist}. "
    ~ "Test is {$version.gist}.\n";
}

subtest 'Class methods', {
  ok Clock.can($_), $_ for <time add-minutes subtract-minutes>;
}

my $c-data = from-json $=pod.pop.contents;
for $c-data<cases>»<cases>»<>.flat -> %case {
  given %case<property> {
    when 'create' {
      is Clock.?new( |%(.<input><hour minute>:p) ).?time, |.<expected description> given %case;
    }
    when 'add'|'subtract' {
      given %case {
        my $clock = Clock.?new: |%(.<input><hour minute>:p);
        $clock.?add-minutes: .<input><value> if .<property> eq 'add';
        $clock.?subtract-minutes: .<input><value> if .<property> eq 'subtract';
        is $clock.?time, |.<expected description>;
      }
    }
    when 'equal' {
      is-deeply ([eq] gather {
        take Clock.?new( |%(.<hour minute>:p) ).?time for %case<input><clock1 clock2>;
      }), |%case<expected description>;
    }
  }
}

is Clock.?new(:0hour,:0minute).?add-minutes(65).?time, '01:05', 'add-minutes method can be chained';
is Clock.?new(:0hour,:0minute).?subtract-minutes(65).?time, '22:55', 'subtract-minutes method can be chained';

=head2 Canonical Data
=begin code
{
  "exercise": "clock",
  "version": "2.2.1",
  "comments": [
    "Most languages require constructing a clock with initial values,",
    "adding or subtracting some number of minutes, and testing equality",
    "in some language-native way.  Negative and out of range values are",
    "generally expected to wrap around rather than represent errors."
  ],
  "cases": [
    {
      "description": "Create a new clock with an initial time",
      "cases": [
        {
          "description": "on the hour",
          "property": "create",
          "input": {
            "hour": 8,
            "minute": 0
          },
          "expected": "08:00"
        },
        {
          "description": "past the hour",
          "property": "create",
          "input": {
            "hour": 11,
            "minute": 9
          },
          "expected": "11:09"
        },
        {
          "description": "midnight is zero hours",
          "property": "create",
          "input": {
            "hour": 24,
            "minute": 0
          },
          "expected": "00:00"
        },
        {
          "description": "hour rolls over",
          "property": "create",
          "input": {
            "hour": 25,
            "minute": 0
          },
          "expected": "01:00"
        },
        {
          "description": "hour rolls over continuously",
          "property": "create",
          "input": {
            "hour": 100,
            "minute": 0
          },
          "expected": "04:00"
        },
        {
          "description": "sixty minutes is next hour",
          "property": "create",
          "input": {
            "hour": 1,
            "minute": 60
          },
          "expected": "02:00"
        },
        {
          "description": "minutes roll over",
          "property": "create",
          "input": {
            "hour": 0,
            "minute": 160
          },
          "expected": "02:40"
        },
        {
          "description": "minutes roll over continuously",
          "property": "create",
          "input": {
            "hour": 0,
            "minute": 1723
          },
          "expected": "04:43"
        },
        {
          "description": "hour and minutes roll over",
          "property": "create",
          "input": {
            "hour": 25,
            "minute": 160
          },
          "expected": "03:40"
        },
        {
          "description": "hour and minutes roll over continuously",
          "property": "create",
          "input": {
            "hour": 201,
            "minute": 3001
          },
          "expected": "11:01"
        },
        {
          "description": "hour and minutes roll over to exactly midnight",
          "property": "create",
          "input": {
            "hour": 72,
            "minute": 8640
          },
          "expected": "00:00"
        },
        {
          "description": "negative hour",
          "property": "create",
          "input": {
            "hour": -1,
            "minute": 15
          },
          "expected": "23:15"
        },
        {
          "description": "negative hour rolls over",
          "property": "create",
          "input": {
            "hour": -25,
            "minute": 0
          },
          "expected": "23:00"
        },
        {
          "description": "negative hour rolls over continuously",
          "property": "create",
          "input": {
            "hour": -91,
            "minute": 0
          },
          "expected": "05:00"
        },
        {
          "description": "negative minutes",
          "property": "create",
          "input": {
            "hour": 1,
            "minute": -40
          },
          "expected": "00:20"
        },
        {
          "description": "negative minutes roll over",
          "property": "create",
          "input": {
            "hour": 1,
            "minute": -160
          },
          "expected": "22:20"
        },
        {
          "description": "negative minutes roll over continuously",
          "property": "create",
          "input": {
            "hour": 1,
            "minute": -4820
          },
          "expected": "16:40"
        },
        {
          "description": "negative hour and minutes both roll over",
          "property": "create",
          "input": {
            "hour": -25,
            "minute": -160
          },
          "expected": "20:20"
        },
        {
          "description": "negative hour and minutes both roll over continuously",
          "property": "create",
          "input": {
            "hour": -121,
            "minute": -5810
          },
          "expected": "22:10"
        }
      ]
    },
    {
      "description": "Add minutes",
      "cases": [
        {
          "description": "add minutes",
          "property": "add",
          "input": {
            "hour": 10,
            "minute": 0,
            "value": 3
          },
          "expected": "10:03"
        },
        {
          "description": "add no minutes",
          "property": "add",
          "input": {
            "hour": 6,
            "minute": 41,
            "value": 0
          },
          "expected": "06:41"
        },
        {
          "description": "add to next hour",
          "property": "add",
          "input": {
            "hour": 0,
            "minute": 45,
            "value": 40
          },
          "expected": "01:25"
        },
        {
          "description": "add more than one hour",
          "property": "add",
          "input": {
            "hour": 10,
            "minute": 0,
            "value": 61
          },
          "expected": "11:01"
        },
        {
          "description": "add more than two hours with carry",
          "property": "add",
          "input": {
            "hour": 0,
            "minute": 45,
            "value": 160
          },
          "expected": "03:25"
        },
        {
          "description": "add across midnight",
          "property": "add",
          "input": {
            "hour": 23,
            "minute": 59,
            "value": 2
          },
          "expected": "00:01"
        },
        {
          "description": "add more than one day (1500 min = 25 hrs)",
          "property": "add",
          "input": {
            "hour": 5,
            "minute": 32,
            "value": 1500
          },
          "expected": "06:32"
        },
        {
          "description": "add more than two days",
          "property": "add",
          "input": {
            "hour": 1,
            "minute": 1,
            "value": 3500
          },
          "expected": "11:21"
        }
      ]
    },
    {
      "description": "Subtract minutes",
      "cases": [
        {
          "description": "subtract minutes",
          "property": "subtract",
          "input": {
            "hour": 10,
            "minute": 3,
            "value": 3
          },
          "expected": "10:00"
        },
        {
          "description": "subtract to previous hour",
          "property": "subtract",
          "input": {
            "hour": 10,
            "minute": 3,
            "value": 30
          },
          "expected": "09:33"
        },
        {
          "description": "subtract more than an hour",
          "property": "subtract",
          "input": {
            "hour": 10,
            "minute": 3,
            "value": 70
          },
          "expected": "08:53"
        },
        {
          "description": "subtract across midnight",
          "property": "subtract",
          "input": {
            "hour": 0,
            "minute": 3,
            "value": 4
          },
          "expected": "23:59"
        },
        {
          "description": "subtract more than two hours",
          "property": "subtract",
          "input": {
            "hour": 0,
            "minute": 0,
            "value": 160
          },
          "expected": "21:20"
        },
        {
          "description": "subtract more than two hours with borrow",
          "property": "subtract",
          "input": {
            "hour": 6,
            "minute": 15,
            "value": 160
          },
          "expected": "03:35"
        },
        {
          "description": "subtract more than one day (1500 min = 25 hrs)",
          "property": "subtract",
          "input": {
            "hour": 5,
            "minute": 32,
            "value": 1500
          },
          "expected": "04:32"
        },
        {
          "description": "subtract more than two days",
          "property": "subtract",
          "input": {
            "hour": 2,
            "minute": 20,
            "value": 3000
          },
          "expected": "00:20"
        }
      ]
    },
    {
      "description": "Compare two clocks for equality",
      "cases": [
        {
          "description": "clocks with same time",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 15,
              "minute": 37
            },
            "clock2": {
              "hour": 15,
              "minute": 37
            }
          },
          "expected": true
        },
        {
          "description": "clocks a minute apart",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 15,
              "minute": 36
            },
            "clock2": {
              "hour": 15,
              "minute": 37
            }
          },
          "expected": false
        },
        {
          "description": "clocks an hour apart",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 14,
              "minute": 37
            },
            "clock2": {
              "hour": 15,
              "minute": 37
            }
          },
          "expected": false
        },
        {
          "description": "clocks with hour overflow",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 10,
              "minute": 37
            },
            "clock2": {
              "hour": 34,
              "minute": 37
            }
          },
          "expected": true
        },
        {
          "description": "clocks with hour overflow by several days",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 3,
              "minute": 11
            },
            "clock2": {
              "hour": 99,
              "minute": 11
            }
          },
          "expected": true
        },
        {
          "description": "clocks with negative hour",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 22,
              "minute": 40
            },
            "clock2": {
              "hour": -2,
              "minute": 40
            }
          },
          "expected": true
        },
        {
          "description": "clocks with negative hour that wraps",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 17,
              "minute": 3
            },
            "clock2": {
              "hour": -31,
              "minute": 3
            }
          },
          "expected": true
        },
        {
          "description": "clocks with negative hour that wraps multiple times",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 13,
              "minute": 49
            },
            "clock2": {
              "hour": -83,
              "minute": 49
            }
          },
          "expected": true
        },
        {
          "description": "clocks with minute overflow",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 0,
              "minute": 1
            },
            "clock2": {
              "hour": 0,
              "minute": 1441
            }
          },
          "expected": true
        },
        {
          "description": "clocks with minute overflow by several days",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 2,
              "minute": 2
            },
            "clock2": {
              "hour": 2,
              "minute": 4322
            }
          },
          "expected": true
        },
        {
          "description": "clocks with negative minute",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 2,
              "minute": 40
            },
            "clock2": {
              "hour": 3,
              "minute": -20
            }
          },
          "expected": true
        },
        {
          "description": "clocks with negative minute that wraps",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 4,
              "minute": 10
            },
            "clock2": {
              "hour": 5,
              "minute": -1490
            }
          },
          "expected": true
        },
        {
          "description": "clocks with negative minute that wraps multiple times",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 6,
              "minute": 15
            },
            "clock2": {
              "hour": 6,
              "minute": -4305
            }
          },
          "expected": true
        },
        {
          "description": "clocks with negative hours and minutes",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 7,
              "minute": 32
            },
            "clock2": {
              "hour": -12,
              "minute": -268
            }
          },
          "expected": true
        },
        {
          "description": "clocks with negative hours and minutes that wrap",
          "property": "equal",
          "input": {
            "clock1": {
              "hour": 18,
              "minute": 7
            },
            "clock2": {
              "hour": -54,
              "minute": -11513
            }
          },
          "expected": true
        }
      ]
    }
  ]
}
=end code
unit class Clock:ver<1>;

constant HOUR = 60;
constant DAY = HOUR * 24;

has Int $!mins = 0;

method new(:$hour = 0, :$minute = 0) {
    self.bless.add-minutes($hour * HOUR + $minute)
}
method add-minutes(Int $minute --> ::?CLASS) {
    $!mins = ($!mins + $minute mod DAY) mod DAY;
    $!mins = DAY + $!mins if $!mins < 0;
    self
}
method time {
    sprintf '%02d:%02d', self!hours, self!mins
}
method !hours { $!mins div HOUR }
method !mins  { $!mins mod HOUR }

What can you learn from this solution?

A huge amount can be learnt from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that I could read more about to develop my understanding?

Community comments

See what others have said about this solution
over 1 year ago
4d47 says

was under the wrong impression that having a single property instead of two would be better