I'm currently learning lisp with Graham's book “ANSI Common Lisp” and as an exercise I am writing Julian-day based calendar calculations. As you know, the Easter Sunday changes from year to year and there is about 10 other special days whose actual date depends on the Easter Sunday date.
I want to define a function for each of these days, all following the following pattern:
(defun carnaval (year)
"Carnaval Monday of YEAR.
This is 48 days before Easter Sunday."
(- (easter year) 48))
Instead of repeating 10 times similar declarations, it seems appropriate to use a macro:
(defmacro %defeasterday (function-name screen-name offset)
`(defun ,function-name (year)
,(format nil "~A of YEAR.~%~%This is ~A day~:p ~A Easter Sunday"
screen-name
(abs offset)
(if (< 0 offset) "after" "before"))
(+ (easter year) ,offset)))
(The starting %
marks my intent to not export the macro in the package where it is defined.)
The macro can be used to define a function for each of the day whose date is based on Eastern Sunday date:
(%defeasterday carnaval "Carnaval Monday" -48)
(%defeasterday mardi-gras "Mardi gras" -47)
(%defeasterday ash "Ash Wednesday" -46)
…
Now, for the sake of the exercise, I would like to pack all the data on a list and use the %defeasterday
macro on its items. My attempt was
(mapc #'(lambda (args) (apply #'%defeasterday args))
'((carneval "Carneval Monday" -48)
(mardi-gras "Mardi gras" -47)
(ash "Ash Wednesday" -46)))
This fails with
Execution of a form compiled with errors.
Form:
#'%DEFEASTERDAY
Compile-time error:
The :macro name %DEFEASTERDAY was found as the argument to FUNCTION.
[Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
which teaches me that a macro is not “just a function mapping code to code” since apply
is picky about running them.
How can I use the %defeasterday
macro above to iterate on a list?
(If you need a ad-hoc easter
function for the purpose of testing, please (defun easter () 2457860)
which gives the expected answer for 2017.)