Lisp 如何定义创建函数别名的函数?

Lisp 如何定义创建函数别名的函数?,lisp,common-lisp,sbcl,Lisp,Common Lisp,Sbcl,Lisp论坛线程有一个使用如下表单创建函数别名的示例 (setf (symbol-function 'zero?) #'zerop) 这很好,使zero?成为有效的谓词。是否可以在不使用宏的情况下参数化此表单?我希望能够调用以下函数并让它创建函数?: (define-predicate-alias 'functionp)` 我的看法大致如下: (defun defalias (old new) (setf (symbol-function (make-symbol new))

Lisp论坛线程有一个使用如下表单创建函数别名的示例

(setf (symbol-function 'zero?) #'zerop)
这很好,使
zero?
成为有效的谓词。是否可以在不使用宏的情况下参数化此表单?我希望能够调用以下函数并让它创建
函数?

(define-predicate-alias 'functionp)` 
我的看法大致如下:

(defun defalias (old new)
  (setf (symbol-function (make-symbol new))
    (symbol-function old)))

(defun define-predicate-alias (predicate-function-name)
  (let ((alias (format nil "~A?" (string-right-trim "-pP" predicate-function-name))))
    (defalias predicate-function-name alias)))

(define-predicate-alias 'zerop)

(zero? '())
尝试调用
zero?
时失败

函数
COMMON-LISP-USER::ZERO?
未定义


生成符号
创建不需要的符号。这就是为什么
zero?
是未定义的


将您的
(新建符号)
替换为例如
(intern new*package*)
。(或者您可能需要更仔细地考虑在哪个包中插入新符号。)

使符号创建一个不需要的符号。这就是为什么
zero?
是未定义的


将您的
(新建符号)
替换为例如
(intern new*package*)
。(或者您可能想更仔细地考虑在哪个包中插入新符号。)

您的代码通过
MAKE-symbol
生成符号,但您不会将其放入包中

使用函数
INTERN
将符号添加到包中

要扩展Lars的答案,请选择正确的软件包。在这种情况下,默认情况可能是使用别名函数中的相同包:

关于风格:

任何以
DEF
开头的内容实际上都应该是宏。如果有函数,请不要使用以“DEF”开头的名称。如果你看一下公共Lisp语言,所有这些都是宏。例如:对于那些定义表单的人,人们通常认为它们在编译文件时会产生副作用:编译器会得到关于它们的信息。函数不能

如果我把这样的东西放进文件里

(define-predicate-alias zerop)

(zero? '())
然后编译该文件,我希望不会看到任何关于未定义的
ZERO?
的警告。因此,宏需要将
(定义谓词别名'zerop)
扩展为某种东西,使新的
ZERO?
在编译时环境中为人所知

我也会将新名称作为第一个参数


因此,对函数使用类似于
MAKE-PREDICATE-ALIAS
的内容,而不是
DEFINE-PREDICATE-ALIAS

您的代码通过
MAKE-symbol
生成符号,但您不将其放入包中

使用函数
INTERN
将符号添加到包中

要扩展Lars的答案,请选择正确的软件包。在这种情况下,默认情况可能是使用别名函数中的相同包:

关于风格:

任何以
DEF
开头的内容实际上都应该是宏。如果有函数,请不要使用以“DEF”开头的名称。如果你看一下公共Lisp语言,所有这些都是宏。例如:对于那些定义表单的人,人们通常认为它们在编译文件时会产生副作用:编译器会得到关于它们的信息。函数不能

如果我把这样的东西放进文件里

(define-predicate-alias zerop)

(zero? '())
然后编译该文件,我希望不会看到任何关于未定义的
ZERO?
的警告。因此,宏需要将
(定义谓词别名'zerop)
扩展为某种东西,使新的
ZERO?
在编译时环境中为人所知

我也会将新名称作为第一个参数


因此,在函数中使用类似于
MAKE-PREDICATE-ALIAS
的内容,而不是
DEFINE-PREDICATE-ALIAS

已经有一些答案解释了如何做到这一点,但我要指出:

命名约定,
P
-P
Common Lisp有一种命名约定,这种约定大部分都是遵循的(即使在标准库中也有例外),即如果类型名是多个单词(包含
-
),那么它的谓词将使用
-p
后缀命名,而如果它没有,后缀就是
p
。所以我们有
keyboard p
lcd-monitor-p
。这样很好,您使用的是
(string right trim“-pP”predicate function name))
,但是由于标准中的
…P
…P
名称,以及由
defstruct
生成的名称将使用
P
,而不是
P
,因此您可以只使用
(string right trim)-P“谓词函数名))
。当然,即使这样,一些名字(例如,
pop
)也可能有问题,但我想这只是地域的问题

符号名称、
格式
*打印案例*
更重要的是,使用
format
为后续实习创建符号名是危险的,因为
format
并不总是用字符打印符号名,而字符实际出现在符号名中。例如:

(let ((*print-case* :downcase))
  (list (intern (symbol-name 'foo))
        (intern (format nil "~A" 'foo))))

;=> (FOO |foo|) ; first symbol has name "FOO", second has name "foo"
使用字符串连接和直接提取符号名可能会更好。这意味着您可以编写这样的代码(这是一个稍微不同的用例,因为其他问题已经解释了如何做您正在尝试做的事情):


已经有一些答案解释了如何做到这一点,但我要指出:

命名约定,
P
-P
Common Lisp有一种命名约定,这种约定大部分都是遵循的(即使在标准库中也有例外),即如果类型名是多个单词(包含
-
),那么它的谓词将使用
-p
后缀命名,而如果它没有,后缀就是
p
。所以我们有
keyboard p
lcd-monitor-p
。这样很好,您使用的是
(string right trim“-pP”predicate function name))
,但由于标准中的
..P
..-P
名称,以及由
defstruct
生成的名称将使用
P
,而不是

(macroexpand-1 '(defpredicate zero))
;=> (DEFUN ZEROP (X) (TYPEP X 'ZERO))

(macroexpand-1 '(defpredicate lcd-monitor))
;=> (DEFUN LCD-MONITOR-P (X) (TYPEP X 'LCD-MONITOR))