module: timespace
// The sixty-unit class
define abstract class <sixty-unit> (<object>)
slot total-seconds :: <integer>, init-keyword: total-seconds:;
end class <sixty-unit>;
// decode-total-seconds
define method decode-total-seconds
(sixty-unit :: <sixty-unit>)
=> (max-unit :: <integer>, minutes :: <integer>, seconds :: <integer>)
decode-total-seconds(abs(time.total-seconds));
end method decode-total-seconds;
define method decode-total-seconds
(total-seconds :: <integer>)
=> (hours :: <integer>, minutes :: <integer>, seconds :: <integer>)
let(total-minutes, seconds) = truncate/(total-seconds, 60);
let(hours, minutes) = truncate/(total-minutes, 60);
values(hours, minutes, seconds);
end method decode-total-seconds;
// encode-total-seconds
define method encode-total-seconds
(max-unit :: <integer>, minutes :: <integer>, seconds :: <integer>)
=> (total-seconds :: <integer>)
((max-unit * 60) + minutes) * 60 + seconds;
end method encode-total-seconds;
// The say generic function
// Given an object, print a description of the object
define generic say (any-object :: <object>) => ();
// The time classes and methods
define abstract class <time> (<sixty-unit>)
end class <time>;
define method say (time :: <time>) => ()
let (hours, minutes) = decode-total-seconds(time);
format-out
("%d:%s%d", hours, if (minutes < 10) "0" else "" end, minutes);
end method say;
// A specific time of day from 00:00 (midnight) to before 24:00 (tomorrow)
define class <time-of-day> (<time>)
end class <time-of-day>;
// A relative time between -24:00 and +24:00
define class <time-offset> (<time>)
end class <time-offset>;
// Method for determining whether a time offset is in the past
define method past? (time :: <time-offset>) => (past? :: <boolean>)
time.total-seconds < 0;
end method past?;
define method say (time :: <time-offset>)
format-out("%s ", if (past?(time)) "minus" else "plus" end);
next-method();
end method say;
// Methods for adding times
define method \+
(offset1 :: <time-offset>, offset2 :: <time-offset>)
=> (sum :: <time-offset>)
let sum = offset1.total-seconds + offset2.total-seconds;
make(<time-offset>, total-seconds: sum);
end method \+;
define method \+
(offset :: <time-offset>, time-of-day :: <time-of-day>)
=> (sum :: <time-of-day>)
make(<time-of-day>,
total-seconds: offset.total-seconds + time-of-day.total-seconds);
end method \+;
define method \+
(time-of-day :: <time-of-day>, offset :: <time-offset>)
=> (sum :: <time-of-day>)
offset + time-of-day;
end method \+;
define method \+ (time1 :: <time>, time2 :: <time>)
error("Sorry, we can't add a %s to a %s.",
object-class(time1), object-class(time2));
end method \+;
// Methods for comparing times
define method \< (time1 :: <time-of-day>, time2 :: <time-of-day>)
time1.total-seconds < time2.total-seconds;
end method \<;
define method \< (time1 :: <time-offset>, time2 :: <time-offset>)
time1.total-seconds < time2.total-seconds;
end method \<;
define method \= (time1 :: <time-of-day>, time2 :: <time-of-day>)
time1.total-seconds = time2.total-seconds;
end method \=;
define method \= (time1 :: <time-offset>, time2 :: <time-offset>)
time1.total-seconds = time2.total-seconds;
end method \=;
// The angle classes and methods
define abstract class <angle> (<sixty-unit>)
end class <angle>;
define method say (angle :: <angle>) => ()
let(degrees, minutes, seconds) = decode-total-seconds(angle);
format-out
("%d degrees %d minutes %d seconds",
degrees, minutes, seconds);
end method say;
define class <relative-angle> (<angle>)
end class <relative-angle>;
// We need to show degrees for <relative-angle> but we do not need to
// show minutes and seconds, so we override the method on <angle>
define method say (angle :: <relative-angle>) => ()
format-out(" %d degrees", decode-total-seconds(angle));
end method say;
define abstract class <directed-angle> (<angle>)
slot direction :: <string>, init-keyword: direction:;
end class <directed-angle>;
define method say (angle :: <directed-angle>) => ()
next-method();
format-out(" %s", angle.direction);
end method say;
// The latitude and longitude classes and methods
define class <latitude> (<directed-angle>)
end class <latitude>;
define method say (latitude :: <latitude>) => ()
next-method();
format-out(" latitude\n");
end method say;
define class <longitude> (<directed-angle>)
end class <longitude>;
define method say (longitude :: <longitude>) => ()
next-method();
format-out(" longitude\n");
end method say;
// The position classes and methods
define abstract class <position> (<object>)
end class <position>;
define class <absolute-position> (<position>)
slot latitude :: <latitude>, init-keyword: latitude:;
slot longitude :: <longitude>, init-keyword: longitude:;
end class <absolute-position>;
define method say (position :: <absolute-position>) => ()
say(position.latitude);
say(position.longitude);
end method say;
define class <relative-position> (<position>)
// Distance is in miles
slot distance :: <single-float>, init-keyword: distance:;
slot angle :: <angle>, init-keyword: angle:;
end class <relative-position>;
define method say (position :: <relative-position>) => ()
format-out("%d miles away at heading ", position.distance);
say(position.angle);
end method say;
|