gpt4 book ai didi

common-lisp - 在 Common Lisp 中以宏列表作为参数的宏

转载 作者:行者123 更新时间:2023-12-01 23:03:26 25 4
gpt4 key购买 nike

在 Common Lisp 中,如何定义一个“元宏”,它将宏列表(和其他参数)作为参数,并组合这些宏以生成所需的代码。

这个问题相当于编写一个“高阶宏”,它从其他宏的变量列表中定义一个宏。

提示问题的具体情况对我来说是一个 CLSQL 的实验,我想从 CLSQL-testsuite 中重新表达员工类

(clsql:def-view-class employee ()
((employee-id
:db-kind :key
:db-constraints (:not-null)
:type integer)
(first-name
:accessor employee-first-name
:type (string 30)
:initarg :first-name)
(last-name
:accessor employee-last-name
:type (string 30)
:initarg :last-name)
(email
:accessor employee-email
:type (string 100)
:initarg :email)
(company-id
:type integer
:initarg :company-id)
(company
:accessor employee-company
:db-kind :join
:db-info (:join-class company
:home-key companyid
:foreign-key companyid
:set nil))
(manager-id
:type integer
:nulls-ok t
:initarg :manager-id)
(manager
:accessor employee-manager
:db-kind :join
:db-info (:join-class employee
:home-key managerid
:foreign-key emplid
:set nil))))

作为

(def-view-class-with-traits employee ()
(trait-mapsto-company trait-mapsto-manager)
((employee-id
:db-kind :key
:db-constraints (:not-null)
:type integer)
(first-name
:accessor employee-first-name
:type (string 30)
:initarg :first-name)
(last-name
:accessor employee-last-name
:type (string 30)
:initarg :last-name)
(email
:accessor employee-email
:type (string 100)
:initarg :email)))

在定义复杂的数据库模式时,使用这种技术将有利于保持一致性和简洁性。

我将我需要的两个特征定义为

(defmacro trait-mapsto-company (class super slots &rest cl-options)
(declare (ignore super slots cl-options))
(let ((company-accessor-name
(intern (concatenate 'string (symbol-name class) "-COMPANY"))))
`((company-id
:type integer
:initarg :company-id)
(company
:accessor ,company-accessor-name
:db-kind :join
:db-info (:join-class company
:home-key companyid
:foreign-key companyid
:set nil)))))

(defmacro trait-mapsto-manager (class super slots &rest cl-options)
(declare (ignore super slots cl-options))
(let ((manager-accessor-name
(intern (concatenate 'string (symbol-name class) "-MANAGER"))))
`((manager-id
:type integer
:initarg :manager-id)
(manager
:accessor ,manager-accessor-name
:db-kind :join
:db-info (:join-class manager
:home-key managerid
:foreign-key emplid
:set nil)))))

但是,我编写 def-view-class-with-traits 的尝试失败了。

(defmacro def-view-class-with-traits (class super traits slots &rest cl-options)
(let ((actual-slots
(reduce (lambda (trait ax) (append (apply trait class super slots cl-options) ax))
traits
:initial-value slots)))
`(clsql:def-view-class ,class ,super ,actual-slots ,@cl-options)))

在用于归约的 lambda 中,trait 代表宏,而我对 apply 的使用对 Lisp 没有任何意义——这是正确的! – 但希望将我的意图传达给其他程序员。

如何让def-view-class-with-traits以合适的方式处理traits的宏列表?

最佳答案

如果您将特征定义为类本身并使用普通继承,我会发现这并不令人惊讶:

(def-view-class trait-mapsto-company ()
((company-id
:type integer
:initarg :company-id)
(company
:accessor company
:db-kind :join
:db-info (:join-class company
:home-key company-id
:foreign-key company-id
:set nil))))

(def-view-class trait-mapsto-manager ()
((manager-id
:type integer
:initarg :manager-id)
(manager
:accessor manager
:db-kind :join
:db-info (:join-class manager
:home-key managerid
:foreign-key emplid
:set nil)))

(def-view-class employee (trait-mapsto-company trait-mapsto-manager)
((employee-id
:db-kind :key
:db-constraints (:not-null)
:type integer)
(first-name
:accessor employee-first-name
:type (string 30)
:initarg :first-name)
(last-name
:accessor employee-last-name
:type (string 30)
:initarg :last-name)
(email
:accessor employee-email
:type (string 100)
:initarg :email)))

这当然不会使访问器名称依赖于继承类的名称,但你真的想要吗?我的观点是,这种写法表明这实际上会破坏解耦原则。

关于common-lisp - 在 Common Lisp 中以宏列表作为参数的宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50754347/

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