Author Topic: What is the best way to localise or set variable to NIL  (Read 8547 times)

0 Members and 1 Guest are viewing this topic.

jaydee

  • Guest
What is the best way to localise or set variable to NIL
« on: August 12, 2011, 12:19:42 AM »
Hi.
I have these sub function call from a main function.
The reason i have these sub function is that they will be called to many main functions.
Its quite straight forward if these subfunctions only contain a few variables, i can simply localise them, but not sure if this is the best way to go if each subfunctions contain roughly 100 or more varibles.What im doing at the moment is to have a subfunction (SetVar2Nil) at the end of the main function.

My question is no matter how you look at it, its a long list of variable.
Is there a short cut to set these vars to nil/or localise them properly.

Thankyou, any advice will be much appriciated.

Code: [Select]
(defun c:fun1 ()
(subfunc1)
(subfunc2)
etc....etc
(SetVar2Nil)
)

Code: [Select]
(defun subfunc1 ()
(setq a "AAA")
(setq b "BBB")
(setq c "CCC")

;Roughly 100 or more (setq's)
)

Code: [Select]
(defun SetVar2Nil ()
(setq
a nil
b nil
c nil
.
.
.
aaa nil
bbb nil
)
)
« Last Edit: August 12, 2011, 01:30:12 AM by jaydee »

BlackBox

  • King Gator
  • Posts: 3770
Re: What is the best way to localise or set variable to NIL
« Reply #1 on: August 12, 2011, 12:50:34 AM »
I'm not sure I understand why you would need to change so many variables to perform a LISP operation, but this should work:

Code: [Select]
(defun subfunc1 ( / A B C)
  (setq A "AAA")
  (setq B "BBB")
  (setq C "CCC")
  ;; ... More
  )
« Last Edit: August 12, 2011, 12:55:58 AM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

jaydee

  • Guest
Re: What is the best way to localise or set variable to NIL
« Reply #2 on: August 12, 2011, 01:29:26 AM »
thankyou Renderman.
I know how to localise variables and those variables  in the subfunctions (only a few) will be passed on to the main functions.

Yes by default i should localise in the main functions (defun c:func1 ( / a b c .........etc)
But my question is there a better way of doing this.

I read up something about foreach member atom and not fully understand about these work and suspected it could be something to do with setting varibles.

Well people reading this post might be curious why i have so many variable.
It in a plot routine i have which i pre-set a varible for each
.pc3
.ctb
paper size
Plot size
etc.

So in my lisp command "-PLOT" will end up with all variablesinstead of "Y" "N" "N" etc.

and for a large office, there will be many combinations.




« Last Edit: August 12, 2011, 01:35:50 AM by jaydee »

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: What is the best way to localise or set variable to NIL
« Reply #3 on: August 12, 2011, 04:00:37 AM »
It's again one of those situations where I'd use a list variable to store all those values. Makes life simpler in the long run.

About the set to nil using a foreach, you're probably referring to the atoms-family list of all variable/defun names. The problem with simply running through that is you can't just go and nil them all - most of them are all the defuns you'd use in other situations (including all the built-in defuns). So you'd need to check their names against a list of names (or perhaps a wildcard).

So you'd either need to use the extract list as strings and then use the read and eval to get to their actual symbols. Or use the vl-symbol-name to extract each to a string for checking.

IMO once you've done all that you've probably done way more typing than simply setting each as localized.

Alternatively you could save a list from atoms-family before setting your variables. Then after completing use the member/vl-position to check which atoms are new in the new call to atoms-family. Though this is going to be quite slow (since there's 100's of atoms already without any extras from your own routines), not to mention you're using quite a lot of RAM to store the names. Much simpler to just "combine" the related settings into a list, you could either then use nth or make your list "readable" and turn it into an association list. E.g:
Code: [Select]
;; Using nth
(defun subfunc1 (/ )
  (setq SettingList
    '("AAA"
      "BBB"
      "CCC"
    )
  )
)
(nth 1 SettingList) ;Returns the 2nd item --> "BBB"

;; Using assoc
(defun subfunc1 (/ )
   (setq SettingList
     '((A . "AAA")
       (C . "CCC")
       (B . "BBB")
     )
   )
)
(cdr (assoc 'C SettingList)) ;Returns the "CCC", list need not be in order
And then to nil them all (if you don't localize the SettingList variable) :
Code: [Select]
(setq SettingList nil)
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

pBe

  • Bull Frog
  • Posts: 402
Re: What is the best way to localise or set variable to NIL
« Reply #4 on: August 12, 2011, 04:11:11 AM »
This what comes in mind.

http://www.theswamp.org/index.php?topic=37689.msg427195#msg427195

The topic there is about finding all floating variables. you can do the next step by assigning nil for every item on that list (result)

BlackBox

  • King Gator
  • Posts: 3770
Re: What is the best way to localise or set variable to NIL
« Reply #5 on: August 12, 2011, 06:28:27 AM »
I had originally posted this, but decided against it as I still feel that you should localize, but FWIW:

Code: [Select]
(vl-load-com)
(foreach var '(A B C)
  (vl-catch-all-apply 'set (list var nil))
)

Edit: Typo! I typed strcat in lieu of set <<Slaps forehead>>
« Last Edit: August 12, 2011, 08:48:30 AM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: What is the best way to localise or set variable to NIL
« Reply #6 on: August 12, 2011, 06:59:44 AM »
RM ... I'm unsure I understand what's supposed to happen there. Aren't you possibly trying the following?
Code: [Select]
(mapcar '(lambda (var) (set var nil)) '(A B C))
The problem with the codes as linked to by pBe ... that's going to remove all global vars which aren't the "built-in" stuff. So anywhere you actually wanted a global (e.g. saving a default value for user entry) this is going to be removed. And if your code expects a global to already exist - there's going to be an error. And then "just-for-fun" if ADesk (ever) decides to "update" A/VLisp the new functions / variables need to be incorporated into your routine.

I'm definitely with you on the score of localizing as much as possible! As for having a long list of vars, I'll reiterate: Use a list variable, you can even then much more easily save that variable to a file for later re-load, thus also allowing to much more easily share the different versions between co-workers. Having several separate vars for such just becomes messy to work with, the code becomes enormous with very little re-usability.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
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.

BlackBox

  • King Gator
  • Posts: 3770
Re: What is the best way to localise or set variable to NIL
« Reply #8 on: August 12, 2011, 08:51:11 AM »
RM ... I'm unsure I understand what's supposed to happen there. Aren't you possibly trying the following?
Code: [Select]
(mapcar '(lambda (var) (set var nil)) '(A B C))

:?... Totally a typo on my part. Code corrected. :sigh:
"How we think determines what we do, and what we do determines what we get."

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: What is the best way to localise or set variable to NIL
« Reply #9 on: August 12, 2011, 10:06:41 AM »
After reading the first post, this comes to mind:

http://www.theswamp.org/index.php?topic=39081.0

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: What is the best way to localise or set variable to NIL
« Reply #10 on: August 12, 2011, 10:32:29 AM »
I'm not seeing any arguments or return values in the original functions posted, which may be part of the problem.  Since all variables won't be needed everywhere, keep the scope of each variable as small as possible.

Generally speaking, if I have more than a dozen or two local variables its either time to use a list or split the function into separate routines.
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

JohnK

  • Administrator
  • Seagull
  • Posts: 10605
Re: What is the best way to localise or set variable to NIL
« Reply #11 on: August 12, 2011, 01:33:35 PM »
Oh, fun.


> creating a list of variable names.

Let's go low tech on this one.
Code: [Select]
gawk -F"setq" "{print $2}" mylispfile.lsp | gawk "{print $1}" >> mylispfile.lsp
or from within Vim.
Code: [Select]
:!gawk -F"setq" "{print $2}" % | gawk "{print $1}" >> %
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

JohnK

  • Administrator
  • Seagull
  • Posts: 10605
Re: What is the best way to localise or set variable to NIL
« Reply #12 on: August 12, 2011, 02:40:24 PM »
BTW, I thought I should add a link to a nice tutorial on AWK.
http://www.theswamp.org/index.php?topic=8476.0
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

jaydee

  • Guest
Re: What is the best way to localise or set variable to NIL
« Reply #13 on: August 12, 2011, 09:56:14 PM »
Thankyou All comments on my post.
theres quite a fair bit of info to absorb.

Another question. Is there any different between.

localise (/ a b c)
and
(setq a nil b nil c nil)

My understanding is that localise will clear the var from memory.
and (setq a nil) is just simply provide a nil value to the symbol, meaning the variable still there but with NO values.


irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: What is the best way to localise or set variable to NIL
« Reply #14 on: August 13, 2011, 04:57:59 AM »
Yes there is a difference. Localizing will "encapsulate" the variable(s) in the scope where it's made. E.g. look at the following:
Code: [Select]
(defun PrintABC (heading / var)
  (princ "\n\n")
  (princ heading)
  (princ "\n------------------------------------------------")
  (foreach var '(a b c d)
    (princ "\n\t")
    (princ var)
    (princ " = ")
    (princ (eval var))
  )
  (princ)
)

(setq a "A global"
      b "B global"
      c "C global"
      d "D global"
)

(defun testlocal (/ b d testlocal-sub)
  (PrintABC "As testlocal starts")
 
  (defun testlocal-sub ( / c d)
    (PrintABC "As testlocal-sub starts")
    (setq a "A in testlocal-sub"
          b "B in testlocal-sub"
          c "C in testlocal-sub"
          d "D in testlocal-sub"
    )
    (PrintABC "As testlocal-sub finishes")
  )
 
  (setq a "A in testlocal"
        b "B in testlocal"
        c "C in testlocal"
        d "D in testlocal"
  )
 
  (PrintABC "Just before testlocal-sub is called")
  (testlocal-sub)
  (PrintABC "As testlocal finishes")
)

(PrintABC "Just before testlocal is called")
(testlocal)
(PrintABC "After testlocal completes")
Running this code you get the following:
Code: [Select]
Just before testlocal is called
------------------------------------------------
   A = A global
   B = B global
   C = C global
   D = D global

As testlocal starts
------------------------------------------------
   A = A global
   B = nil
   C = C global
   D = nil

Just before testlocal-sub is called
------------------------------------------------
   A = A in testlocal
   B = B in testlocal
   C = C in testlocal
   D = D in testlocal

As testlocal-sub starts
------------------------------------------------
   A = A in testlocal
   B = B in testlocal
   C = nil
   D = nil

As testlocal-sub finishes
------------------------------------------------
   A = A in testlocal-sub
   B = B in testlocal-sub
   C = C in testlocal-sub
   D = D in testlocal-sub

As testlocal finishes
------------------------------------------------
   A = A in testlocal-sub
   B = B in testlocal-sub
   C = C in testlocal
   D = D in testlocal

After testlocal completes
------------------------------------------------
   A = A in testlocal-sub
   B = B global
   C = C in testlocal
   D = D global
Firstly, all localized variables are initialized to nil. E.g. in the 2 cases where the defun starts you can see the localized versions contain nil

As you can see the b & d retained their original values at the end since they were localized in the testlocal function. It's as if inside the function there's new versions of b & d, and thus any setq's to them won't affect their previous versions.

Same happens inside the testlocal-sub, where c & d are localized. Only now you see that b gets altered for the localized version inside the testlocal function, thus its value in the "As testlocal finishes" position shows the value as it's been assigned in the testlocal-sub function.

If you simply nil'ed them all the globals would also be nil, as would all the values shown in "As testlocal finishes".

So here you can see the effect of nesting localizations. Sometimes you'd want to edit a value in the surrounding function, in such case you'd not localize the variable in the sub-function (as done with the b variable). Only when you want the value to be retained after all the defuns are complete would you NOT localize - as done with the a variable.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: What is the best way to localise or set variable to NIL
« Reply #15 on: August 13, 2011, 05:05:13 AM »
BTW, I've seen in some other thread that using the foreach "Doesn't require localization ... as the value gets turned to nil when the loop finishes". See my PrintABC function in the previous post. IMO it still requires localization since it could cause clashes with surrounding (also calling) functions. Say the var variable had a global value, at the end this value is still retained. But if I didn't localize it the value would be nil.

Also note, lisp has what's called "closures". A simple explanation is that you would get the same effect if the testlocal-sub was defined outside of the testlocal. I.e. the localizations apply to "when the function is called" not "where it is made".
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: What is the best way to localise or set variable to NIL
« Reply #16 on: August 13, 2011, 05:11:27 AM »
And to answer the last portion of your post: You're close to correct. (setq a nil) doesn't clear the variable from RAM ... immediately. Lisp takes what's known as garbage collection: whenever there's some idle time it runs a (gc) which checks through all the atoms-family list and removes all atoms = nil from RAM.

With localized it gets removed from ram when the defun completes.

There's no functional difference in this case, since if you test a variable which has not been setq'ed (i.e. it's not in RAM), its value would be nil in any case.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: What is the best way to localise or set variable to NIL
« Reply #17 on: August 13, 2011, 08:20:14 AM »
Nice explanation Irneb, this is another good read.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: What is the best way to localise or set variable to NIL
« Reply #18 on: August 13, 2011, 10:05:48 AM »
Thanks. I was thinking of explaining the "true" ins-and-outs about how Lisp handles localized variables ... but no-one actually needs to know that all variables are in fact lists (used as stacks) and each time a variable is localized again it gets another cons on top of that list, then a cdr after the defun completes.

What it does tell me however is there should be a way to modify the golbal version of a variable even if it has a localized symbol name. Though I don't see any way of doing so in AutoLisp ... unless you pass it as a quoted argument and then use set instead of setq on it. But that kind of defeats the "purpose" ... I'd have like to be able to do something like this:
Code: [Select]
(setq var "Global")
(defun test (/ var)
  (setq var "Local")
  (print var)
  (print (cdr var))
  (setq (cdr var) "Global changed in defun")
)
(print var)
(test)
(print var)
As you can already see the cdr would cause an error, since the internal workings of the variable's allocation isn't available to the lisp program. But I'd have liked to see an output from that like the following:
Code: [Select]
"Global"
"Local"
"Global changed in defun"
"Global changed in defun"
That way you could have the best of both worlds ... allow assignment to globals even if a local variable has the same name.

Edit: Anyhow, to get around this scenario - it shows one of the reasons for using the asterisk prefix/suffix for global variable names. Makes for the possibility of a clash with a local var's name impossible.
« Last Edit: August 13, 2011, 10:19:38 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

jaydee

  • Guest
Re: What is the best way to localise or set variable to NIL
« Reply #19 on: August 13, 2011, 01:50:59 PM »
Thankyou irneb
I alway been very confused about localising vars especially it involve multiple subfunctions that require to pass the vars from one subfunction to another or main function.

Now your explanation is clear as mud

Very appriciated for you to take time to write and formated it in a very easy to understand.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: What is the best way to localise or set variable to NIL
« Reply #20 on: August 13, 2011, 02:00:38 PM »
You're welcome  :kewl: . Lisp is a bit "strange" when it comes to variables ... at least in comparison to most other programming languages. It's not easy to understand how it works the first time round. We all get better as our experience grows, I guess. Happy lisping!
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: What is the best way to localise or set variable to NIL
« Reply #21 on: August 13, 2011, 02:55:35 PM »
Say the var variable had a global value, at the end this value is still retained. But if I didn't localize it the value would be nil.

Not quite - the symbol argument passed to foreach (in this case 'var')  is local to that expression:

Code: [Select]
(setq var "Global")

(defun test ( )
  (foreach var '(1 2 3 4 5) (print var))
)
(print var)
(test)
(print var)
Code: [Select]
"Global"
1
2
3
4
5
"Global"

Also:

Code: [Select]
(setq var "Global")

(defun test ( / var )
  (setq var "Local")
  (foreach var '(1 2 3 4 5) (print var))
  (print var)
)
(print var)
(test)
(print var)
Code: [Select]
"Global"
1
2
3
4
5
"Local"
"Global"

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: What is the best way to localise or set variable to NIL
« Reply #22 on: August 13, 2011, 03:52:49 PM »
Thanks! I didn't know that. So the foreach automatically localizes the incremental variable. Awesome!

So it works much the same as the let construct in Common Lisp?
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Lee Mac

  • Seagull
  • Posts: 12906
  • London, England
Re: What is the best way to localise or set variable to NIL
« Reply #23 on: August 13, 2011, 04:43:24 PM »
Yes, the variable is local to the function, just as the bindings are local to the 'let' body - the same is true of vlax-for.

There are a few of these floating around but here's a possible 'let' function for AutoLISP:

Code: [Select]
(defun let ( bindings body )
  (apply (list 'lambda (mapcar 'car bindings) body) (mapcar 'eval (mapcar 'cadr bindings)))
)