Author Topic: To defun or not to defun?  (Read 8308 times)

0 Members and 1 Guest are viewing this topic.

whdjr

  • Guest
To defun or not to defun?
« on: November 23, 2004, 04:48:04 PM »
Why do some people use multiple defuns inside of one all encompassing defun and others just use a bunch of seperate defuns?
Is one way better than another?  wrong or right?
They seem to have the same outcome.

What is the opinion of The Swamp and why?

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
To defun or not to defun?
« Reply #1 on: November 23, 2004, 05:24:27 PM »
It's a personal preference.

I use nested defuns to divide and conquer, to keep code organized and easy to maintain, rather than one monolithic spew of logic. I try to keep each defun to precisely one task. It's a convention that I employ in all the languages I code in (the one task thing that is).

While I could employ a non nested approach, the nesting approach ensures that all dependent logic will be available at run time. I had read long ago that there was a performance penaly for nested defuns, but I have not observed that myself. However, it depends how logic is structured and called. For example, you wouldn't want to call code from inside a loop that iterates thousands of times that would re-defun the called functions each iteration.

Even if there is a slight performance penalty for thoughfully arranged and called code, it's a price I'm willing to pay for the luxery of keeping it structured in a way that is easy to maintain. But I repeat myself, sorry.

</opinion>
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

JohnK

  • Administrator
  • Seagull
  • Posts: 10627
To defun or not to defun?
« Reply #2 on: November 23, 2004, 07:07:06 PM »
Mostly what he ^ said.

Multiple procedure defintions (multiple defuns) are a basic tool used by most programers. The method is quite powerfull when used correctly and ofen a pain when not.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
To defun or not to defun?
« Reply #3 on: November 23, 2004, 08:23:37 PM »
I will generally use nested defuns (also known as local functions when placed in the localize section of the wrapper defun) This prevents crashes with other proggies that may inadvertently have the same name as your program. For example ..

Localized functions
Code: [Select]

(defun C:wrapper ( / local1 local2 local3)
 (defun local1()
  ;;code here
 )
 (defun local2()
  ;;code here
 )
 (defun local3()
  ;;code here
 )
)


Global functions
Code: [Select]

(defun C:wrapper ()
  ;;code here
 )
 (defun global1()
  ;;code here
 )
 (defun global2()
  ;;code here
 )
 (defun global3()
  ;;code here
 )


If you localize the functions they will not be accessable outside the wrapper function.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

JohnK

  • Administrator
  • Seagull
  • Posts: 10627
To defun or not to defun?
« Reply #4 on: November 23, 2004, 09:40:47 PM »
For the most part i agree with you Keith, but this post was touching on the just the opposite. (...But i know what your saying about the localized procedures and how they dont "show up" as global functions. --In theory they arnt available to other procedures.)

So i guess its important to note that the procedures might be local and, in theory, not be accessible to other procedures but the can and are available because autolisp isnt really a OOP language. *phew!*
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
To defun or not to defun?
« Reply #5 on: November 23, 2004, 10:16:09 PM »
Ok, I suppose I should offer just one quick bit of information on how AutoLISP manages its' variables, more aptly those localized variables.

Once you define an application globally, when the calling program exits, the command stays bound and maintains the ability be called by any subsequent application, that is why it is global in nature. The program is available to the entire memory structure and not simply the program utilizing that code.
On the other hand if you define a function as local,  it is defined within the confines of that wrapper function alone. IF there is a value already bound to that variable, then the current value it pushed and the new value as set. When the wrapper function completes successfully, the localized value is popped and the variable returns to it's global value.

There is no way to access a localized variable (if it is indeed declared as localized) unless you are operating within the confines of the wrapper function.

To test this scenario, try this:
Code: [Select]

(defun WHAT_AM_I ()
 (alert "I am a global function")
)

Now at the command line when you enter
(WHAT_AM_I)
You will get an alert telling you that it is a global function.
Now do this in the same drawing
Code: [Select]

(defun C:WRAPPER ( / WHAT_AM_I)
 (defun WHAT_AM_I ()
  (alert "I am a local function")
 )
 (WHAT_AM_I)
)


Now at the command line type
WRAPPER
You will get an alert telling you that it "I am a local function"

NOW before doing anything else type

(WHAT_AM_I)

You will get an alert that states:
"I am a global function"

Clearly the function WRAPPER defun'd the new command WHAT_AM_I , but because it was localized it goes the way of all localized variables and is discarded when the wrapper function completes.

LISP was never intended to be an OOP language and as such it is not, you must bear in mind that localized functions have been around long before OOP was conceived. Try looking back at some true C programming or other non-OOP languages.

