gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 17:16:02 25 4
gpt4 key购买 nike

我目前正在通过 Graham 的书“ANSI Common Lisp”学习 lisp,并且作为练习,我正在编写基于 Julian-day 的日历计算。如您所知,复活节星期日每年都在变化,还有大约 10 个其他特殊日子的实际日期取决于复活节星期日的日期。

我想为这些天中的每一天定义一个函数,全部遵循以下模式:

(defun carnaval (year)
"Carnaval Monday of YEAR.

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

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

(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)))

(开始的 % 表明我打算不在定义宏的包中导出宏。)

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

(%defeasterday carnaval   "Carnaval Monday" -48)
(%defeasterday mardi-gras "Mardi gras" -47)
(%defeasterday ash "Ash Wednesday" -46)

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

(mapc #'(lambda (args) (apply #'%defeasterday args))
'((carneval "Carneval Monday" -48)
(mardi-gras "Mardi gras" -47)
(ash "Ash Wednesday" -46)))

这失败了

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 年的预期答案。)

最佳答案

应用宏不起作用

您不能应用宏:

(apply #'defsomething '(foo bar))

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

(eval (let ((args '(foo bar)))
`(defsomething ,@args)))

(let ((args '(foo bar)))
(eval `(defsomething ,@args)))

如果您想确保代码已编译,另请参阅函数 COMPILE

使用定义宏

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

(%defeasterdays
(carnaval "Carnaval Monday" -48)
(mardi-gras "Mardi gras" -47)
(ash "Ash Wednesday" -46))

%defeasterdays 宏应该在上面扩展为:

(progn
(%defeasterday carnaval "Carnaval Monday" -48)
(%defeasterday mardi-gras "Mardi gras" -47)
(%defeasterday ash "Ash Wednesday" -46))

DEFUN 是顶级宏。曾经通常想保持这种状态。如果您使用 DEFUN 形式的 EVAL,那么它不在文件编译器的顶层。因此,您需要在宏中进行转换,以便定义表单仍处于“顶层”。 PROGN 子表单仍然处于文件编译器的顶层。

让文件编译器开心

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

; 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*)

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

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

关于time - 使用宏定义计算 10 个复活节相关日期的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46483153/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com