CompuServe Thread

#Function Hierarchy?

9 messages in this thread
#33790From: Phillip E. RutledgeFeb 19, 1992 11:32 PM
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
#33794From: Tony Tanzillo [LISP TM]Feb 20, 1992 12:21 AM
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>…
#33833From: Duff Kurland [Adesk]Feb 20, 1992 10:42 AM
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.
#33846From: Tony Tanzillo [LISP TM]Feb 20, 1992 1:59 PM
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.
#33911From: Phillip E. RutledgeFeb 20, 1992 11:24 PM
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
#33931From: Tony Tanzillo [LISP TM]Feb 21, 1992 1:49 AM
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.
#34103From: Phillip E. RutledgeFeb 22, 1992 12:57 AM
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
#34191From: Tony Tanzillo [LISP TM]Feb 22, 1992 4:17 PM
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.
#33795From: Tony Tanzillo [LISP TM]Feb 20, 1992 12:21 AM
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.