Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A ruby quiz, want to do it in CL?

2 views
Skip to first unread message

marc spitzer

unread,
Oct 23, 2007, 11:35:53 PM10/23/07
to
this is ruby quiz 144, anybody feel like doing it in CL?

Time Window (#144)

by Brian Candler

Write a Ruby class which can tell you whether the current time (or any
given time) is within a particular "time window". Time windows are
defined by strings in the following format:

ruby
# 0700-0900 # every day between these times
# Sat Sun # all day Sat and Sun, no other times
# Sat Sun 0700-0900 # 0700-0900 on Sat and Sun only
# Mon-Fri 0700-0900 # 0700-0900 on Monday to Friday only
# Mon-Fri 0700-0900; Sat Sun # ditto plus all day Sat and Sun
# Fri-Mon 0700-0900 # 0700-0900 on Fri Sat Sun Mon
# Sat 0700-0800; Sun 0800-0900 # 0700-0800 on Sat, plus 0800-0900 on Sun

Time ranges should exclude the upper bound, i.e. 0700-0900 is 07:00:00
to 08:59:59. An empty time window means "all times everyday". Here are
some test cases to make it clearer:

ruby
class TestTimeWindow < Test::Unit::TestCase
def test_window_1
w = TimeWindow.new("Sat-Sun; Mon Wed 0700-0900; Thu 0700-0900 1000-1200")

assert ! w.include?(Time.mktime(2007,9,25,8,0,0)) # Tue
assert w.include?(Time.mktime(2007,9,26,8,0,0)) # Wed
assert ! w.include?(Time.mktime(2007,9,26,11,0,0))
assert ! w.include?(Time.mktime(2007,9,27,6,59,59)) # Thu
assert w.include?(Time.mktime(2007,9,27,7,0,0))
assert w.include?(Time.mktime(2007,9,27,8,59,59))
assert ! w.include?(Time.mktime(2007,9,27,9,0,0))
assert w.include?(Time.mktime(2007,9,27,11,0,0))
assert w.include?(Time.mktime(2007,9,29,11,0,0)) # Sat
assert w.include?(Time.mktime(2007,9,29,0,0,0))
assert w.include?(Time.mktime(2007,9,29,23,59,59))
end

def test_window_2
w = TimeWindow.new("Fri-Mon")
assert ! w.include?(Time.mktime(2007,9,27)) # Thu
assert w.include?(Time.mktime(2007,9,28))
assert w.include?(Time.mktime(2007,9,29))
assert w.include?(Time.mktime(2007,9,30))
assert w.include?(Time.mktime(2007,10,1))
assert ! w.include?(Time.mktime(2007,10,2)) # Tue
end

def test_window_nil
w = RDS::TimeWindow.new("")
assert w.include?(Time.mktime(2007,9,25,1,2,3)) # all times
end
end

marc

--
ms4...@sdf.lonestar.org
SDF Public Access UNIX System - http://sdf.lonestar.org

ericwn...@gmail.com

unread,
Oct 25, 2007, 6:52:01 PM10/25/07
to
Ok, I did it. You can check it out here:

http://www.lispcast.com/index.php/2007/10/time-window-matcher/

I have the code, plus some comments on it.

Eric
http://www.lispcast.com

Ken Tilton

unread,
Oct 25, 2007, 7:30:33 PM10/25/07
to

ericwn...@gmail.com wrote:
> Ok, I did it. You can check it out here:
>
> http://www.lispcast.com/index.php/2007/10/time-window-matcher/
>
> I have the code, plus some comments on it.
>


You requested feedback. Code looks great. Spotted this:

(some #'identity (mapcar (lambda (tw)
(includesp tw time))
(rest tw))))

I am scratching my head, why not?:

(some (lambda (tw) (includesp tw time)) (rest tw))

Also saves you from consing up all the results. You did the same later
with (every 'identity (lambda ....

My next point would be you do not really need the result (which is a
predicate anyway, not the matched tw) so find-if does as well and does
not mislead the maintainer into thinking the result matters other than
being non-nil.

Finally:

(loop for tw in (rest tw)
thereis (includesp tw time))

Not so many parentheses. :)

hth,kt

--
http://www.theoryyalgebra.com/

"Career highlights? I had two. I got an intentional walk
from Sandy Koufax and I got out of a rundown against the Mets."."
- Bob Uecker

