How to get the amount of arguments of a function

1k Views Asked by At

I'm looking for a way to get the amount of arguments of a function, a function like the imaginary length-args function below:

(defun func1 (arg1 arg2) 
  ())

(defun get-count-args-func (func) 
  (length-args func))

(get-count-args-func #'func1)
;; return 2

In practice what I really need is to solve a question from my test library that I am bringing from javascript to lisp, where I deal both with synchronous and asynchronous tests, where I need to do something like this(simplifying to the maximum the reality):

(defun test () 
  ;;...
  )

(defun done ()
  ;;...
  )

(defun run-test (test)
  (if (>= (get-count-args-func test) 1)
      (test (funcall done))
      (test)))
2

There are 2 best solutions below

1
PerduGames On

I found a way to resolve this issue by looking at this answer here: Find Function's Arity in common lisp, an arglist function is required to handle the various implementations, see the implementation of arglist:

;; function provided by @sds at https://stackoverflow.com/questions/15465138/find-functions-arity-in-common-lisp
(defun arglist (fn)
  "Return the signature of the function."
  #+allegro (excl:arglist fn)
  #+clisp (sys::arglist fn)
  #+(or cmu scl)
  (let ((f (coerce fn 'function)))
    (typecase f
      (STANDARD-GENERIC-FUNCTION (pcl:generic-function-lambda-list f))
      (EVAL:INTERPRETED-FUNCTION (eval:interpreted-function-arglist f))
      (FUNCTION (values (read-from-string (kernel:%function-arglist f))))))
  #+cormanlisp (ccl:function-lambda-list
                (typecase fn (symbol (fdefinition fn)) (t fn)))
  #+gcl (let ((fn (etypecase fn
                    (symbol fn)
                    (function (si:compiled-function-name fn)))))
          (get fn 'si:debug))
  #+lispworks (lw:function-lambda-list fn)
  #+lucid (lcl:arglist fn)
  #+sbcl (sb-introspect:function-lambda-list fn)
  #-(or allegro clisp cmu cormanlisp gcl lispworks lucid sbcl scl)
  (error 'not-implemented :proc (list 'arglist fn)))

Now I can do:

(defun func1 (arg1 arg2) 
  ())

(defun get-count-args-func (func) 
  (length (arglist func)))

(get-count-args-func #'func1) => 2

So I have the number of arguments, you just have to be attentive, if you have some &key argument, &rest or &optional, for example:

(defun func1 (&optional arg1 arg2) 
  ())

(defun get-count-args-func (func) 
  (length (arglist func)))

(get-count-args-func #'func1) => 3

See that you get 3, because what arglist function is returning:

(&OPTIONAL ARG1 ARG2)

while only with required arguments he returned:

(ARG1 ARG2)
3
Simeon Ikudabo On

The most simple way to do this would be to add rest parameters to your function and within the function use the built-in length function to determine the number of args the function takes:

CL-USER> (defun fun-1(&rest args)
   (length args))
FUN-1
CL-USER> (fun-1 1 2 3)
3