首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用宏定义计算10个复活节相关日期的函数

使用宏定义计算10个复活节相关日期的函数
EN

Stack Overflow用户
提问于 2017-09-29 06:43:26
回答 1查看 109关注 0票数 2

我目前正在学习lisp的格雷厄姆的书“ANSI公共Lisp”,作为一个练习,我写朱利安日日历计算。如你所知,复活节的星期日每年都在变化,另外大约有10个特殊的日子,其实际日期取决于复活节星期日的日期。

我想为这些日子中的每一天定义一个函数,所有这些都遵循以下模式:

代码语言:javascript
复制
(defun carnaval (year)
  "Carnaval Monday of YEAR.

This is 48 days before Easter Sunday."
  (- (easter year) 48))

与其重复10次类似的声明,不如使用宏:

代码语言:javascript
复制
(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)))

(启动的%标志着我不打算在定义宏的包中导出宏。)

宏可用于为日期基于东方星期日日期的每一天定义一个函数:

代码语言:javascript
复制
(%defeasterday carnaval   "Carnaval Monday" -48)
(%defeasterday mardi-gras "Mardi gras"      -47)
(%defeasterday ash        "Ash Wednesday"   -46)
…

现在,为了练习,我想将所有数据打包到一个列表中,并对其项使用%defeasterday宏。我的尝试是

代码语言:javascript
复制
(mapc #'(lambda (args) (apply #'%defeasterday args))
      '((carneval   "Carneval Monday" -48)
        (mardi-gras "Mardi gras"      -47)
        (ash        "Ash Wednesday"   -46)))

这不符合

代码语言:javascript
复制
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]

这告诉我,宏不仅仅是“将代码映射到代码的函数”,因为apply在运行它们时很挑剔。

如何使用上面的%defeasterday宏对列表进行迭代?

(如果你需要一个临时的easter函数来进行测试,请(defun easter () 2457860)给出2017年的预期答案。)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-09-29 13:54:45

应用宏的不工作

不能应用宏:

代码语言:javascript
复制
(apply #'defsomething '(foo bar))

但是您可以评估一个表单:

代码语言:javascript
复制
(eval (let ((args '(foo bar)))
         `(defsomething ,@args)))

代码语言:javascript
复制
(let ((args '(foo bar)))
  (eval `(defsomething ,@args)))

如果要确保代码已编译,请参见函数COMPILE

使用定义的宏

使用定义(!)的正确方法宏是这样的:

代码语言:javascript
复制
(%defeasterdays
  (carnaval   "Carnaval Monday" -48)
  (mardi-gras "Mardi gras"      -47)
  (ash        "Ash Wednesday"   -46))

然后,%defeasterdays宏应该扩展到以下内容:

代码语言:javascript
复制
(progn
 (%defeasterday carnaval   "Carnaval Monday" -48)
 (%defeasterday mardi-gras "Mardi gras"      -47)
 (%defeasterday ash        "Ash Wednesday"   -46))

DEFUN是一个顶级宏.曾经想要保持这种状态。如果您使用EVALDEFUN窗体,那么它并不在文件编译器的顶层。因此,您需要在宏中进行转换,这样定义的表单仍然处于“顶层”。对于文件编译器来说,PROGN子窗体仍然处于顶层.

使文件编译器高兴

可以使用文件编译器编译以下代码:

代码语言:javascript
复制
; we need the file compiler to know the value of *DAYS*
;  thus the eval-when. By default `DEFVAR` would not have
;  been executed
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defvar *days*
    '((carnaval   "Carnaval Monday" -48)
      (mardi-gras "Mardi gras"      -47)
      (ash        "Ash Wednesday"   -46))))

; a file compiler sees the following macro
:  and its definition is automatically available at compile time
(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)))

; same here, the compiler learns about the next macro
(defmacro %defeasterdays (list)
  `(progn ,@(loop for item in (symbol-value list)
                  collect `(%defeasterday ,@item))))

; now the file compiler sees the following form.
;  it will be macro expanded. The macros are known.
;  one of the macros now needs at expansion time the value
;  of *DAYS*. Since we have made *DAYS* known to the compiler,
;  this will work.
(%defeasterdays *days*)

主要优点是文件编译器将在编译时看到所有生成函数的定义。它可以为函数生成高效的代码,也可以为调用这些函数的表单生成高效的代码。

您也可以加载此文件,但它取决于实现是否将编译该代码,或者是否以解释函数结束。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46483153

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档