Page 1 of 1

Redefinition of function(operator) *

Posted: Wed Nov 04, 2009 6:33 am
by justanoob
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.

Re: Redefinition of function(operator) *

Posted: Tue Nov 24, 2009 2:33 am
by justanoob
Thinking of another approach, I wanted to know if i can redefine an operator for a particular class (like in C++).

Re: Redefinition of function(operator) *

Posted: Tue Nov 24, 2009 6:37 am
by gugamilare
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.

Re: Redefinition of function(operator) *

Posted: Tue Nov 24, 2009 2:01 pm
by nuntius
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"

Re: Redefinition of function(operator) *

Posted: Sat Dec 05, 2009 9:09 am
by justanoob
@gugamilare
Thanks for your suggestions and improvements.
@nuntius
Nice one.