So, in theory they are not available to outside programs, nor are they available in reality.

If you understand the way assembly uses push and pop then you will understand how lisp accomplishes this feat. It is nothing more than a stack with the most current value on top (push -ing the old value down), then when the program no longer needs it, (because it has exited) it is removed (pop -ed off the stack, exposing the previous value).

That is why so many people have problems with recursive functions because they do not understand how the variables are stored and retrieved.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
To defun or not to defun?
« Reply #6 on: November 23, 2004, 11:18:10 PM »
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

SPDCad

  • Bull Frog
  • Posts: 453
To defun or not to defun?
« Reply #7 on: November 24, 2004, 12:32:43 AM »
I tend to programme globally with seperate 'defuns'. I can't recall ever nesting a 'defun' within another defun.

I programme this way, because I write code to do specific tasks then consolidate the individual class code into the final programme.  Alot of time the seperate class code becomes part of my library. Which is loaded, and  used like any other lisp command.
   I guess this practice comes for learning to programme in 'Assembly'. If you have ever programmed in 'Assembly' you know that there is very few commands and in order to programme quickly you write small programme  to do specific task and add them to whats known as a libray there by increasing the number of commands you have avilable.

Examples of a few xtra commands I have available to use when I code in lisp. (Yes alot of the little lisps I use where coded before Visual Lisp was standard in Autocad)

r2d.lsp = radians to degrees
d2r.lsp = degrees to radians
dxf.lsp  = returns the corrosponding dxf code vale in a object lisp
dlgload = load a dcl dialog box, with error checking
tolist = sends a list to the listbox in dcl
scantxt =  check to see if character string exists in string, and returns before, after, before with, or after with, or everthing but, depending on what i specify.
               
I think I have an extra 100 command i can use to programme quickly with lisp.
AutoCAD 2010, w/ OpenDCL

visit: http://reachme.at/spd_designs

SMadsen

  • Guest
To defun or not to defun?
« Reply #8 on: November 24, 2004, 05:00:54 AM »
Will, I don't know if the question popped up due to this example but it pretty much bags my argument for nesting defun's: When it's obvious that a function is private to another function.

In the example linked to above, there are named tiles of a dialog box that will not be used anywhere else. Wrapping it within the main dialog code saves storage space, allows for reusable function names and simply keeps a structured appearance that is easier to maintain.
Notice that the MK_LIST function is not nested because it may just happen that my app would want to use another dialog with multiple select list boxes (if it was a real app, that is).

All in all, the things already said by Mr. Puckett et al.

I mostly use nested (read: private) defuns in dialog code and for multiple LAMBDA expressions within algorithms and such.

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
To defun or not to defun?
« Reply #9 on: November 24, 2004, 08:25:47 AM »
Quote from: SPDCad
If you have ever programmed in 'Assembly' you know that there is very few commands and in order to programme quickly you write small programme  to do specific task and add them to whats known as a libray there by increasing the number of commands you have avilable.


I have and yes you are correct. But when I write assembly programming, I typically load them directly into memory and write them out from there. As such I don't utilize them except to cut and paste ... ( I do it the archaic way)
Whatever works I guess ...
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

JohnK

  • Administrator
  • Seagull
  • Posts: 10627
To defun or not to defun?
« Reply #10 on: November 24, 2004, 08:54:12 AM »
I agree with SMadsen, in that that we are saying almost the same thing. But, I was refering to the fact that if you nest your defuns the sub procedure is still availble to you after the main procedure has finished. --For instance:
Code: [Select]
(defun main ()
   (defun sub ()
      (alert "Goodbye!"))
   (alert "Hello there!")
   (sub)
 )

Go ahead and load that up and run 'main'. Now run 'sub'. The procedure isnt localised like a variable is localised. But this is just beating a dead horse...

One of the major reasons for creating 'block-structures' (a procedure with nested procedures) is for creating "true" 'black box' routines/solutions.

Ok im done.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
To defun or not to defun?
« Reply #11 on: November 24, 2004, 09:13:59 AM »
Well no one has mentioned compiling yet, so I guess I will. What about compiling with "separate namespace"?

Using Keith's example:
Code: [Select]

(defun WHAT_AM_I ()
 (alert "I am a global function")
)

(defun C:WRAPPER ()
 (WHAT_AM_I)
)


compiled using separate namespace (WHAT_AM_I) is not available outside of WRAPPER.

Quote
Command: (what_am_i)
; error: no function definition: WHAT_AM_I
TheSwamp.org  (serving the CAD community since 2003)

whdjr

  • Guest
To defun or not to defun?
« Reply #12 on: November 24, 2004, 09:20:27 AM »
WOW!!

