我希望能够重载+运算符,使其适用于Common Lisp向量——就像线性代数中的向量一样。是否可以使用+运算符进行重载?
这是我的预期定义:
(defmethod + ((v1 vector) (v2 vector))
感谢您对所有帮助的支持!
我希望能够重载+运算符,使其适用于Common Lisp向量——就像线性代数中的向量一样。是否可以使用+运算符进行重载?
这是我的预期定义:
(defmethod + ((v1 vector) (v2 vector))
如果我要做这个,我会首先在一个单独的包中进行。然后我会编写一个使用二进制运算符的通用函数:
(defun + (&rest addends)
(reduce #'binary+ (cdr addends) :initial-value (car addends)))
(defgeneric binary+ (addend1 addend2))
那么您可以在通用函数 binary+
上定义方法,使您能够将两个向量相加、一个向量和一个标量相加...
以下是一个适合生成包装器的宏:
(defmacro define-operator (op &key (binary-signifier :binary) (package *package*)
"Defines a generic operator OP, being essentially a reduce operation using
a generic function whose name is a concatenation of BINARY-SIGNIFIER and OP."
(let ((op op)
(binary (intern (concatenate 'string
(string binary-signifier)
(string op))
package)))
`(progn
(defun ,op (&rest args)
(reduce (function ,binary) (cdr args) :initial-value (car args)))
(defgeneric ,binary (arg1 arg2)))))
(defmethod binary+ ((x number) (y number))
(cl:+ x y))
(defmethod binary+ ((x vector) (y vector))
(map 'vector 'cl:+ x y))
(defmethod binary+ ((x list) (y list))
(map 'list 'cl:+ x y))
这是对Vatine的答案的扩展,但是加入了更多细节以使实现更加清晰:
(defpackage #:generic-arithmetic
(:use "COMMON-LISP")
(:shadow "+"))
(in-package #:generic-arithmetic)
(defun + (&rest addends)
(reduce 'binary+ (cdr addends) :initial-value (car addends)))
(defgeneric binary+ (addend1 addend2))
(defmethod binary+ ((x number) (y number))
(cl:+ x y))
(defmethod binary+ ((x vector) (y vector))
(map 'vector 'cl:+ x y))
(defmethod binary+ ((x list) (y list))
(map 'list 'cl:+ x y))
(+ 1 1)
;=> 2
(+ #(1 2) #(0 -1))
;=> #(1 1)
(+ '(1 3) '(3 1))
;=> (4 4)
如果首先屏蔽它,就可以重新定义+
:
? (shadow '+)
? (defgeneric + (a &rest b))
? (defmethod + ((a number) &rest b) (apply 'cl:+ a b))
? (+ 1 2)
3
? (+ 2 3 4)
9
? (defmethod + ((a string) &rest b) (apply #'cl:concatenate 'string a b))
? (+ "Hello" "World")
"HelloWorld"
? (+ "Hello" " cruel " "World")
"Hello cruel World"
? (defmethod + ((a vector) &rest b) (apply #'map 'vector 'cl:+ a b))
? (let ((v0 #(1 2 3)) (v1 #(4 5 6))) (+ v0 v1))
#(5 7 9)
apply
函数添加引号:(apply '+ 1 2 '(3 4))
- Rainer Joswig+
的效率变慢了(而我猜想 +
是非常常用的函数)。 - Mark Karpov定义通用函数+
可能不是一个好主意,因为这个符号是被锁定的。CLOS与其他语言(如C++)中的对象系统不同,因此术语“重载”可能不完全正确。
实际上,您不需要特殊的函数来求和向量,可以使用map
:
CL-USER> (let ((v0 #(1 2 3))
(v1 #(4 5 6)))
(map 'vector #'+ v0 v1))
#(5 7 9)
+
成为泛型在Common Lisp中是没有意义的。你可以很容易地使用map
来解决你的问题,就像我展示的那样,或者如果你真的需要一个函数来处理数字和向量,那么就创建一个新的函数。 - Mark Karpov