Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Assign variable if item is a member of a list?

17 REPLIES 17
SOLVED
Reply
Message 1 of 18
mid-awe
1034 Views, 17 Replies

Assign variable if item is a member of a list?

Hi all,

 

I have a big ugly function that I want to make smaller and faster. Currently I have some rather large CONDs in my lisp that I know I can do away with if I can figure out how to check the "item" in my FOREACH to see which list it is a member of. At this point I have 3 lists but I know I'll have more with at least 20 items in each. Once I determine which list the item is in, I'll need to assign a name (the name of the list) to a variable for use later in a princ. I appreciate any suggestions, Thank you in advance.

 

Let me know if seeing the code is necessary because it is big and ugly Smiley Wink

 

BTW- I was looking at the member function, but if I understand correctly it only returns the remainder of the list which is not what I am looking for I believe.

17 REPLIES 17
Message 2 of 18
Kent1Cooper
in reply to: mid-awe


@mid-awe wrote:

....  I have some rather large CONDs in my lisp that I know I can do away with if I can figure out how to check the "item" in my FOREACH to see which list it is a member of. At this point I have 3 lists but I know I'll have more with at least 20 items in each. Once I determine which list the item is in, I'll need to assign a name (the name of the list) to a variable for use later in a princ. ... 

BTW- I was looking at the member function, but if I understand correctly it only returns the remainder of the list which is not what I am looking for I believe.


You still want the (member) function, which will return nil if the item isn't in the list.  If it returns anything other than nil, that will satisfy an (if) or (cond) test.  You should be able to do something like this:

 

(setq VariableForUseLater

  (cond

    ((member YourItem ListA) ListA)

    ((member YourItem ListB) ListB)

    ((member YourItem ListC) ListC)

  ); cond

); setq

 

And there are certainly some other ways to go about it.

 

EDIT:  If you don't want to spell out a line like that for each List, given that there might not always be the same number of them, you can do something like put all those Lists into a combined Master List of Lists, and do something like this:

 

(setq MasterList (list ListA ListB ListC [.... etc. somehow])) 

(foreach List MasterList

  (if (member YourItem List)

    (setq VariableForUseLater List)

  ); if

); foreach

 

If an item is in more than one List, that latter would save the name of the last one it finds it in, whereas the (cond) approach above would save the name of the first one it finds it in.  You could get fancier and have a non-(cond) version look until it finds the item in some List, and stop there, using some kind of (while (not found) ... approach, if that's appropriate.

Kent Cooper, AIA
Message 3 of 18
pbejse
in reply to: Kent1Cooper

Another:

 

(setq VariableForUseLater
     (vl-some '(lambda (x)
                  (if (member YourItem x) x)
                  )
           (list ListA ListB ListC)
           )
      )

 

HTH

 

Message 4 of 18


@Kent1Cooper wrote:

@mid-awe wrote:

....  I have some rather large CONDs in my lisp that I know I can do away with if I can figure out how to check the "item" in my FOREACH to see which list it is a member of. At this point I have 3 lists but I know I'll have more with at least 20 items in each. Once I determine which list the item is in, I'll need to assign a name (the name of the list) to a variable for use later in a princ. ... 

BTW- I was looking at the member function, but if I understand correctly it only returns the remainder of the list which is not what I am looking for I believe.

...

 

(setq MasterList (list ListA ListB ListC [.... etc. somehow])) 

(foreach List MasterList

  (if (member YourItem List)

    (setq VariableForUseLater List)

  ); if

); foreach

 


 

A few problems with this:

- This would produce a list where YourItem occurs, not the symbol naming the variable where the list is assigned like the original poster asked
- there is a name collision: you are using "list" as a variable, but using that would break the built-in function LIST.
This would work in Common Lisp, where there is a separate namespace for functions and values, but AutoLISP is like Scheme where the values and functions are all in the same namespace.
http://www.nhplace.com/kent/Papers/Technical-Issues.html
 