When you guys get to explaining you really get to explaining.
All the comments are greatly appreciated and I understand very clearly.
Keith your second statement is very clear indeed and I am very informed now.  It seems that local and global defuns basically work exactly like local and global variables.
Quote from: SMadesn
Will, I don't know if the question popped up due to this example.

Actually, yes it did stem from that post.  I have seen it done that way before and I wasn't sure why.  Now I know.  However, when I took your local defuns out and made them global defuns, then ran the code in the VLIDE it did not recognize those defuns.  Curiuos. :?

Thanks guys,

SMadsen

  • Guest
To defun or not to defun?
« Reply #13 on: November 24, 2004, 09:34:54 AM »
Quote from: whdjr
However, when I took your local defuns out and made them global defuns, then ran the code in the VLIDE it did not recognize those defuns.  Curiuos. :?

Ah yes. There's a lesson there. You didn't remove the symbol names from the local list, did you?
So .. when a tile makes a calls to, say, doListBox, it is seen as a local variable within C:WHDJR and is therefore nil. The callback expression (setq listpicks (doListBox $value samples)) essentially becomes (setq listpicks (nil $value samples))

whdjr

  • Guest
To defun or not to defun?
« Reply #14 on: November 24, 2004, 09:37:29 AM »
Quote from: SMadsen
You didn't remove the symbol names from the local list, did you?

Duh!?!?!?!?!?

That's like lisp 101 right.

Thanks stig,

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
To defun or not to defun?
« Reply #15 on: November 24, 2004, 02:23:00 PM »
Quote from: Se7en
I agree with SMadsen, in that that we are saying almost the same thing. But, I was refering to the fact that if you nest your defuns the sub procedure is still availble to you after the main procedure has finished. --For instance:
Code: [Select]
(defun main ()
   (defun sub ()
      (alert "Goodbye!"))
   (alert "Hello there!")
   (sub)
 )

Go ahead and load that up and run 'main'. Now run 'sub'. The procedure isnt localised like a variable is localised. But this is just beating a dead horse...

One of the major reasons for creating 'block-structures' (a procedure with nested procedures) is for creating "true" 'black box' routines/solutions.

Ok im done.


Please note that you DID NOT declare them local. This is exactly what I said previously. If you do not declare them local it makes no difference. This is exactly how the autoload stub function creates the autoload lisp programs.

Quote from: Mark Thomas
Well no one has mentioned compiling yet, so I guess I will. What about compiling with "separate namespace"?

If you compile to a seperate namespace none of the defuns are available EXCEPT those defun'd with C:
It makes absolutely no difference if you declare them local or not in compiled seperate namespace applications.
The location of the defuns are also unimportant (inside vs outside a wrapper function).
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
To defun or not to defun?
« Reply #16 on: November 24, 2004, 05:50:37 PM »
Quote from: Keith
It makes absolutely no difference if you declare them local or not in compiled seperate namespace applications.
The location of the defuns are also unimportant (inside vs outside a wrapper function).

That's the point I was trying to make.
TheSwamp.org  (serving the CAD community since 2003)

JohnK

  • Administrator
  • Seagull
  • Posts: 10627
To defun or not to defun?
« Reply #17 on: November 26, 2004, 12:32:28 PM »
Ok keith i see what your saying. (Localise the nested procedures in the parameters list.) But I think lazyness has taken over here. (we have been getting lazy when it comes to the correct way to use local procedures.)

There is a lot more to declairing a local procedure then what we have been doing. If your ever in a situation that requires you use defun instead of lambda inside a procedure you need to do a lot more then just declaire a function inside the main. for instance: you dont need to use bound variables. you can then use free variables. making those procedures are truly "local" (In that they opperate on variables inside the main procedure and not on concepts.) That is one of the major points to local procedures.  

There is a lot to this subject; Like other things there are many other ways to accomplish the same task. But if your after a way to "hide" your procedures or create a "true blackbox" routine then you need to read up on the subject more. There is a lot of information on procedure structure(s). look into it if your interisted in it.

Code: [Select]
;;; Create a local function that uses free vars instead of bound.
;;; NOTE: this can be done using lambda alot easier.
;;; Using lambda would make this alot more readable and easier to
;;; maintain in the long run.
(defun main ( / helper var1)
 ;; Create a local function that operates on the variables themselves.
  (defun helper ()
     (setq var1 (+ 1 var1)) )
  ;; Get a number and declaire a variable
  (setq var1 (getint "Enter a number: "))
  ;; chagne the variable
  (helper)
  ;; promt user the new value of the variable
  (prompt var1)
  (princ)
 )
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org