Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find function's arity in Common Lisp

I've been doing some Genetic Programming and I've been separating functions into different function sets based on their arity; it's all rather complex.

I'd like to know if there's a simpler way to to do it. For example, if there's a function that returns the arity of a given function.

Cheers in advance.

like image 876
Johnny McKenzie Avatar asked Mar 17 '13 19:03

Johnny McKenzie


2 Answers

For interpreted functions you should be able to use function-lambda-expression.

For compiled functions, alas, this function often returns nil, so you will have to use an implementation-dependent function (clocc/port/sys.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)))

EDIT: note that arity in CL is not really a number, since Lisp functions can accept optional, rest and keyword arguments in addition to required ones; this is why the above arglist function returns the lambda list of the argument function, not a number.

If you are only interested in functions which accept only required parameters, you would need to use something like

(defun arity (fn)
  (let ((arglist (arglist fn)))
    (if (intersection arglist lambda-list-keywords)
        (error "~S lambda list ~S contains keywords" fn arglist)
        (length arglist))))
like image 98
sds Avatar answered Sep 22 '22 07:09

sds


There is a portable library that gives the function's lambda list: https://github.com/Shinmera/trivial-arguments

(ql:quickload "trivial-arguments")

Example:

(arg:arglist #'gethash)
;; => (sb-impl::key hash-table &optional sb-impl::default)

(defun foo (a b c &optional d) nil)
(arglist #'foo)  ;; => (a b c &optional d)

It returns the full lambda list, with &optional and stuff, so we can't just get the length of the result for the arity.

like image 38
Ehvince Avatar answered Sep 21 '22 07:09

Ehvince