#Function Hierarchy?
9 messages in this thread
Hi Tony, could you answer a question for me please?
I often find it benificial to look at someone else's lisp routine to get an
answer to my questions or problems, but this has me puzzled.
Looking over some of the sample routines included with ACAD I find
"secondary" functions (for lack of a better term) built inside the main
fuction. Some Error fuctions seem to be common examples. They are nested
inside the main fuction. Other times it may be a "sub-function" or a
branch routine.
Is there a reason for this that I am missing? Is there pros or cons for
doing this or not doing it?
I haven't found anything about it in any of the books I have acquired (or I
haven't found it at least).
Would appreciate your comments or advice. Thanks!
Phil
Phil – The "secondary" functions, as you refer to them, are more commonly
thought of as "subroutines". There are many benefits to them. Mainly, they
allow you to write applications in a modular fashion, by breaking down every
task or problem into smaller ones, and dealing with each component problem
or task seperately, which thereby allows us to reduce the overall complexity
of an application. So, in the case of a large application, it often helps
to decompose it into many small logical 'modules', each of which is designed
to do a very specific and limited function. You can think of the hierarchy
of functions as a tree, where the root is the main program, and each branch
is a subroutine, which itself can branch to other "sub-subroutines".
In this analogy, as you get deeper into the tree (that is, further from the
'root' or main program), function or operation performed by each subroutine
will usually become more specific and limited in scope.
Additionally, if a procedure needs to be performed many times, and/or at
several different points within the same program, it makes little sense to
replicate the same code at each and every one of those points when instead,
the code could appear just once as a subroutine, and be "called" from many
different places.
<more>…
Tony – I have a feeling that Phil is asking more about the functions
DEFUN'ed inside other functions, and listed as local symbols in the outer
function's argument / symbol list.
Phil, that's just an isolation technique, used just as other local symbols
are used, to limit the scope of those functions. When the outer function
exits, its local symbols are removed from the ATOMLIST.
Duff – Looking at Phil's question again, I think you're right. Except that
in AutoLISP, using this technique to "isoloate" or bind subroutines locally,
isn't very effective, since any error condition causes the "local" function
definitions to become global.
I don't advocate it, because it causes duplication of data (e.g., a local
function exists as part of the body of its 'parent', as well as the body
of the symbol it gets assigned to when the parent executes).
Personally, I prefer a rational symbol-naming convention that lets static
data exist in one place, as a global.
-TonyT.
Hi Tony … I'm sorry, I guess my question needs to be rephrased. I do
understand the advantage of subroutines … my question involves their
placement. I have seen some routines where the subroutine(s) are placed
before the main program … Example: (defun myerr (msg)
…… ) ;end of myerr (defun subroutine ()
…… ) ;end of subroutine (defun c:myprogram ()
……………………… body of main program ) ;end of
c:myprogram
I have found others that have the subroutines placed "under the root" if
you will … Example: (defun c:myprogram ()
(defun myerr (msg)
…..
) ;end of myerr
(defun subroutine ()
…..
) ;end of subroutine
……………………. body of c:myprogram function ) ;end of
c:myprogram
Is there a reason why some subroutines would be placed "at" the root of the
main program while others are placed "under" the root? I hope I made myself
a little clearer … thanks again for your help, Tony.
……Phil
Phil – In AutoLISP, there's no benefit to nesting function definitions. The
only reason that it is done in other LISP's is because it causes the execution
of the nested functions to be dynamically-scoped (e.g., a local declared in a
'parent' function can be referenced from the environment of a nested 'child'
function as well). But, since all scoping in AutoLISP is dynamic, you don't
need to nest definitions for *that* reason.
Another reason to nest definitions, is simply because they are specific to the
functions they are nested in, and are not needed outside of same. But, since
AutoLISP's error handling mechansim leaves the values of local symbols intact
when an error occurs, nesting functions to isolate them from other similarily-
named functions that might exist as globals, is basically futile.
In fact, when a function that has other functions nested in its definition as
declared locals, the bodies of the nested functions will exist in duplicate,
during execution of the parent. That is because when the parent executes, it
then assigns the nested definition to a local symbol, and there are then two
copies of the body of the nested function in memory (one is part of the parent
function's body, and one is assigned to the local symbol).
To make things worse, the first time an error occurs, the nested functions are
them made global, and the next time the parent function executes, there will be
*three* copies of the body of the nested function (the global created by the
error handler, which is pushed onto the stack frame each time the parent of the
nested function executes, makes three).
To make things even worse, if a function is recursive, and calls itself, there
will be one copy of each nested definition pushed onto the stack frame for each
degree of recursion.
So, I don't advocate this approach, partly because it imposes a lot of overhead
both memory, and performance wise, and it serves no purpose in an environment
where there is no real protection against the possiblity of a local variable's
value overwriting the value of a global variable with the same name.
-TonyT.
Thanks for you help Tony, I wasn't sure if I was missing something. I must
say your knowledge of Autolisp is very impressive … anyone ever stump you
with a question yet?
A little off topic, but …. do you see a time when us "general users"
might loose the AutoLisp environment to work in down the road? Do you
think the future will bring a more "user friendly" evironment to ADS for us
common folk? It certainly appears to me that ADS will be the future …
but I am afraid it may end up being beyond my scope as a casual user … in
it's present form.
Just wondering … Phil
Phil – What you got was my opinion on nesting definitions. I can't be "right"
or "wrong" on that, I don't think. I usually can spot the "stump the band"
type of questions, and try to let someone else deal with them. I've made my
share of mistakes, however.
Down the road, I see programming in general, as becoming less-procedural, so
that users will be able to generate programs without resorting to the methods
they must now. And, I see the preference of using one language over another
being less-significant, since the object-oriented paradigm allows you to use
whatever language suits your fancy.
-TonyT.
Another benefit of this 'modular' or layered approach to development is that
since a code fragment can appear once, and be referenced or called from many
places, it becomes much easier to maintain, modify, and enhance the function
or procedure, since all changes are isolated in one single location, and if
you've ever written a large application, you'll appreciate the benefits of a
modular approach to its design.
My favorite example of a useful 'subroutine' is also the one I use the most.
Every time you need to pull a value out of an entity data list, or another
type of association list, you use the ASSOC and CDR functions. Since I do
this often, I doesn't pay for me to write it like this:
(setq color (cdr (assoc 62 <list> )))
This is fine if you only need to do it once or twice, but in my case, I can
save some code, and reduce the complexity of what code I have, using a small
"macro" function for the above:
(defun get (key alist)
(cdr (assoc key alist))
)
With this, whenever I need a value from an association list, I can do it with
a call to just one function, rather than two:
(setq color (get 62 <list> ))
For one who is familar with the function of the (GET) subroutine, this form
will be much clearer, and more effecient.
-TonyT.