Jump to content

localizing variables?


cadman6735

Recommended Posts

I am having issues understanding how and when and what variables to localize...

 

 

I snagged some code from this site to play around with (entmakex is from lee mac post: Entmake Functions) the rest is me playing...

 

 
(defun Line ()
 (entmakex (list (cons 0 "Line")
   (cons 8 "0")
   (cons 10 pt1)
   (cons 11 pt2)
    )
 )

(princ)
)

(defun C:test ()
 (setq
   pt1 (getpoint "\nSelect fist point")
   pt2 (getpoint "\nSelect next point")
 )
(Line)
 
(princ)
)

 

 

should I localize my variables in the (defun Line (pt1 pt2)) , (defun Line (/pt1 pt2))?

 

or

 

should I localize my variables in the (defun c:test (pt1 pt2)) , (defun Line (/pt1 pt2))?

 

Which is the correct way to write the variable? I give two examples above for each.

 

either way when I "localize" I get a message:

 

"too few arguments"

 

 

this seems as it should be a simple thing to understand but I am having a huge hurdle getting over this.

 

1.) Which variable should be localized?

 

(I read code and only some of the variables are localized and the rest seem to be not.)

 

2.) When should I localize a variable?

 

 

Thanks

Link to comment
Share on other sites

  • Replies 52
  • Created
  • Last Reply

Top Posters In This Topic

  • irneb

    10

  • Se7en

    8

  • Lee Mac

    8

  • cadman6735

    7

Here it is ...

 

(defun Line (pt1 pt2)
 (entmakex (list (cons 0 "Line")
   (cons 8 "0")
   (cons 10 pt1)
   (cons 11 pt2)
    )
 )

(princ)
)

(defun C:test (/ pt1 pt2)
 (setq
   pt1 (getpoint "\nSelect fist point")
   pt2 (getpoint "\nSelect next point")
 )
(Line pt1 pt2)
 
(princ)
)

 

Tharwat

Link to comment
Share on other sites


(defun Line ( p1 p2 )
 (entmakex
   (list
     (cons 0 "LINE")
     (cons 10 p1)
     (cons 11 p2)
   )
 )
)


(defun C:test ( / pt1 pt2 )
 (if (and (setq pt1 (getpoint "\nSelect fist point"))
          (setq pt2 (getpoint "\nSelect next point" pt1)))
   
   (Line pt1 pt2)
 )
 
 (princ)
)

 

Don't add the 'princ' in the subfunction, otherwise the entity created will not be returned and hence the subfunction becomes less useful.

 

Lee

Link to comment
Share on other sites

I'm not sure that this is a very clear example, but my mind is blank of examples at the moment... :?

 

(defun f1 ( / Local )

 (setq Local "Local" Global "Global")

 (princ "\nf2 called from within f1: ")
 (f2)
)

(f1)

(princ "\n------------")

(princ "\nf2 called from outside of f1: ")
(f2)


(defun f2 ( )

 (princ "\nLocal= ")
 (princ Local)
 (princ "\nGlobal= ")
 (princ Global)

 (princ)
)

 

When run:

 

f2 called from within f1:
Local= Local
Global= Global
------------
f2 called from outside of f1:
Local= nil
Global= Global

 

So, you can see that after the function has been evaluated, those symbols localised within that function are set to nil. But, when f2 is called from within f1, f1 has not finished evaluating, hence the variables are still accessible from f2.

Link to comment
Share on other sites

 

1.) Which variable should be localized?

 

2.) When should I localize a variable?

 

 

 

Tharwat, and Lee have already given you sound advice. If I might add my two cents toward the remaining questions...

 

A local variable is only available to the function that defines it. Once the function is complete, the variable returns to its original state, or nil.

 

A global variable is defined with the purpose of being made available to other routines.

 

Please note - Not to be confused with system, or environment variables.

 

An argument is a variable that has been provided to another sub-routine.

 

Example:

 

