From: Bruno D. <bru...@gm...> - 2007-08-03 12:14:26
|
Here's the realization of the mentioned workaround. Over CMUCL's block-compilation, it has the advantage that it may cross file boundaries. See the code below. > Thanks for your answer. The second possibility is not always an option, e.g. > for recursive functions. The first possibility amounts to manually duplicating > the code into labels definitions for each calling function, but this could be > automated by macros: > > (defun-local f1 (a b) > ...) > > (defun f2 (c) > (with-local-functions (f1) > ... > (f1 c1 c2) > ... > (f1 c3 c4) > ... > )) > > (defun f3 (c) > (with-local-functions (f1) > ... > (f1 c5 c6) > ... > )) > > For non-recursive functions (and functions not mutually recursive with some > other inline function etc.) I consider it best to use both approaches and > generate f1 as an inline function and with-local-functions to generate just a > small delegating local functions calling f1: > > (defun-local-inline f1 (a b) > ...) > > (defun f2 (c) > (with-local-functions (f1) > ... > )) > > What do you think of these workarounds? Here's the implementation: (defun append-symbol (symbol suffix) "creates a new symbol out of a symbol name and a suffix" (intern (concatenate 'string (string symbol) suffix))) (defun function->local-macro-name (func) "name of the auxiliary macro for inserting local functions" (append-symbol func "-local-macro-aux")) (defun function->inline-function-name (func) "name of the auxiliary inline function created for local-inline functions" (append-symbol func "-inline-aux")) (defmacro defun-local (func (&rest args) &body body) "Prepare this function to be automatically inserted as a local function by with-local-functions. This will make its function calls faster since some arguments won't have to boxed. Contrary to block-compilation in CMUCL, this feature may also cross file boundaries. (Is an eval-when-statement needed?) If the function is non-recursive, use defun-local-inline instead, because it will avoid complete recompilation of the local function in each with-local-functions statement. The function may use optional and keyword parameters, but no &rest parameter." `(defmacro ,(function->local-macro-name func) () '(,func (,@args) ,@body))) (defun arg-list-for-call (args) "transforms a formal argument list of a function into an actual argument list of a call in a delegating function. &rest arguments are not supported, keyword and optional arguments are." (let ((keyword-package (symbol-package :dummy)) (key-args-reached nil) (args-of-call nil)) (flet ((symbol->keyword (symbol) (intern (string symbol) keyword-package))) (dolist (arg args) (cond ((eq '&key arg) (setq key-args-reached t)) ((eq '&optional arg)) (t (let ((arg-car (if (consp arg) (car arg) arg))) (if key-args-reached (progn (push (symbol->keyword arg-car) args-of-call) (push arg-car args-of-call)) (push arg-car args-of-call)))))) (reverse args-of-call)))) (defmacro defun-local-inline (func (&rest args) &body body) "Declare a copy of this function inline and prepare a simple delegating function to be automatically inserted as a local function by with-local-functions. This is not applicable to recursive functions, since they cannot be declared inline. The function may use optional and keyword parameters, but no &rest parameter." (let* ((inline-function-name (function->inline-function-name func)) (args-of-call (arg-list-for-call args))) `(progn (declaim (inline ,inline-function-name)) (defun ,inline-function-name (,@args) ,@body) (defmacro ,(function->local-macro-name func) () '(,func (,@args) (,inline-function-name ,@args-of-call)))))) (defmacro with-local-functions ((&rest funcs) &body body) "Insert prepared local functions in a labels statement." `(labels ,(loop :for func in funcs :collect (macroexpand-1 `(,(function->local-macro-name func)))) ,@body)) (defun-local-inline f1 (a b &key (c 0)) (+ a b c)) (defun f2 (c) (with-local-functions (f1) (* (f1 c (* 2 c)) (f1 (* 3 c) (* 4 c))))) (f2 3) (defun-local f1a (a b &key (c 0)) (+ a b c)) (defun f2a (c) (with-local-functions (f1a) (* (f1a c (* 2 c)) (f1a (* 3 c) (* 4 c))))) (f2a 3) The disadvantage is that you need the with-local-functions statement. This inconvenience might be lifted by a defun-variant that automatically recognizes calls of functions that been registered in some hash as local functions. But a similar variant of defmacro would then be needed as well, since macroexpansions might introduce additional calls of local functions. On the other hand it must be avoided that superfluous repetitions of the same local function will be introduced by macro expansions. Best regards Bruno Daniel |