Ken Tilton

unread,
Oct 25, 2007, 7:40:04 PM10/25/07
to

Ken Tilton wrote:
>
>
> ericwn...@gmail.com wrote:
>
>> Ok, I did it. You can check it out here:
>>
>> http://www.lispcast.com/index.php/2007/10/time-window-matcher/
>>
>> I have the code, plus some comments on it.
>>
>
>
> You requested feedback. Code looks great. Spotted this:

(defun hour-of (time)
(multiple-value-bind (second minute hour) (decode-universal-time time)
hour))

That would give me warnings about unused second and minute variables. I
could declare ignore or:

(nth-value 2 (decode....))

carlos...@gmail.com

unread,
Oct 26, 2007, 10:11:54 PM10/26/07
to
On Oct 24, 5:35 am, marc spitzer <ms4...@sverige.freeshell.org> wrote:
> this is ruby quiz 144, anybody feel like doing it in CL?

Another solution (depends only on split-sequence):

(defvar *weekdays* '(mon tue wed thu fri sat sun))

(defclass time-window ()
((weekdays :accessor weekdays :initarg :weekdays)
(from :accessor from :initarg :from)
(to :accessor to :initarg :to)))

(defmethod print-object ((window time-window) stream)
(print-unreadable-object (window stream :type t)
(with-slots (weekdays from to) window
(format stream "~A from ~A to ~A" weekdays from to))))

(defmethod includes ((windows list) (time list))
(includes windows (apply #'encode-universal-time
(reverse (subseq (append time '(0 0 0)) 0 6)))))

(defmethod includes ((windows list) (time integer))
(loop for window in windows when (includes window time) return t))

(defmethod includes ((window time-window) (time integer))
(flet ((hhmmss-as-seconds (h &optional m s)
(+ (* 3600 h) (* 60 (or m 0)) (or s 0))))
(multiple-value-bind (sec min hour date month year day)
(decode-universal-time time)
(declare (ignore date month year))
(and (member (nth day *weekdays*) (weekdays window))
(<= (apply #'hhmmss-as-seconds (from window))
(hhmmss-as-seconds hour min sec))
(< (hhmmss-as-seconds hour min sec)
(apply #'hhmmss-as-seconds (to window)))))))

(defun create-windows (string)
(loop for def in (split-sequence:split-sequence #\; string)
append (let (days time-intervals)
(loop for item in (split-sequence:split-sequence #\Space def)
unless (string= item "")
do (ecase (length item)
(3 (push (read-from-string item) days))
(7 (let ((from-day (position (read-from-string (subseq item 0
3)) *weekdays*))
(to-day (position (read-from-string (subseq item 4 7))
*weekdays*)))
(when (< to-day from-day) (incf to-day 7))
(loop for i from from-day to to-day
do (pushnew (nth (mod i 7) *weekdays*) days))))
(9 (push item time-intervals))))
(loop for time-interval in (or time-intervals '("0000-2400"))
collect (make-instance 'time-window :weekdays (or days *weekdays*)
:from (list (parse-integer (subseq time-interval 0 2))
(parse-integer (subseq time-interval 2 4)))
:to (list (parse-integer (subseq time-interval 5 7))
(parse-integer (subseq time-interval 7 9))))))))

(progn
(let ((windows (create-windows "Sat-Sun; Mon Wed 0700-0900; Thu
0700-0900 1000-1200")))
(print windows)
(print (list (not (includes windows '(2007 9 25 8 0 0)))
(includes windows '(2007 9 26 8 0 0))
(not (includes windows '(2007 9 26 11 0 0)))
(not (includes windows '(2007 9 27 6 59 59)))
(includes windows '(2007 9 27 7 0 0))
(includes windows '(2007 9 27 8 59 59))
(not (includes windows '(2007 9 27 9 0 0)))
(includes windows '(2007 9 27 11 0 0))
(includes windows '(2007 9 29 11 0 0))
(includes windows '(2007 9 29 0 0 0))
(includes windows '(2007 9 29 23 59 59)))))
(let ((windows (create-windows "Fri-Mon")))
(print windows)
(print (list (not (includes windows '(2007 9 27)))
(includes windows '(2007 9 28))
(includes windows '(2007 9 29))
(includes windows '(2007 9 30))
(includes windows '(2007 10 1))
(not (includes windows '(2007 10 2))))) )
(let ((windows (create-windows "")))
(print windows)
(print (list (includes windows '(2007 9 25 1 2 3))))))

;; (#<TIME-WINDOW (SUN SAT) from (0) to (24)>
;; #<TIME-WINDOW (WED MON) from (7 0) to (9 0)>
;; #<TIME-WINDOW (THU) from (10 0) to (12 0)>
;; #<TIME-WINDOW (THU) from (7 0) to (9 0)>)
;; (T T T T T T T T T T T)
;; (#<TIME-WINDOW (MON SUN SAT FRI) from (0) to (24)>)
;; (T T T T T T)
;; (#<TIME-WINDOW (MON TUE WED THU FRI SAT SUN) from (0) to (24)>)
;; (T)

ericwn...@gmail.com

unread,
Oct 27, 2007, 3:44:03 AM10/27/07
to
Thanks so much for the informative feedback!

Eric
http://www.lispcast.com

Leandro Rios

unread,
Oct 28, 2007, 4:34:43 PM10/28/07
to
This is my version. Comments and suggestions are welcome.

Leandro

;(require 'split-sequence)

(defstruct timeframe
days hour-ranges)

(defparameter *weekdays* '#1=(mon tue wed thu fri sat sun . #1#))

;;; Timewindow parsing and creation

(defun get-days-range (di df)
"Returns a list containing the set of weekdays between di and df"
(loop for day in *weekdays*
with proceed
when (eq day di) do (setf proceed t)
when proceed collect day into days
when (and (eq day df) proceed) return days))

(defun day-lapse (string)
"Parses a string representing a range of weekdays 'Mon-Fri'
and returns a list with the set of weekdays into that range"
(get-days-range (read-from-string (subseq string 0 3))
(read-from-string (subseq string 4))))

(defun hour-lapse (string)
"Parses a string representing an hour range '0900-1200' and returns
a list with the limits of that range"
(list (* (parse-integer (subseq string 0 4)) 100)
(- (* (parse-integer (subseq string 5)) 100) 1)))

(defun parse-timeframe (string)
"Parses a string representing a timeframe and returns a timeframe
struct"
(if (= (length string) 0) ; all weekdays, all times
(make-timeframe)
(loop
for string-lapse in (split-sequence:split-sequence #\Space string)
for tipo = (search "-" string-lapse)
when (eq tipo 3) append (day-lapse string-lapse) into days ;weekdays range
when (eq tipo 4) collect (hour-lapse string-lapse) into hours ;hour range
when (null tipo) collect (read-from-string string-lapse) into days
;single weekday
finally (return (make-timeframe :days days :hour-ranges hours)))))

(defun create-timewindow (string)
"parses a string representing a timewindow and returns a list
of timeframe structs (a timewindow)"
(loop for string-window in (split-sequence:split-sequence #\; string)
collecting (parse-timeframe (string-trim " " string-window))))

;;; Predicates to test inclusion in timewindow

(defun in-timeframe-p (tf weekday time)
"Checks if weekday and time belong to timeframe"
(let ((hours (timeframe-hour-ranges tf))
(days (timeframe-days tf)))
(and (if days
(member weekday days)
t)
(if hours
(loop for (start end) in hours
when (<= start time end) return t)
t))))

(defun include-p (timewindow year month day hours minutes seconds)
"Checks if date and time belong to timewindow"
(assert (<= 0 seconds 59) (seconds))
(assert (<= 0 minutes 59) (minutes))
(assert (<= 0 hours 23) (hours))
(let ((time (+ (* hours 10000) (* minutes 100) seconds))
(day (nth (day-of-week day month year) *weekdays*)))
(loop for tf in timewindow
when (in-timeframe-p tf day time) return t)))

;;; Utility

;;; borrowed from cl-cookbook:
(defun day-of-week (day month year)
"Returns the day of the week as an integer.
Monday is 0."
(nth-value 6 (decode-universal-time
(encode-universal-time 0 0 0 day month year 0)
0)))

;; Usage:

(include-p (create-timewindow


"Sat-Sun; Mon Wed 0700-0900; Thu 0700-0900 1000-1200")

2007 10 25 8 0 0)


0 new messages