Redefinition of function(operator) *

Discussion of Common Lisp
Post Reply
justanoob
Posts: 5
Joined: Wed Nov 04, 2009 6:17 am

Redefinition of function(operator) *

Post by justanoob » Wed Nov 04, 2009 6:33 am

I am new to Cl.
I have to define +,-,* and / operators for strings.
so, if the input is :
setf ret (+ 1 2 3 "asd"), I want the output in ret to be "1+2+3+asd". Similarly, for other operators.

I had no issues while redefining +, - and /. But redefining '*' is giving me headaches.

The sample * function:

Code: Select all

(defun abc:* (&rest arg)
 (if (isAllNum arg)
  (setf ret (multiple-value-call #'common-lisp:* (values-list arg)))
  ;else
  (if(= (list-length arg) 1)(setf ret (format nil "~a" arg)) (setf ret (format nil "~{~a~^ * ~}" arg))
  )
 )
 ret
)
The package abc uses common-lisp package and I have shadowed and exported opertaors(+, -, *, /).
Except '*' , the rest work fine according to my requirement. I get a name-conflict error for *.
Please suggest a better approach if available.

justanoob
Posts: 5
Joined: Wed Nov 04, 2009 6:17 am

Re: Redefinition of function(operator) *

Post by justanoob » Tue Nov 24, 2009 2:33 am

Thinking of another approach, I wanted to know if i can redefine an operator for a particular class (like in C++).

gugamilare
Posts: 406
Joined: Sat Mar 07, 2009 6:17 pm
Location: Brazil
Contact:

Re: Redefinition of function(operator) *

Post by gugamilare » Tue Nov 24, 2009 6:37 am

justanoob wrote:I am new to Cl.
I have to define +,-,* and / operators for strings.
so, if the input is :
setf ret (+ 1 2 3 "asd"), I want the output in ret to be "1+2+3+asd". Similarly, for other operators.

I had no issues while redefining +, - and /. But redefining '*' is giving me headaches.
But there should be no difference from defining operators +, -, / and * (except for the obvious).
justanoob wrote:IThe sample * function:

Code: Select all

(defun abc:* (&rest arg)
 (if (isAllNum arg)
  (setf ret (multiple-value-call #'common-lisp:* (values-list arg)))
  ;else
  (if(= (list-length arg) 1)(setf ret (format nil "~a" arg)) (setf ret (format nil "~{~a~^ * ~}" arg))
  )
 )
 ret
)
The package abc uses common-lisp package and I have shadowed and exported opertaors(+, -, *, /).
Except '*' , the rest work fine according to my requirement. I get a name-conflict error for *.
Please suggest a better approach if available.
I got no problems defining that function here (except for the variable "ret", which is not defined). Did you export the symbol * from the package abc? I can't thing of any other possible problems.

You took the right approach by creating a new package and shadowing the operators +, -, * and /. But there are a few things that you should change.

First of all, your function does not work correctly if you invoke it with only one string argument:

Code: Select all

abc> (* "1")
"(1)"
There is no need for the last if. The call (format nil "~{~a~^ * ~}" arg) will work if arg is a list of only one element.

Second, you are implicitly creating a global function named "ret". This is not good (in CL, it is not defined what exactly will happen in those cases, but that you might learn in another time). You should either create a local variable (using let) or not create any variables at all - the last approach is more lispy in this case, only create variables when they are needed.

Instead of calling (multiple-value-call #'common-lisp:* (values-list arg)), you may use (apply #'common-lisp:* arg) (which does the same thing you were doing).

And last, I don't know how you implemented the function isAllNum, but there is a simple way to implement it using every, like this:

Code: Select all

(every #'numberp arg)
This function will call the function #'numberp to every element of arg and test if they all return true.

With the modifications I suggested, here is your function:

Code: Select all

(defun * (&rest arg)
 (if (every #'numberp arg)
     (apply #'common-lisp:* arg)
     (format nil "~{~a~^ * ~}" arg)))
This is how I've evaluated everything:

Code: Select all

cl-user> (defpackage :abc (:use :cl) (:shadow cl:+ cl:- cl:/ cl:*))
#<package "ABC">
cl-user> (in-package :abc)
#<package "ABC">
abc> (defun * (&rest arg)
 (if (every #'numberp arg)
     (apply #'common-lisp:* arg)
     (format nil "~{~a~^ * ~}" arg)))
*
abc> (* "1")
"1"
abc> (* "1" 2 3 4)
"1 * 2 * 3 * 4"
abc> (* 1 2 3 4)
24
justanoob wrote:Thinking of another approach, I wanted to know if i can redefine an operator for a particular class (like in C++).
Unfortunately, no.

nuntius
Posts: 538
Joined: Sat Aug 09, 2008 10:44 am
Location: Newton, MA

Re: Redefinition of function(operator) *

Post by nuntius » Tue Nov 24, 2009 2:01 pm

As you've already discovered, these redefinitions belong in a new package.

The other part of your question hinges on the difference between "a+b+c" in C++ and "(+ a b c)" in CL. In C++, you have 2 calls to "operator+", whereas the CL code has a single call.

To achieve the C++ semantics in CL, try something like

Code: Select all

;; define a package
(defpackage :genop (:use :cl) (:shadow '+))
(in-package :genop)

;; define the protocol
(defgeneric operator+ (a b)
  (:documentation "operator overloading for binary +"))
(defun + (&rest args)
  "N-ary +, dispatches to operator+"
  (reduce #'operator+ args))

;; define the overloads
(defmethod operator+ ((a number) (b number))
  (cl:+ a b))
(defmethod operator+ ((a string) (b string))
  (concatenate 'string a b))
(defmethod operator+ ((a number) (b string))
  (format nil "~A~A" a b))
(defmethod operator+ ((a string) (b number))
  (format nil "~A~A" a b))

;; simple tests
(+ 1 2) ;; 3
(+ "a" "b") ;; "ab"
(+ 1 2 "b") ;; "3b"
(+ "a" 1 2) ;; "a12"

justanoob
Posts: 5
Joined: Wed Nov 04, 2009 6:17 am

Re: Redefinition of function(operator) *

Post by justanoob » Sat Dec 05, 2009 9:09 am

@gugamilare
Thanks for your suggestions and improvements.
@nuntius
Nice one.

Post Reply