So, something like this should work better (untested, of course...)
(setq MasterList (list 'ListA 'ListB 'ListC))

(foreach listname MasterList
  (if (member YourItem (vl-symbol-value Listname))
      (setq VariableForUseLater Listname)))

 --

   

Message 5 of 18
pbejse
in reply to: martti.halminen


@martti.halminen wrote:
- This would produce a list where YourItem occurs, not the symbol naming the variable where the list is assigned like the original poster asked

in that case

 

(setq VariableForUseLater
     (vl-some '(lambda (x)
                  (if (member YourItem (eval x)) x)
                  )
           MasterList
           )
      )

 

Message 6 of 18


@mid-awe wrote:
.... Once I determine which list the item is in, I'll need to assign a name (the name of the list) to a variable for use later in a princ. ....
@martti.halminen wrote:
.... 

A few problems with this:

- This would produce a list where YourItem occurs, not the symbol naming the variable where the list is assigned like the original poster asked
- there is a name collision: you are using "list" as a variable, but using that would break the built-in function LIST.
....

You're correct about the first problem, though I'm not sure that what I called VariableForUseLater [of course they would give it a different name] wouldn't serve just as well, functionally speaking, as the original variable name, since either would call up exactly the same list, so items could be extracted out of it the same way, or other functions used on it, with the same results.  But I don't know enough about how they plan to use it to say.

 

But you're not correct about the second problem, though I grant that it might be better not to use that word as an argument, just to avoid any possible confusion.  "List" as used there is not a variable in the usual sense [there is no (setq List ...) happening], but rather a place-holder argument inside the (foreach) function.  Yes, the AutoLISP Reference describes it as assigning each item to a variable named for the 'name' argument, but it's not in the same way as (setq) does it.  Think of it as being like a localized variable in a (defun)'d routine or command, in that it has no meaning, nor any effect on anything else, outside the (foreach) function in which it is used.  It does work.  If I do this:

 

(setq ListA '(1 2 3) ListB '(4 5 6) ListC '(7 8 9))

 

and then run that routine, removing the [...etc....] part from the top line, and using [for example] 6 for YourItem, it puts into VariableForUseLater the correct list:

 

!VariableForUseLater

(4 5 6)

 

And similarly finds the correct list with other YourItem values.  And it has no effect on the (list) function, which continues to function normally afterwards.

Kent Cooper, AIA
Message 7 of 18
mid-awe
in reply to: mid-awe

Either I am not understanding how to implement the solutions or I did not explain my challenge clearly enough previously. If I am just a fool, then please excuse my foolishness Smiley Indifferent

 

I took some time to abreviate my code so that it can be clearly seen what I am doing now that works:

 

(DEFUN c:AllPlantsInDictionary (/ item PALM TREE SHRUB)
  (setq	PALM  (list "Bismark"	 "Date"	      "Fan"	   "Pineapple"
		    "Pygmy"	 "Queen"      "Sago"	   "Windmill"
		    "Ponytail"	 "Blue Fan"   "Pindo"	   "Sabal"
		   )
	TREE  (list "Wattle"	 "Ash"	      "Bottle"	   "Blue"
		    "Pepper"	 "Cascalote"  "Chaste"	   "Mesquite"
		    "Elm"	 "Pistache"   "Ficus"	   "Coolibah"
		    ...etc...
		   )
	SHRUB (list "Daisy"	  "Jasmine"	"Sage"
		    "Fern"	  "Baja"	"Ruellia"
		    "Bamboo"	  "Bougainvillea"
		    ...etc...
		   )
	num   (length SHRUB)
	num   (+ num (length TREE))
	num   (+ num (length SHRUB))
  )
  (IF (TBLSEARCH "BLOCK" "Palms_DYN")
    (PROGN
      (foreach item PALM
	(COND
	  ((= item "Bismark") (princ (strcat "\n Bismark : " (rtos (dict-get "PALM" "Bismark") 2 0))))
	  ((= item "Date") (princ (strcat "\n Date : " (rtos (dict-get "PALM" "Date") 2 0))))
	  ((= item "Fan") (princ (strcat "\n Fan : " (rtos (dict-get "PALM" "Fan") 2 0))))
	  ...etc...
	)
      )
    )
  )
  (IF (TBLSEARCH "BLOCK" "TREES_DYN")
    (PROGN
      (foreach item TREE
	(COND
	  ((= item "Wattle") (princ (strcat "\n Wattle : " (rtos (dict-get "TREE" "Wattle") 2 0))))
	  ((= item "Ash") (princ (strcat "\n Ash : " (rtos (dict-get "TREE" "Ash") 2 0))))
	  ((= item "Bottle") (princ (strcat "\n Bottle : " (rtos (dict-get "TREE" "Bottle") 2 0))))
	  ...etc...
	)
      )
    )
  )
  (IF (TBLSEARCH "BLOCK" "SHRUB_DYN")
    (PROGN
      (foreach item SHRUB
	(COND
	  ((= item "Daisy") (princ (strcat "\n Daisy : " (rtos (dict-get "SHRUB" "Daisy") 2 0))))
	  ((= item "Jasmine") (princ (strcat "\n Jasmine : " (rtos (dict-get "SHRUB" "Jasmine") 2 0))))
	  ((= item "Sage") (princ (strcat "\n Sage : " (rtos (dict-get "SHRUB" "Sage") 2 0))))
	  ...etc...
	)
      )
    )
  )
  (princ)
)

 As I tried each of the solutions (each are greatly appreciated) I continued getting - nil; or error: bad argument type: stringp nil

 

I'm sure the stringp nil is because I had to use the list name as the variable and unless I put the princ in the foreach loop it would fail.

 

Thank you all for your help, I always learn more than I'm looking for when I look for help here Smiley Happy

Message 8 of 18
mid-awe
in reply to: Kent1Cooper

I can understand your first solution best so I've given it another try. Now with the code rewritten to include your condition it does not work. Please if you can, I need some help understanding what I've done wrong.

 

(DEFUN c:AllPlantsInDictionary (/ num cnt CAT PALM TREE SHRUB PLANT PLANTS)
  (setq	PALM  (list "Bismark"	 "Date"	      "Fan"	   "Pineapple"
		    "Pygmy"	 "Queen"      "Sago"	   "Windmill"
		    "Ponytail"	 "Blue Fan"   "Pindo"	   "Sabal"
		   )
	TREE  (list "Wattle"	 "Ash"	      "Bottle"	   "Blue"
		    "Pepper"	 "Cascalote"  "Chaste"	   "Mesquite"
		    "Elm"	 "Pistache"   "Ficus"	   "Coolibah"
		    ...etc...
		   )
	SHRUB (list "Daisy"	  "Jasmine"	"Sage"
		    "Fern"	  "Baja"	"Ruellia"
		    "Bamboo"	  "Bougainvillea"
		    ...etc...
		   )
PLANTS (list 'PALM 'TREE 'SHRUB) num (length SHRUB) num (+ num (length TREE)) num (+ num (length SHRUB)) ) (princ (strcat "\n\t -> Total: " (rtos num 2 0) " \n")) (while (< cnt num) (foreach PLANT PLANTS (setq CAT (cond ((foreach PLANT PALM (member PLANT PALM)) "PALM") ((foreach PLANT TREE (member PLANT TREE)) "TREE") ((foreach PLANT SHRUB (member PLANT SHRUB)) "SHRUB") ) ) (if (>= (dict-get CAT PLANT) 1) (princ (strcat "\n " PLANT " : " (rtos (dict-get CAT PLANT) 2 0)) ) ) (setq cnt (1+ cnt)) ) ) (princ) )

 The out put I get is: "; error: bad argument type: stringp PALM" and that's all. What am I doing wrong here? I can't see it.

 

Thank you for any help of advice you can give.

Message 9 of 18
Kent1Cooper
in reply to: mid-awe

I don't see (dict-get) defined, and it's not a native AutoLISP function, so I'm not sure exactly what it's doing.

 

As an aside, the (+) function can take more than two arguments, so you can replace:

 

    num   (length SHRUB)
    num   (+ num (length TREE))
    num   (+ num (length SHRUB))

 

with just:

 

    num (+ (length SHRUB) (length TREE) (length SHRUB))

 

[but shouldn't one of those be PALM instead?].

 

If the intent of setq-ing

    PLANTS (list 'PALM 'TREE 'SHRUB)

is, as I assume from the way it is used later, to make one combined list of all the plant names, then I think what you really want is:
    PLANTS (append PALM TREE SHRUB)

 

And if that's what you really want, then you could just setq

    num (length PLANTS)

instead of adding up the lengths of the three separate lists.

 

[I'm headed out the door -- I'll look again later, I hope after you've replied about (dict-get).]

Kent Cooper, AIA
Message 10 of 18
mid-awe
in reply to: Kent1Cooper

Thank you for the reply. You pointed out a few things that should've been obvious and you were completely correct. 

 

I made the changes that you pointed out, and while I'm not getting correct output, I am at least not getting errors.

 

(DEFUN c:AllPlantsInDictionary (/ num cnt CAT PALM TREE SHRUB PLANT PLANTS)
  (setq	PALM  (list "Bismark"	 "Date"	      "Fan"	   "Pineapple"
		    "Pygmy"	 "Queen"      "Sago"	   "Windmill"
		    "Ponytail"	 "Blue Fan"   "Pindo"	   "Sabal"
		   )
	TREE  (list "Wattle"	 "Ash"	      "Bottle"	   "Blue"
		    "Pepper"	 "Cascalote"  "Chaste"	   "Mesquite"
		    "Elm"	 "Pistache"   "Ficus"	   "Coolibah"
		    ...etc...
		   )
	SHRUB (list "Daisy"	  "Jasmine"	"Sage"
		    "Fern"	  "Baja"	"Ruellia"
		    "Bamboo"	  "Bougainvillea"
		    ...etc...
		   )
        PLANTS (append PALM TREE SHRUB)
	num (length PLANTS)
	cnt    0
  )
  (princ (strcat "\n\t -> Total: " (rtos num 2 0) " \n"))
  (while (< cnt num)
    (foreach PLANT PLANTS
      (setq CAT
	     (cond
	       ((foreach PLANT PALM (member PLANT PALM)) "PALM")
	       ((foreach PLANT TREE (member PLANT TREE)) "TREE")
	       ((foreach PLANT SHRUB (member PLANT SHRUB)) "SHRUB")
	     )
      )	
      (if (>= (dict-get CAT PLANT) 1)
	(princ
	  (strcat "\n " PLANT " : " (rtos (dict-get CAT PLANT) 2 0))
	)
      )
      (setq cnt (1+ cnt))
    )
  )
  (princ)
)

 It seams to be cycling through the PALM list and then giving me a strange return that makes no sence. I only have 90 plants in my test file but it's returning 101 for my total.

 

- I got the Dict-Put-Get.lsp from James Maeding who I've not seen around here in a long while, but the thread on this forum is here: Store variable information in dwg? it basically saves and retrieve lists to and from the dictionary with "dictadd". The format is easy to use: 

 

(dict-put "mydict" "mykey" "whatever")
(dict-get "mydict" "mykey")

I have a seperate function that counts my plants and stores the number in the dictionary with dict-put. The function I'm trying to improve reads the value for each plant from the dictionary and prints it to the screen if at least a value of 1 exists, since 0 is also possible.

 

I would eventually like to do both in the same function.

 

Thank you greatly for your help. 

Message 11 of 18
Kent1Cooper
in reply to: mid-awe

Without loading it up and trying it, I suspect what you need to do is to change this section:

....

     (cond
       ((foreach PLANT PALM (member PLANT PALM)) "PALM")
       ((foreach PLANT TREE (member PLANT TREE)) "TREE")
       ((foreach PLANT SHRUB (member PLANT SHRUB)) "SHRUB")
     )

....

to just this:

....

     (cond
       ((member PLANT PALM) "PALM")
       ((member PLANT TREE) "TREE")
       ((member PLANT SHRUB) "SHRUB")
     )

....

Kent Cooper, AIA
Message 12 of 18
pbejse
in reply to: mid-awe

So you basically want to  process every item on the list if and only if the block associated with the list exists on the drawing session

 

Here's a demo: You may need to add your list before you try this code :

 

(Defun c:demo ( / MasterList Palm Tree shrub dictname)    
(vl-load-com)
;;;	This section will create a master list filtering out items  	;;;
;;;	if the block does not exist in the drawing session		;;;
(if  (setq MasterList (mapcar 'cadr
	(vl-remove-if-not
	  	'(lambda (i)(tblsearch "block" (car i)))
	        '(("Palms_DYN" PALM) ("Tree_DYN"  TREE)("SHRUB_DYN" SHRUB)))))
;;;	This section will iterate thru the remaining list variables	;;;
  
  	(foreach lst MasterList
          	(setq dictname (vl-symbol-name lst))
          	(foreach key (eval lst)
                  	(princ (strcat "\n" key ": " (rtos (dict-get dictname key) 2 0)))
                  )
          )
  (princ "\nBlocks Not found")
  )
  (princ)
  )

 

HTH

Message 13 of 18


@Kent1Cooper wrote:

 

...

 

But you're not correct about the second problem, though I grant that it might be better not to use that word as an argument, just to avoid any possible confusion.  "List" as used there is not a variable in the usual sense [there is no (setq List ...) happening], but rather a place-holder argument inside the (foreach) function.  Yes, the AutoLISP Reference describes it as assigning each item to a variable named for the 'name' argument, but it's not in the same way as (setq) does it.  Think of it as being like a localized variable in a (defun)'d routine or command, in that it has no meaning, nor any effect on anything else, outside the (foreach) function in which it is used.  It does work.  If I do this:

 

(setq ListA '(1 2 3) ListB '(4 5 6) ListC '(7 8 9))

 

and then run that routine, removing the [...etc....] part from the top line, and using [for example] 6 for YourItem, it puts into VariableForUseLater the correct list:

 

!VariableForUseLater

(4 5 6)

 

And similarly finds the correct list with other YourItem values.  And it has no effect on the (list) function, which continues to function normally afterwards.


I don't know the compiler internals, but running the interpreter in the VLIDE Console window I seem to be getting
different results:

 

$ (setq lista '(1 2 3) listb '(4 5 6) listc '(7 8 9))
(7 8 9)
_$ (setq MasterList (list ListA ListB ListC))
((1 2 3) (4 5 6) (7 8 9))
_$ (foreach List MasterList

  (if (member YourItem List)

    (setq VariableForUseLater List)

  ); if

); foreach
; User warning: assignment to protected symbol: LIST <- (1 2 3)
_1_$

 

...............
LOG Trace stack
...............
<1> :PROTECT-ASSIGN LIST := (1 2 3)
[2] (FOREACH ...)
@[3] (#<USUBR @2c4512d0 -top->)
<4> :USER-INPUT (foreach List MasterList
    (if (member YourItem List)
        (setq VariableForUseLater List)))
<5> :CALLBACK-ENTRY
[6] (C:VLIDE)
<7> :CALLBACK-ENTRY

 

 

- On the other hand, the assignment disappears outside the FOREACH call, so this would only produce an error if I tried to call the LIST function inside the FOREACH loop:

 

_$ (foreach list '((1)(2)) (print (list list)))
; User warning: assignment to protected symbol: LIST <- (1)
_1_$
; continue after break loop
; error: bad function: (1)

_1$ list
(1)
_1$
; reset after error
_$ list
@#<SUBR @28058e08 LIST>

--

Message 14 of 18


@martti.halminen wrote:
.... 

- On the other hand, the assignment disappears outside the FOREACH call, so this would only produce an error if I tried to call the LIST function inside the FOREACH loop:

....


It did occur to me later that there could be a problem in that circumstance, so your suggestion not to use that as an argument name is certainly worthwhile.

Kent Cooper, AIA
Message 15 of 18
mid-awe
in reply to: pbejse

Thank you all for your help. Maybe this should not have been so difficult but it was. I really appreciate everyones help and now this is working exactly as I need it to. A great solution here saving me hundreds of lines of code. Again, thank you all. Smiley Very Happy

Message 16 of 18
mid-awe
in reply to: pbejse

I have one more quick question.

 

I tried to use :

 

(foreach PLANT (vl-sort PLANTS '(lambda (x y) (< (car x) (car y))))
  (setq CATEGORY (vl-symbol-name PLANT))
  (foreach PLANTNAME (eval PLANT)
    (if	(>= (dict-get CATEGORY PLANTNAME) 1)
      (princ (strcat "\n\t "
		     PLANTNAME
		     " : "
		     (rtos (dict-get CATEGORY PLANTNAME) 2 0)
	     )
      )
    )
    (setq total (+ total (dict-get CATEGORY PLANTNAME)))
  )
)

 To get the list alphabetized, but it didn't work. How can I do it correctly?

 

Thank you.

Message 17 of 18
pbejse
in reply to: mid-awe


@mid-awe wrote:

I have one more quick question.

 

 To get the list alphabetized, but it didn't work. How can I do it correctly?

 

Thank you.


Since  the plant lists are hard-coded, you can sort the lists PALM/TREE/SHRUBS then paste the sorted list in your code:

 

(acad_strlsort palm)

("Bismark" "Blue Fan" "Date" "Fan" "Pindo" "Pineapple" "Ponytail" "Pygmy" "Queen" "Sabal" "Sago" "Windmill")

(acad_strlsort shrub)

(acad_strlsort tree)

 

That way you dont have to sort the list everytime you use your routine

 

HTH

 

Cheers

 

Message 18 of 18
mid-awe
in reply to: pbejse

Thank you. I had a crazy idea about haveing the entire list sorted, but then I realized it makes little sense because they are categorized Smiley LOL

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost