Resusing variables without having to set their value again

Resusing variables without having to set their value again

Anonymous
Not applicable
2,607 Views
24 Replies
Message 1 of 25

Resusing variables without having to set their value again

Anonymous
Not applicable

I am having trouble reusing a couple variables which I set values to, which I have been researching online on how to set "Global Variables" like surrounding the variable in *'s but nothing seems to be working out.

 

 (defun c:WSH( / sset );Write Spot Height
		(setq sc1 (getreal "\n* Enter Plot Scale : "))
		(setq sc2 (* (/ sc1 200) 1))
		(setq th (* (/ sc1 1000.0) 2)
	(command "_insert" "SH_CROSS.dwg" 1000,1000)
	(setq olayer (getvar "clayer"))
	(setvar "clayer" "SPOTLEVELS")
	(SPOT)
 	(if sset (cross))
	(pstext "TK " "" 1)
	(while (eq 1 (logand 1 (getvar "CMDACTIVE")))(command pause))
	(if (/= (getvar "clayer") olayer)
		(setvar "clayer" olayer)
	)
 (princ)
)

This is what the routine in general is and I am trying to keep the variables "sc1, sc2 and th" (these variables are used within the other subfunctions) constant so that I don't have to set keep typing in the values. Is there a way I could possible fix this?

0 Likes
Accepted solutions (1)
2,608 Views
24 Replies
Replies (24)
Message 2 of 25

martti.halminen
Collaborator
Collaborator

 

I'm not absolutely sure what you are trying to achieve, but let's try to clarify matters.

 

- First, the convention of naming global variables with names surrounded with asterikses: *some-global-variable* is just a user convention with no semantic meaning: the compiler/interpreter does not change its behaviour in any way for these, they are just names like any other. So it is up to the programmer to use the names sensibly.

 

There are no user-definable constants in AutoLISP, so if you have a global variable, there is no way to prevent any part of the program from changing its value. So, if you do not want any other part of the program changing the values, define them as local, for example

 

(defun c:WSH( / sset sc2 th)

would define sc2 and th as local so no other part of the program would see them.

 

On the other hand, your program doesn't seem to use those itself, so is the idea rally to have them as global?

 

If the problem is just avoiding giving the scale at every call to WSH,  make the (setq sc1 ...) call conditional: if the user just hits ENTER, use the previous value.

 

-- 

0 Likes
Message 3 of 25

Anonymous
Not applicable

Sorry for the confusion. What I am trying to do is, once those variables have values set to them, If I want to use the WSH function again, I don't want to have to type the same value over and over again as those values will never change once they are set or once AutoCAD is closed.

 

This is probably where the Conditional Statement comes into play.

 

From what I understand it would roughly be like

 

(cond ((= sc1 nil)(= sc2 nil)(= th nil)
       ;  (setting the values for these)
          )
          )  ;continue with the rest of the function

The variables "sc1, sc2 and th"  are used in other function such as "SPOT" and "CROSS", I just haven't posted them here. 

0 Likes
Message 4 of 25

Kent1Cooper
Consultant
Consultant

One way is to ask for such a value only if it does not already have one:

 

  (if (not sc1) (setq sc1 (getreal "\n* Enter Plot Scale : ")))

 

which you will see some do this way:

 

  (or sc1 (setq sc1 (getreal "\n* Enter Plot Scale : ")))

Kent Cooper, AIA
0 Likes
Message 5 of 25

Anonymous
Not applicable

So if I used;

 (defun c:WSH( / sset );Write Spot Height
 (or ( sc1 sc2 th)
		(setq sc1 (getreal "\n* Enter Plot Scale : "))
		(setq sc2 (* (/ sc1 200) 1))
		(setq th (* (/ sc1 1000.0) 2)
)
	(command "_insert" "SH_CROSS.dwg" 1000,1000)
	(setq olayer (getvar "clayer"))
	(setvar "clayer" "SPOTLEVELS")
	(SPOT)
 	(if sset (cross))
	(pstext "TK " "" 1)
	(while (eq 1 (logand 1 (getvar "CMDACTIVE")))(command pause))
	(if (/= (getvar "clayer") olayer)
		(setvar "clayer" olayer)
	)
 (princ)
)

Then if I redo the WSH function I will not have to assign a value to those variables each time I want to use it?

0 Likes
Message 6 of 25

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

... 

This is probably where the Conditional Statement comes into play .... it would roughly be like

 

(cond ((= sc1 nil)(= sc2 nil)(= th nil)
       ;  (setting the values for these)
....

.... 


A couple of things about that:

 

[The (= sc1 nil) thing would work, but it's not necessary to check whether something is "equal to nil" -- you can just check whether it exists at all, with (not sc1) or (null sc1).]

 

The construction there would need to do one test per condition, and follow it with what to do if that one test does not return nil, rather than three in a row like that.  If the idea is to test for the existence of values in all three before going on to set them if they don't exist, it would need to be more like:

  (cond (and (not sc1) (not sc2) (not th))

    ....

 

or

 

  (cond (not (or sc1 sc2 th))

    ....

 

But in such a case an (if) function would do just as well.  And if by chance one or two, but not all, of those did have values, it would not ask for a value for the one(s) that didn't.

 

If you built it like this:

 

  (cond

    ((not sc1) .... {ask for a value for that} ....)

    ((not sc2) .... {ask for a value for that} ....)

    ((not th) .... {ask for a value for that} ....)

  ); cond

 

then if sc1 had a value, it would never get to checking whether sc2 and/or th had values.

 

If there would always and unfailingly be values in all three variables if there was a value in any one of them, you could do something like this:

 

(if (not sc1) ; check for any one of them

  (setq ; then -- get all three

    sc1 .... {ask for a value for that} ....

    sc2 .... {ask for a value for that} ....

    th .... {ask for a value for that} ....

  ); setq

); if

Kent Cooper, AIA
0 Likes
Message 7 of 25

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

So if I used;

 (defun c:WSH( / sset );Write Spot Height
 (or ( sc1 sc2 th)
		(setq sc1 (getreal "\n* Enter Plot Scale : "))
		(setq sc2 (* (/ sc1 200) 1))
		(setq th (* (/ sc1 1000.0) 2)
)
....

Then if I redo the WSH function I will not have to assign a value to those variables each time I want to use it?


[I didn't notice before that the sc2 and th values are results of the sc1 value, so obviously it wouldn't be necessary to ask for values for the later ones after getting one for sc1, as I suggested.]

 

RE-EDIT:  As written, sc1 would be taken as a function name.  I think what you intend there is:

 

  (or (or sc1 sc2 th)

 

or possibly

 

  (or (and sc1 sc2 th)

 

But if that's how it's built, then go on to what I originally posted:

 

Suppose somehow there's a value in th but not in the others, however that might occur.  The first construction above would not ask for an sc1 value, and therefore it would try to proceed without all the necessary values.  Since you can't localize those variables if you want them reusable, but since the others are resultants from sc1, I would do it more like the end of my previous Reply, but of course with the sc2 and th values calculated from sc1 rather than asking for them.

 

 

Kent Cooper, AIA
0 Likes
Message 8 of 25

Anonymous
Not applicable

Okay so using the code you supplied. It would end up looking like so;

 

 (defun c:WSH( / sset );Write Spot Height
 (if (not sc1)
		(setq sc1 (getreal "\n* Enter Plot Scale : "))
		(setq sc2 (* (/ sc1 200) 1))
		(setq th (* (/ sc1 1000.0) 2)
)
....

And after doing this the first time, if I use the "WSH" command again, it will find out if "sc1" has a value and if so it will skip the "setq" stage?

0 Likes
Message 9 of 25

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

Okay so using the code you supplied. It would end up looking like so;

 

  (if (not sc1)
		(setq sc1 (getreal "\n* Enter Plot Scale : "))
		(setq sc2 (* (/ sc1 200) 1))
		(setq th (* (/ sc1 1000.0) 2)

And after doing this the first time, if I use the "WSH" command again, it will find out if "sc1" has a value and if so it will skip the "setq" stage?


[See my Edit to Post 7, just so you don't miss it.]

 

Yes except for missing a right parenthesis at the end of the th setting].  If there's a value in sc1, the (not sc1) will return nil, so it won't do what's called for as a 'thenexpr' argument.  And you can shortcut that a little -- a single (setq) function can set more than one variable:

 

  (if (not sc1)

    (setq

      sc1 (getreal "\n* Enter Plot Scale : ")

      sc2 (* (/ sc1 200) 1)

      th (* (/ sc1 1000.0) 2)

    ); setq

  ); if

 

If there's any possibility that sc1 might have a value but the others might not, consider having that set only sc1 within that (if) function, and then outside that, calculate sc2 and th.   That way, it won't ask for sc1, but it will still put values into sc2 and th.

Kent Cooper, AIA
Message 10 of 25

Anonymous
Not applicable

Okay that makes sense. Thanks for clearing all the confusion up for me.

0 Likes
Message 11 of 25

john.uhden
Mentor
Mentor
Hold it a second. If sc1 is to always be the same, then why not just set it? If it might not want to be the same every time, then you have to give the user the ability to change it. But you could make it easy by making the previous value the default so if the user wants to use the previous value, then all he has to do is hit Enter...
Let's say that the most popular value of sc1 is 3.45
(if (not sc1)(setq sc1 3.45))
(if (setq ans (getreal (strcat "\nSC1 <" (rtos sc1 2 2) ">: ")))
(setq sc1 ans)
)
And if you want to hold its latest value between drawings in one AutoCad session, use the vl-bb-set and vl-bb-ref functions, or write its value to a default file or into the registry.

John F. Uhden

0 Likes
Message 12 of 25

Kent1Cooper
Consultant
Consultant

@john.uhden wrote:
.... you could make it easy by making the previous value the default so if the user wants to use the previous value, then all he has to do is hit Enter...
Let's say that the most popular value of sc1 is 3.45
(if (not sc1)(setq sc1 3.45))
(if (setq ans (getreal (strcat "\nSC1 <" (rtos sc1 2 2) ">: ")))
(setq sc1 ans)
)
And if you want to hold its latest value between drawings in one AutoCad session, use the vl-bb-set and vl-bb-ref functions, or write its value to a default file or into the registry.

There's a way to offer the previous setting as default if it exists, without that temporary [in this case 'ans'] variable, and even to incorporate an initial default value for Enter even on first use without presetting it if it doesn't exist yet, as done above, and all within one (setq) function.  Let's say you want to offer an initial default of 100 if a value has not yet been set for sc1:

 

(setq sc1

  (cond

    ( (getreal ; User input

        (strcat

          "\n* Enter Plot Scale <"

          (if sc1 (rtos sc1 2 2) "100.00"); offer prior value as default if it exists, otherwise offer 100

          ">: "

        ); strcat

      ); getreal

    ); end User-input condition [returns nil on Enter, going on to:]

    (sc1); Enter on subsequent use -- use prior value [nil if there isn't one, going on to:]

    (100.0); Enter on first use -- no prior value; use initial default

  ); cond

); setq

 

An Environment Variable [via (setenv) / (getenv)] is another way to preserve the value across multiple editing sessions of the same drawing, and apply the same one across multiple drawings without having to set it in each.  But if it's a plot scale, presumably you wouldn't want it applied at the same value in all drawings.

 

Other considerations:

 

Since sc1 is a plot scale, might it be extractable from something like the DIMSCALE System Variable, rather than having to ask the User for it?

 

Is there any reason not to simplify the calculations for the sc2 and th variables?  [It seems odd to multiply something by 1.]  Adjusting from Post 9:

 

 (if (not sc1)

    (setq

      sc1 (getreal "\n* Enter Plot Scale : ")

      sc2 (/ sc1 200)

      th (/ sc1 500)

    ); setq

  ); if

 

Those divisors can be integers [not like the original 1000.0 with decimal], since sc1 will be a real number.  Which raises the question:  would sc1 always be a whole number [which seems likely for plot scales]?  If so, should it use (getint) instead?  If changed to that, the offered default in the (cond) above should be:

 

          (if sc1 (itoa sc1) "100"); offer prior value as default if it exists, otherwise offer 100

and the Enter-on-first-use default should be:
    (100); Enter on first use -- no prior value; use initial default

 

and those divisors in calculating sc2 and th should both be real numbers, or at some scales the sc2 and th values will come out wrong.

Kent Cooper, AIA
0 Likes
Message 13 of 25

john.uhden
Mentor
Mentor
Sorta like skinning a cat.

BTW, how do you get your posted code to indent? Tabs?

John F. Uhden

0 Likes
Message 14 of 25

Anonymous
Not applicable

Once the value has been set, it won't ever need to be changed throughout the same drawing because the plan is a constant scale.

 

With that, it's not actually the scale of the drawing file itself but a A3 plan template scale which determines the size of the contents within the file.

 

 

One last thing, when I tried to use;

 (defun c:WSH( / sset );Write Spot Height
  (if (not sc1)
	(setq 
		sc1 (getreal "\n* Enter Plot Scale : ")
		sc2 (* (/ sc1 200) 1)
		th (* (/ sc1 1000.0) 2)
        )
)

Within the function, when I load the LISP file into the drawing and try to use the command, it says "Unknown Command"???

Have I done something wrong here?

0 Likes
Message 15 of 25

john.uhden
Mentor
Mentor
It appears that each of you setq lines has an extra parenthesis at the end.

Also, why not just th (/ sc1 500) ?
And why * 1 ?

I mean it's okay, but the guys around here just love to get picky (mostly with their own work).

John F. Uhden

0 Likes
Message 16 of 25

scot-65
Advisor
Advisor
Looks like you want a session Gremlin?
Be careful of these as other developed programs might use the
same variable and may cause a crash that is hard to dig up. BT/DT.

To avoid this, assign a naming convention and stick with it.
I use a prefix "USER_" followed by the name of the defun.
For your example it would be "USER_WSH".

If a particular program has more than one value to store
(for example get_tile in a DCL), create a LIST. Access this
list using NTH.

(setq USER_WSH (list 10.2 3000 "My time is up"))

(nth 1 USER_WSH) = 3000
NTH is zero based.

Beyond the basics:
Looks like you are handling plot chores. It would make sense to
store the variable as a Dictionary item so as to fetch the variable(s)
at a later time. Look into VLAX-LDATA-PUT and VLAX-LDATA-GET.
The information that will be stored in the DWG file is insignificant
in size /character count such that the file size increase is negligible.
VLAX-LDATA-PUT does accept LISTs so only one key is required per
developed program.

???

Scot-65
A gift of extraordinary Common Sense does not require an Acronym Suffix to be added to my given name.

0 Likes
Message 17 of 25

Anonymous
Not applicable

Yeah I saw that and fixed it up so it's;

 (defun c:WSH( / sset );Write Spot Height
	(setq 
		sc1 (getreal "\n* Enter Plot Scale : ")
		sc2 (/ sc1 200)
		th (/ sc1 500)
	)

But it still will not allow me to use the command. I've tried setting it back to the old version of it but it still ain't letting me use the command.

0 Likes
Message 18 of 25

john.uhden
Mentor
Mentor
I presume you are posting only an excerpt of your code. If not, then you are missing the closing parenthesis that pairs with (defun. Maybe you want to post the full code.

John F. Uhden

0 Likes
Message 19 of 25

Anonymous
Not applicable
 (defun c:WSH( / sset );Write Spot Height
	(setq 
sc1 (getreal "\n* Enter Plotting Scale : ") sc2 (/ sc1 200) th (/ sc1 500)
) (command "_insert" "SH_CROSS.dwg" 1000,1000) (setq olayer (getvar "clayer")) (setvar "clayer" "SPOTLEVELS") (SPOT) (if sset (cross)) (pstext "TK " "" 1) (while (eq 1 (logand 1 (getvar "CMDACTIVE")))(command pause)) (if (/= (getvar "clayer") olayer) (setvar "clayer" olayer) ) (princ) )
0 Likes
Message 20 of 25

Kent1Cooper
Consultant
Consultant

I don't see anything obvious that would prevent the command name from being recognized, but I'll take a shot in the dark:  Could it be that a space is required after the command name and before the arguments/local-variables list's opening left parenthesis?  I've always done that, and that's typically what I see in other people's code, though I don't know whether it's required.  But maybe it's trying to use WSH( as the command name, and then the following syntax is incorrect.

 

Internally, is the Insert command completed by something in the (spot) or (cross) or (pstext) function?  And that 1000,1000 should be in double-quotes [or done as a list].  If it's not being completed, and even with that incorrect point-input format, I would expect that to cause a different error, but it should at least recognize the command name, and hit an error only when you run the command.

 

[In answer to your earlier question, my preference is two spaces per indentation level.  Tabs take up too much space for my taste, and one space doesn't make an obvious-enough difference.]

Kent Cooper, AIA
0 Likes