Discussion:
date iterator extension to iterate
Venkatesan S
2011-01-25 16:50:58 UTC
Permalink
Hi,

Olof-joachim, iterate-devel: Thanks for the help.

I have written a small driver for iterate which iterates over dates in a
given range. The basic usage is:
(iter (for (y m d) from-date '(2011 1 26) to '(2011 2 27) by 2 skipweekends
nil)
(format t "~a/~a/~a~%" d m y))

notes:
1) default value for "to" is today
2) default value for "by" is 1
3) skipweekends if true, skips over weekends


I would like to include this in date-calc package, as I feel it logically
belongs there. Please let me know how to go about this.
I am just learning lisp, and all comments/flames are welcome.

In the iterate driver, I would like to reuse the given variables (y m d)
instead of gensym'ming , but I get a slew of errors, which I am unable to
comprehend.
Instead of:

(defmacro-driver (FOR (y m d) FROM-DATE from-date-spec &optional TO
to-date-spec BY (n 1) SKIP-WEEKENDS (skipval t))
"Driver for iterating over dates. All dates are in YMD format, ie (YYYY
MM DD)"
(let ((ty (gensym "TO-YEAR"))
(tm (gensym "TO-MONTH"))
(td (gensym "TO-DATE"))
(fy (gensym "FROM-YEAR"))
(fm (gensym "FROM-MONTH"))
(fd (gensym "FROM-DATE"))
.....
(,kwd ,y next (progn
....
(list ,fy ,fm ,fd)
....

I would like to use the input yy instead of gensym'ming to hold the current
iteration value like:
(defmacro-driver (FOR (y m d) FROM-DATE from-date-spec &optional TO
to-date-spec BY (n 1) SKIP-WEEKENDS (skipval t))
"Driver for iterating over dates. All dates are in YMD format, ie (YYYY
MM DD)"
(let ((ty (gensym "TO-YEAR"))
(tm (gensym "TO-MONTH"))
(td (gensym "TO-DATE"))
.....
(,kwd ,y next (progn
....
(list ,y ,m ,d)

Also, any pointers to improve the code are very much appreciated.


Thanks,
Venkatesan
--
Audentis fortuna juvat
--
Audentis fortuna juvat
Olof-Joachim Frahm
2011-01-26 02:45:07 UTC
Permalink
Post by Venkatesan S
Olof-joachim, iterate-devel: Thanks for the help.
np :)
Post by Venkatesan S
I have written a small driver for iterate which iterates over dates in a
(iter (for (y m d) from-date '(2011 1 26) to '(2011 2 27) by 2
skipweekends nil)
    (format t "~a/~a/~a~%" d m y))
1) default value for "to"  is today 
2) default value for "by" is 1 
3) skipweekends if true, skips over weekends
In the iterate driver, I would like to reuse the given variables (y m d)
instead of gensym'ming , but I get a slew of errors, which I am unable
to comprehend.
Well, using something other than a plain symbol after the FOR keyword
isn't supported by DEFMACRO-DRIVER because that symbol is inserted in a
normal macro definition. E.g.:

(macroexpand
'(defmacro-driver (FOR (a b c d) FROM-DATE from-date-spec) ...))

expands to something like (minus some other things):

(DEFMACRO CLAUSE-FOR-FROM-DATE... (&KEY ((:FOR A) B C D) ...))
...)

which isn't exactly legal[1] even if SBCL doesn't warn here for macro
definitions.

Instead, the return value of the generator code can be destructured by
the user like in your example above. But that part is separated from
the generator and you can't hook into it unless you want to use some
internal functionality from iterate.

Is it worth the hassle? If so, I've attached a somewhat working
rewrite based on some experimentation. It's not finished, I've only
transcribed from your code and it works for me (tm).
Post by Venkatesan S
I would like to include this in date-calc package, as I feel it
logically belongs there. Please let me know how to go about this. 
I am just learning lisp, and all comments/flames are welcome.
Well, contact the author? And although it may for you logically belong
there, it adds a dependency to the date-calc package, which is, for a
small library perhaps not worth it. Since iterate has contribs (or one,
the postgres driver) I'd think it could be added there instead more
easily. But I'm only using this library, so same goes here.

Code remarks: I'd probably use &key instead of &optional, because
wrapping date-calc:today every time I want to change skip-weekends would
be too much work. Everything else is personal style, such as my habit
for long names. And for just learning it looks good, imho.

General remarks: use irc.freenode.net, #lisp and, if you like usenet,
comp.lang.lisp, because there're certainly more people reading in those
places. And you'll get more flames ;)

Cheers,
Olof

[1]: http://www.lispworks.com/documentation/HyperSpec/Body/03_dd.htm

--8<---------------cut here---------------start------------->8---
(iterate::defclause-driver (FOR year-month-date-vars FROM-DATE from-date-spec &optional TO to-date-spec BY (n 1) SKIP-WEEKENDS (skipval t))
"Driver for iterating over dates. All dates are in (YYYY MM DD) format."
(declare (ignorable to-date-spec))
(iterate::top-level-check)
(unless (and (listp year-month-date-vars)
(every #'symbolp year-month-date-vars))
(iterate::clause-error "~a should be a list of up to three variables: ~
the year, month and day" year-month-date-vars))
(mapc #'iterate::make-default-binding year-month-date-vars)
(let* ((step-var (iterate::make-var-and-default-binding 'by :type 'fixnum))
(fy (iterate::make-var-and-default-binding 'from-year :type 'fixnum))
(fm (iterate::make-var-and-default-binding 'from-month :type 'fixnum))
(fd (iterate::make-var-and-default-binding 'from-day :type 'fixnum))
(ty (iterate::make-var-and-default-binding 'to-year :type 'fixnum))
(tm (iterate::make-var-and-default-binding 'to-month :type 'fixnum))
(td (iterate::make-var-and-default-binding 'to-day :type 'fixnum))
(step `((multiple-value-setq (,fy ,fm ,fd)
(get-next-day ,fy ,fm ,fd ,step-var ,skipval))
(multiple-value-setq ,year-month-date-vars
(values ,fy ,fm ,fd))))
(test `(when (< (date-calc:delta-days ,fy ,fm ,fd ,ty ,tm ,td) 0)
(go ,iterate::*loop-end*))))
(setf iterate::*loop-end-used?* T)
(iterate::return-driver-code
:initial `((setq ,step-var ,n)
(destructuring-bind (y m d)
,from-date-spec
(multiple-value-setq (,fy ,fm ,fd)
(get-next-day y m d (- ,step-var) ,skipval)))
(let ((spec ,to-date-spec))
(multiple-value-setq (,ty ,tm ,td)
(if spec
(values-list spec)
(date-calc:today)))))
:next `(,.step ,test)
:variable year-month-date-vars)))
--8<---------------cut here---------------end--------------->8---
--
The world is burning. Run.

_______________________________________________
iterate-devel site list
iterate-***@common-lisp.net
Venkatesan S
2011-01-26 03:01:19 UTC
Permalink
Post by Olof-Joachim Frahm
Well, using something other than a plain symbol after the FOR keyword
isn't supported by DEFMACRO-DRIVER because that symbol is inserted in a
yeah, I saw it in macroexpand and the macro flew into a rage if I tried to
use any other variable than the first in the next form (like: (,kwd ,mm ...
). I ascribed it to my lack of knowledge and never thought it was illegal
:)
Post by Olof-Joachim Frahm
Well, contact the author? And although it may for you logically belong
there, it adds a dependency to the date-calc package, which is, for a
small library perhaps not worth it. Since iterate has contribs (or one,
the postgres driver) I'd think it could be added there instead more
easily. But I'm only using this library, so same goes here.
I posted to date-calc-devel mailing list too, but as you say contribs might
be a better place for this. How do I go about putting this into
iterate/contribs?


Thanks for your time, and the inputs. I very much appreciate it.

Code remarks: I'd probably use &key instead of &optional, because
Post by Olof-Joachim Frahm
wrapping date-calc:today every time I want to change skip-weekends would
be too much work. Everything else is personal style, such as my habit
for long names. And for just learning it looks good, imho.
General remarks: use irc.freenode.net, #lisp and, if you like usenet,
comp.lang.lisp, because there're certainly more people reading in those
places. And you'll get more flames ;)
Cheers,
Olof
[1]: http://www.lispworks.com/documentation/HyperSpec/Body/03_dd.htm
--8<---------------cut here---------------start------------->8---
(iterate::defclause-driver (FOR year-month-date-vars FROM-DATE
from-date-spec &optional TO to-date-spec BY (n 1) SKIP-WEEKENDS (skipval t))
"Driver for iterating over dates. All dates are in (YYYY MM DD) format."
(declare (ignorable to-date-spec))
(iterate::top-level-check)
(unless (and (listp year-month-date-vars)
(every #'symbolp year-month-date-vars))
(iterate::clause-error "~a should be a list of up to three variables: ~
the year, month and day" year-month-date-vars))
(mapc #'iterate::make-default-binding year-month-date-vars)
(let* ((step-var (iterate::make-var-and-default-binding 'by :type 'fixnum))
(fy (iterate::make-var-and-default-binding 'from-year :type 'fixnum))
(fm (iterate::make-var-and-default-binding 'from-month :type 'fixnum))
(fd (iterate::make-var-and-default-binding 'from-day :type 'fixnum))
(ty (iterate::make-var-and-default-binding 'to-year :type 'fixnum))
(tm (iterate::make-var-and-default-binding 'to-month :type 'fixnum))
(td (iterate::make-var-and-default-binding 'to-day :type 'fixnum))
(step `((multiple-value-setq (,fy ,fm ,fd)
(get-next-day ,fy ,fm ,fd ,step-var ,skipval))
(multiple-value-setq ,year-month-date-vars
(values ,fy ,fm ,fd))))
(test `(when (< (date-calc:delta-days ,fy ,fm ,fd ,ty ,tm ,td) 0)
(go ,iterate::*loop-end*))))
(setf iterate::*loop-end-used?* T)
(iterate::return-driver-code
:initial `((setq ,step-var ,n)
(destructuring-bind (y m d)
,from-date-spec
(multiple-value-setq (,fy ,fm ,fd)
(get-next-day y m d (- ,step-var) ,skipval)))
(let ((spec ,to-date-spec))
(multiple-value-setq (,ty ,tm ,td)
(if spec
(values-list spec)
(date-calc:today)))))
:next `(,.step ,test)
:variable year-month-date-vars)))
--8<---------------cut here---------------end--------------->8---
--
The world is burning. Run.
_______________________________________________
iterate-devel site list
http://common-lisp.net/mailman/listinfo/iterate-devel
--
Audentis fortuna juvat
Loading...