(defun c:TEST ([color=red]argument[/color] / [color=blue]localVariable[/color])
 ;; ...Code
 )

 

 

 

It is accepted as a best practice to localize your variables (where applicable) in order to avoid potential conflicts with other routines, and for good housekeeping, so as to not have defined variables floating around inside AutoCAD when your routine is done.

 

 

There are many resources available to help you, here are just a couple:

  • Developer Documentation (F1 from VLIDE)
  • Google: Visual LISP Developer's Bible

Hope this helps!

Link to comment
Share on other sites

Tharwat, I can see the two differences in your code on the variables, question being

 

(pt1 pt2) are input variables?

(/ pt1 pt2) are localized variables? is there a space after the (/ )? I can see in Lee Macs there is a space before ( / ) and after, does this make a difference?

 

 

 

Lee Mac, I notice in your code, you (defun Line (p1 p2) "p1" "p2"... I was using pt1 and pt2? I am assuming that when having different function such as a subfunction here, I can make the variables different... Line can be p1 and p2, when C:test can be pt1 and pt2 because they are two separate function yet they are working together, ( I hope that made sense)

 

Lee, why do you add pt1 and pt2 here: (Line pt1 pt2), it seems to work fine without the variables?

 

 

 

How do I know which variables to localize? Or should I localize all variable?

Link to comment
Share on other sites

Tharwat, and Lee have already given you sound advice.

 

RenderMan do you call my first post is sound advice. :)

 

Actually my post was wrong, due to the (princ) function that I forgot to remove, and Lee remove it from his post

as best as it should be.

 

So for mine, I would agree with you. But Lee two post are really perfect and wildly explained in his second post.

 

Thanks for your nice words all the time.

 

Tharwat

Link to comment
Share on other sites

Cadman,

 

I think you need to read up on the difference between arguments and variables.

 

An example of an argument:

 

(defun dtr ( x ) (* pi (/ x 180.)))

Here the function 'dtr' requires one argument 'x', when we call this function we must do so hence:

 

(dtr 45)

Supplying it with data to be bound to the argument. The argument symbol is local to the function in which it is defined and hence will not 'clash' with other similarly named variables/arguments.

 

Example:

 

(defun test ( / var1 )

 (setq var1 "Lee")

 (f1 "Mac")

 (princ "\nValue of var1 in main function: ") (princ var1)
 (princ)  
)

(defun f1 ( var1 )
 (princ "\nValue of var1 in subfunction: ") (princ  var1)
 (princ)
)

(test)

Value of var1 in subfunction: Mac
Value of var1 in main function: Lee

I believe it is better to pass arguments where possible, so that one can have a library of stand-alone functions that are independent of variables and can be called upon when needed.

 

As, with your example posted earlier, the Line subfunction depends on the two points being named 'pt1' and 'pt2' everytime it is used - if this is not the case, it will error.

 

Whereas, in my example, I can call my points whatever I like, and just supply their values as arguments to the subfunction.

 

Also, don't use (princ) at the end of every function, as the return of the function is exactly what we require the majority of the times we call a subfunction.

 

Example:

 

(defun dtr ( x ) (* pi (/ x 180.0)))

(print (dtr 45.0))

If we had (princ) at the end of dtr, we would effectively be writing off the calculation completely, as the value it returns cannot be retrieved as it is not bound to any variable.

 

One more thing: the LISP interpreter disregards white space, unlike languages such as Python in which white space defines function structure, you can have as many lines/spaces as you want between your functions and their arguments.

 

Lee

Link to comment
Share on other sites

Tharwat, I can see the two differences in your code on the variables, question being

 

(pt1 pt2) are input variables?

(/ pt1 pt2) are localized variables? is there a space after the (/ )? I can see in Lee Macs there is a space before ( / ) and after, does this make a difference?

How do I know which variables to localize? Or should I localize all variable?

 

Yes there is a space after the slash like this (/ pt1 pt2) and I did it right in my post take a deep look at it once a gain please.

 

And for The first (pt1 pt2) these variables do not have values so that why they were without a slash.

 

