digikar
digikar

Reputation: 588

Common Lisp: Efficiently handling supplied-p-parameter efficiently without using &rest arguments

Suppose I have the function

(defun bar (a &optional (b nil bp))
  (declare (ignore a b bp)) ; <-- replace it with (list a b bp)
                            ;     to check for correctness
  )
;; I want to preserve the value of bp here inside bar, and inside foo below

and I wish to write a wrapper around bar:

(defun foo (a &optional (b nil bp))
  (declare (optimize speed))
  (apply #'bar a (nconc (when bp (list b)))))

while preserving the value of bp in the call from foo to bar, and also keeping the arguments b visible in the emacs' minibuffer / eldoc-mode. I wanted to know if there's a possibly-nonportable non-consing way to preserve the value of bp in this call.

For example,

CL-USER> (time (loop repeat 1000000 do (foo 4 2)))
Evaluation took:
  0.040 seconds of real time
  0.043656 seconds of total run time (0.043656 user, 0.000000 system)
  110.00% CPU
  96,380,086 processor cycles
  15,990,784 bytes consed <--- plenty of consing
  
NIL

If I ignore the b-visibility-in-eldoc, I could possibly use &rest arguments, but I do want the arguments to be visible.

While there are other ways to achieve this in this particular case, I do want to consider the case when there are multiple &optional (or &keyword) arguments.

Upvotes: 0

Views: 91

Answers (2)

ad absurdum
ad absurdum

Reputation: 21318

Use a cond form to decide how to call bar:

(defun bar (a &optional (b nil bp) (c nil cp))
  (declare (ignore a b bp c cp)))

(defun foo (a &optional (b nil bp) (c nil cp))
  (declare (optimize speed))
  (cond (cp (funcall #'bar a b c))
        (bp (funcall #'bar a b))
        (t (funcall #'bar a))))
CL-USER> (time (loop repeat 1000000 do (foo 1 2 3)))
Evaluation took:
  0.015 seconds of real time
  0.017203 seconds of total run time (0.017148 user, 0.000055 system)
  113.33% CPU
  41,186,554 processor cycles
  0 bytes consed

Checking the arguments passed to bar:

(defun bar (a &optional (b nil bp) (c nil cp))
  (list a b bp c cp))
CL-USER> (foo 1 2 3)
(1 2 T 3 T)
CL-USER> (foo 1 2)
(1 2 T NIL NIL)
CL-USER> (foo 1)
(1 NIL NIL NIL NIL)

Upvotes: 3

Martin Buchmann
Martin Buchmann

Reputation: 1231

I am not sure what bar and foo are actually supposed to do but what about:

(defun foo (a &optional (b nil bp))
  (declare (optimize speed))
  (funcall #'bar a (when bp b))) 

CL-USER> (time (loop repeat 1000000 do (foo 4 2)))
Evaluation took:
  0.005 seconds of real time
  0.005810 seconds of total run time (0.005804 user, 0.000006 system)
  120.00% CPU
  8,099,624 processor cycles
  0 bytes consed

Upvotes: -1

Related Questions