Visual LISP, AutoLISP and General Customization

Visual LISP, AutoLISP and General Customization

Reply
Mentor
mid-awe
Posts: 1,230
Registered: ‎12-08-2004
Message 1 of 18 (358 Views)
Accepted Solution

Assign variable if item is a member of a list?

358 Views, 17 Replies
01-17-2013 01:41 PM

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 :smileywink:

 

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.

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

*Expert Elite*
Kent1Cooper
Posts: 5,779
Registered: ‎09-13-2004
Message 2 of 18 (355 Views)

Re: Assign variable if item is a member of a list?

01-17-2013 02:12 PM 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
*Expert Elite*
pbejse
Posts: 2,500
Registered: ‎11-24-2009
Message 3 of 18 (342 Views)

Re: Assign variable if item is a member of a list?

01-17-2013 09:20 PM in reply to: Kent1Cooper

Another:

 

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

 

HTH

 

Valued Mentor
martti.halminen
Posts: 344
Registered: ‎12-31-2009
Message 4 of 18 (333 Views)

Re: Assign variable if item is a member of a list?

01-18-2013 12:37 AM in reply to: Kent1Cooper

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)))

 --

   

*Expert Elite*
pbejse
Posts: 2,500
Registered: ‎11-24-2009
Message 5 of 18 (327 Views)

Re: Assign variable if item is a member of a list?

01-18-2013 01:28 AM 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
           )
      )

 

*Expert Elite*
Kent1Cooper
Posts: 5,779
Registered: ‎09-13-2004
Message 6 of 18 (313 Views)

Re: Assign variable if item is a member of a list?

01-18-2013 06:21 AM in reply to: martti.halminen

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
Mentor
mid-awe
Posts: 1,230
Registered: ‎12-08-2004
Message 7 of 18 (307 Views)

Re: Assign variable if item is a member of a list?

01-18-2013 08:13 AM 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 :smileyindifferent:

 

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 :smileyhappy:

Mentor
mid-awe
Posts: 1,230
Registered: ‎12-08-2004
Message 8 of 18 (293 Views)

Re: Assign variable if item is a member of a list?

01-18-2013 12:18 PM 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.

*Expert Elite*
Kent1Cooper
Posts: 5,779
Registered: ‎09-13-2004
Message 9 of 18 (281 Views)

Re: Assign variable if item is a member of a list?

01-18-2013 02:13 PM 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
Mentor
mid-awe
Posts: 1,230
Registered: ‎12-08-2004
Message 10 of 18 (276 Views)

Re: Assign variable if item is a member of a list?

01-18-2013 03:09 PM 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. 

Post to the Community

Have questions about Autodesk products? Ask the community.

New Post
Announcements
Are You Going To Be @ AU 2014? Feel free to drop by our AU topic post and share your plans, plug a class that you're teaching, or simply check out who else from the community might be in attendance. Ohh and don't forgot to stop by the Autodesk Help | Learn | Collaborate booths in the Exhibit Hall and meet our community team if you get a chance!