And for the second it's on the contrary of the first one. :)

 

Hope this would be clear to read.

 

Tharwat

Link to comment
Share on other sites

RenderMan do you call my first post is sound advice. :)

 

Actually my post was wrong, due to the (princ) function that I forgot to remove, and Lee remove it from his post

as best as it should be.

 

So for mine, I would agree with you. But Lee two post are really perfect and wildly explained in his second post.

 

Thanks for your nice words all the time.

 

 

Yes, Tharwat, I still consider your prompt response (for the most part) sound advice, and here's why:

 

The core of the OP's question(s) are regarding placement of variables, arguments, and the like. You quickly, and correctly placed the variables for the OP. Technically the princ is undesired in this instance, as a result of the sub-routine... otherwise, the princ is preferred. You made a very understandable mistake, in your haste.

 

I agree with you that Lee's posts thereafter are preferable... in fact I often prefer Lee's code over my own! :lol:

 

The kind words accredited to you are (again, for the most part), well deserved.

 

Your development capabilities have come a long way since I first encountered you (not that mine are much better). :) Just keep learning, and you'll do fine.

Link to comment
Share on other sites

Maybe this will help, also:

 

(defun Line ( p1 p2 ) [color=seagreen];<- two arguments[/color]
 (entmakex
   (list
     (cons 0 "LINE")
     (cons 10 p1) [color=#2e8b57];<- argument[/color]
     (cons 11 p2) [color=#2e8b57];<- argument[/color]
   )
 )
)


(defun C:test ( / pt1 pt2 ) [color=#2e8b57];<- two local variables[/color]
 (if (and (setq pt1 (getpoint "\nSelect fist point"))
          (setq pt2 (getpoint "\nSelect next point" pt1)))
   
   (Line pt1 pt2) [color=#2e8b57];<- two local variables, being passed as arguments[/color]
 )
 
 (princ)
)

Link to comment
Share on other sites

Again, you guys are too fast at what you do, impressive

 

Arguments and variables, thanks, I can move on from here

 

too all, you are great teachers, thanks for sharing your knowledge.

Link to comment
Share on other sites

I did it right in my post take a deep look at it once a gain please.

 

tharwat, I was not questioning or comparing your code accuracy, I am sorry if my question bled together and was mis-interprated.

I thank you for your help and do not wish to be seen as rude.

Edited by cadman6735
Link to comment
Share on other sites

tharwat, I was not questioning or comparing your code accuracy, I am sorry if my question bled together and was mis-interprated.

I thank you for your help and do now wish to be seen as rude.

 

No worries my friend, I just wanted you to get sure of it once again that's all.

 

And you're welcome any time.

 

You have been so nice.

 

Wish you best of luck

 

Tharwat

Link to comment
Share on other sites

Actually, the parameters do not need to be passed to the Line function ;

and you can just make the variables local to the calling

routine

 

eg:

 

(defun Line () (entmakex
                (list (cons 0 "Line") (cons 8 "0") (cons 10 pt1) (cons 11 pt2)))
)
(defun C:doit (/ Pt1 Pt2)
 (setq pt1 (getpoint "\nSelect fist point")
       pt2 (getpoint pt1 "\nSelect next point")
 )
 (Line)
 (princ)
)

 

A little testing will demonstrate this ... even is there are existing global variables named Pt1 and Pt2 they will not be addressed or affected by this code.

Link to comment
Share on other sites

Yes, Kerry ... you don't need to pass the pt1 and pt2 as arguments to line (in this case). This is due to a "quirkiness" of lisp, in most other programming languages this won't work though. Lisp's "localized" variables aren't "scoped" only inside the function which defined them. Basically all that localizing variables by placing them after the / in the defun's header does is the following:

 

 

  • Open a new memory location on the stack linked to that variable name. Could be seen as doing (cons nil #varstack#). So if there's already another variable with the same name, its data is stored on a lower portion of the stack.
  • Set the variable name to link to the newest item on this stack, so any assignments / queries of that variable name only refers to the value in that item of the stack.
  • Once the defun completes, remove the last item from that variable name's stack (if it's the only item remove the stack). As if you did a cdr on the stack.

 

As an example:

(setq var "Test") ;Setup a global variable

The stack structure of the var variable looks thus: VAR=("Test").

 

Now lets make some functions:

(defun foo1 (arg / ) (princ arg))

(defun foo2 ( / ) (princ var))

(defun foo3 ( / var) ;Line foo3.0
 (foo1 var) ;Line foo3.1
 (foo2) ;Line foo3.2
 (setq var 1234) ;Line foo3.3
 (foo1 var) ;Line foo3.4
 (foo2) ;Line foo3.5
)

(foo1 var) ;Line foo4.1
(foo2) ;Line foo4.2
(foo3) ;Line foo4.3
(foo1 var) ;Line foo4.4
(foo2) ;Line foo4.5

I think lines 4.1 & 4.2 are reasonably simple. Let's look at line 4.3: It call the function foo3. After the header completes (Line foo3.0) the variable stack looks like this: VAR=(nil "Test").

 

On line 3.1 the current latest value on the stack is sent to an argument of foo1. The argument inside foo1 is handled as if it's a variable with a value set to it already. Thus the global variable stack looks like this (while inside foo1):

VAR=(nil "Test"); ARG=(nil).

 

After foo1 completes the stack for ARG is cleared and removed. Thus through line 3.2 the stack only looks like: VAR=(nil "Test").

 

Now line 3.3 assigns a value to the variable VAR. This changes only the "last" item in var's stack (or rather the uppermost item): VAR=(1234 "Test").

 

So here line 3.4 does a similar thing when calling foo1. Only difference being that the argument ARG has a value of 1234 instead of nil as previous.

 

Line 3.5 also does something similar, still only the latest value of VAR is used. Even though foo2 is not part of foo3.

 

Now comes the cleanup as foo3 completes. the stack of VAR basically get's the latest value removed. It's as if a cdr is called on it. So it's returned to its original state: VAR=("Test").

 

In which case lines 4.4 and 4.5 have exactly the same state as lines 4.1 and 4.2.

 

In most other programming languages (e.g. VB, C, etc.) a call to foo2 (even from inside foo3) would have resulted in the value Test being printed. Lisp (and some other languages) don't use this variable scoping idea, i.e. everything is "global" in a sense - localization simply keeps previous levels of values from being overwritten and cleans up after itself.

 

So in lisp you get the same effect whether you use foo1 or foo2. Both operate on the same values. However this doesn't mean you shouldn't need to worry about localization and / or using arguments. There's some major reasons for using arguments and localized variables:

 

 

  1. Using arguments ensures that anything the function does to the argument does not affect other functions. So inside foo1 a call to (setq arg 567) will not affect the value(s) of var at all.
  2. Using localized variables allows a function to "know" that there's nothing "strange" that may have been placed into that variable's latest value. So without doing anything else you're assured that the VAR has a value of nil. Thus you need not initialize to nil at the start of the function.
  3. Because any assignments or queries on the variable (or argument) only reflects on the last value of that variable. Anything done while the function is not running will operate on a previous "level" of that variable's stack.
  4. Global variables are prone to have their values set by other functions, which may or may not know of each other. It makes for inconsistencies in values, e.g. running command A before B causes B to fail.
  5. The variable / argument's name can be a lot more condensed as it's "only relevant" while the function is running. Global vars generally need more descriptive names to aleviate point 4 above.
  6. If not using arguments on a function, it could be called while the required variables are not defined. If the function needs those to have a specific value it might then fail if called from another place than intended.

 

There's probably more reasons, but these are off the top of my head.

Link to comment
Share on other sites

irneb, is this behavior typical for all interpreted languages (Note: You had mentioned VB in your list and I'm trying to shore up my memory[ies] about VB but i haven't had my coffee yet so bear with me please)?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...