Author Topic: Foreah or ...  (Read 1943 times)

0 Members and 1 Guest are viewing this topic.

velasquez

  • Newt
  • Posts: 195
Foreah or ...
« on: August 21, 2012, 05:30:51 PM »
Hi
If I need only provide the elements of a list to a function?
What is the best option (foreach. .. (mapcar or ...?

Thanks
velasquez

Lee Mac

  • Seagull
  • Posts: 12913
  • London, England
Re: Foreah or ...
« Reply #1 on: August 21, 2012, 05:45:27 PM »
Very generally:

single list + not using return of function evaluation = foreach

multiple lists or using return of function evaluation = mapcar

BlackBox

  • King Gator
  • Posts: 3770
Re: Foreah or ...
« Reply #2 on: August 21, 2012, 06:40:46 PM »
Very succinct, Lee... Great answer. :beer:
"How we think determines what we do, and what we do determines what we get."

Lee Mac

  • Seagull
  • Posts: 12913
  • London, England
Re: Foreah or ...
« Reply #3 on: August 21, 2012, 06:46:33 PM »
Cheers dude  8-)

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: Foreah or ...
« Reply #4 on: August 21, 2012, 07:00:33 PM »
If list order is important (front-to-tail -> front-to-tail), or early exit required = (while ...).

(foreach...) works the list front-to-tail but if a (cons...) is involved in building another list from the first the order can get out sync; if multiple lists are getting built/iterated I find trying to keep the order straight (reverse needed here?  or did the other loop reverse it first?) annoying enough to dictate the same implementation in every loop thats built.
If you are going to fly by the seat of your pants, expect friction burns.

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

chlh_jd

  • Guest
Re: Foreah or ...
« Reply #5 on: August 22, 2012, 01:43:52 AM »
Foreach and mapcar in single list will run same routine , for 2 or multi-list will deffrent in their Variables.
E.G.  Lista = (a1 a2 a3 ... an) , Listb = (b1 b2 b3 ... bm) , suppose n > m :
(foreach a lista (foreach b listb ...))  the following routine will use all ((a1 b1) (a1 b2) ... (a1 bm) ... (an b1) (an b2) ... (an bm));
(mapcar (function (lambda (a b) ...)) Lista Listb) , only run  ((a1 b1) (a2 b2) ... (am bm)) .

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Foreah or ...
« Reply #6 on: August 22, 2012, 04:00:04 AM »
E.g.:
Code - Auto/Visual Lisp: [Select]
  1. ;; Adding 2 lists together
  2. (setq answer nil
  3.       numbers1 '(1 2 3 4)
  4.       numbers2 '(5 6 7
  5.       index (min (length numbers1) (length numbers2)))
  6. (while (>= (setq index (1- index)) 0)
  7.   (setq total (+ (nth index numbers1) (nth index numbers2))))
  8. answer ;Return (6 8 10 12)
  9.  
  10. ;; In this case mapcar works a lot simpler
  11. (setq numbers1 '(1 2 3 4)
  12.       numbers2 '(5 6 7)
  13. (mapcar '+ numbers1 numbers2) ;Returns (6 8 10 12)
Doing the same with foreach makes for a lot more coding (actually it's nearly impossible to add 2 lists together by only using foreach), since foreach only works from one single list (not 2 or more like mapcar). And it only returns the last calculation's result, so you'd need a temporary variable like the answer in the while loop. But also as dgorsman's indicated foreach starts at the begining of the list - so you might have to reverse the answer at the end.

E.g. here's a scenario where mapcar and foreach can equally be applied. Say you want to square each item in the list:
Code - Auto/Visual Lisp: [Select]
  1. (defun sqr (num) (* num num))
  2.  
  3. ;; Standard while loop
  4. (setq answer nil
  5.       numbers '(1 2 3 4)
  6.       index (length numbers))
  7. (while (>= (setq index (1- index)) 0)
  8.   (setq answer (cons (sqr (nth index numbers)) answer)))
  9. answer ;Returns (1 4 9 16)
  10.  
  11. ;; Using foreach instead
  12. (setq answer nil
  13.       numbers '(1 2 3 4))
  14. (foreach num numbers
  15.   (setq answer (cons (sqr num) answer)))
  16. (reverse answer) ;Since answer is built back-to-front using cons, the reverse returns (1 4 9 16)
  17.  
  18. ;; But see how much simpler mapcar is
  19. (setq numbers '(1 2 3 4))
  20. (mapcar 'sqr numbers) ;Returns (1 4 9 16)

There's another possibility (we'd need some more info as to what the OP is after), apply can also be used on a list. If there's only one list, then foreach could also be used. E.g. summing the values of a list together:
Code - Auto/Visual Lisp: [Select]
  1. ;; Instead of this
  2. (setq total 0
  3.       numbers '(1 2 3 4)
  4.       index (length numbers))
  5. (while (>= (setq index (1- index)) 0)
  6.   (setq total (+ total (nth index numbers))))
  7. total ;Return 10
  8.  
  9. ;;You could use this
  10. (setq total 0
  11.       numbers '(1 2 3 4))
  12. (mapcar '(lambda (num)
  13.             (setq total (+ total num)))
  14.         numbers) ;This actually returns (1 3 6 10)
  15. total ;Return 10
  16.  
  17. ;; But probably this seems more conducive to this scenario
  18. (setq total 0
  19.       numbers '(1 2 3 4))
  20. (foreach num numbers
  21.   (setq total (+ total num)))
  22. total ;Return 10
But if the function you want to apply onto all the items in the list can accept more than one argument, you can use apply
Code - Auto/Visual Lisp: [Select]
  1. ;; The total can be done like this of course
  2. (+ 1 2 3 4)
  3. ;; But consider you don't know the list of arguments' contents and/or length at the time you write your code
  4. (setq numbers '(1 2 3 4))
  5. (apply '+ numbers)
The apply works as if you send the 2nd argument (the list) as parameters of the 1st argument (the function).

So the 3 alternatives each have their own scenarios where they become handy. Unfortunately AutoLisp does not have the &rest modifier for arguments - otherwise you could have made your own defun to use with the apply function. So unlike in Common Lisp the apply is less useful than it may have been - if the function is more involved than one of the built-in multi-argument functions, then you're probably better off using a foreach.

I've also tried to show you variations where either (or even any of the 3) can be used to get the exact same result. When such happens then it's up to you which you prefer using. It's a bit subjective, since the one might be more readable to you - while to others they might feel it's more succinct to go a different route. There's no one single answer which covers all possibilities, e.g. with squaring it seemed as if mapcar is nicer to use, but for total the foreach was less convoluted than mapcar (ignoring apply in this scenario to make a point  ;) ).

One thing I can say, mapcar calls a function (which has a small effect on performance - especially with lambda functions), foreach inlines the code which usually then executes faster. Also compiling your lisp to FAS/VLX tends to optimize foreach a bit better than mapcar, this can be alleviated by setting lambda's to be seen as normal functions (see the function keyword). Though if apply can be used, I'd say rather go with it - it's usually much faster than either foreach/mapcar - compiled or not; and IMO it's more readable; and definitely more succinct (read less typing + less chance for typos).
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

velasquez

  • Newt
  • Posts: 195
Re: Foreah or ...
« Reply #7 on: August 22, 2012, 10:07:35 AM »
Thank you for all the answers.
Look below the function that took me to this question.
velasquez

Code - Auto/Visual Lisp: [Select]
  1. (defun MyFunction (/)
  2. ;;;Trata os PictureBox
  3.   (foreach JoyItem
  4.            (list (list DuctilCAD_DuctilCAD_MainForm_PictureBox1 '(1 83 122 120) "Item-1" 149)
  5.                  (list DuctilCAD_DuctilCAD_MainForm_PictureBox2 '(123 83 122 120) "Item-2" 150)
  6.                  (list DuctilCAD_DuctilCAD_MainForm_PictureBox3 '(245 83 122 120) "Item-3" 151)
  7.                  (list DuctilCAD_DuctilCAD_MainForm_PictureBox4 '(367 83 122 120) "Item-4" 152)
  8.                  (list DuctilCAD_DuctilCAD_MainForm_PictureBox5 '(1 203 122 120) "Item-5" 153)
  9.            ) ;_ fim de list
  10.     (dcl_Control_SetPos
  11.       (car JoyItem)
  12.       (nth 0 (cadr JoyItem))
  13.       (nth 1 (cadr JoyItem))
  14.       (nth 2 (cadr JoyItem))
  15.       (nth 3 (cadr JoyItem))
  16.     ) ;_ fim de dcl_Control_SetPos
  17.     (dcl_Control_SetPicture
  18.       (car JoyItem)
  19.       (nth 3 JoyItem)
  20.     ) ;_ fim de dcl_Control_SetPicture
  21.     (dcl_Control_SetToolTipMainText
  22.       (car JoyItem)
  23.       (nth 2 JoyItem)
  24.     ) ;_ fim de dcl_Control_SetToolTipTitle
  25. ;;;Liga os PictureBox da lista
  26.     (dcl_Control_SetVisible
  27.       (car JoyItem)
  28.       T
  29.     ) ;_ fim de dcl_Control_SetVisible
  30.   ) ;_ fim de foreach
  31.  

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Foreah or ...
« Reply #8 on: August 22, 2012, 10:24:42 AM »
2 other versions using the mapcar idea. Also eval to build the control's name from a string with a number. And the last shows a way of using apply as well.
Code - Auto/Visual Lisp: [Select]
  1. (defun myFunction2 ()
  2.   (mapcar '(lambda (control x y w h tip pic)
  3.              (dcl_Control_SetPos control x y w h)
  4.              (dcl_Control_SetPicture control pic)
  5.              (dcl_Control_SetToolTipMainText control tip)
  6.              (dcl_Control_SetVisible control T))
  7.           (mapcar '(lambda (n) (eval (read (strcat "DuctilCAD_DuctilCAD_MainForm_PictureBox" (itoa n))))) '(1 2 3 4 5))
  8.           '(1 123 245 367 1)
  9.           '(83 83 83 203)
  10.           '(122 122 122 122)
  11.           '(120 120 120 120)
  12.           (mapcar '(lambda (n) (strcat "Item-" (itoa n))) '(1 2 3 4 5))
  13.           '(149 150 151 152 153)))
  14.  
  15. (defun myFunction3 ()
  16.   (mapcar '(lambda (control size tip pic)
  17.              (apply 'dcl_Control_SetPos (cons control size))
  18.              (dcl_Control_SetPicture control pic)
  19.              (dcl_Control_SetToolTipMainText control tip)
  20.              (dcl_Control_SetVisible control T))
  21.           (mapcar '(lambda (n) (eval (read (strcat "DuctilCAD_DuctilCAD_MainForm_PictureBox" (itoa n))))) '(1 2 3 4 5))
  22.           '((1 83 122 120) (123 83 122 120) (245 83 122 120) (367 83 122 120) (1 203 122 120))
  23.           (mapcar '(lambda (n) (strcat "Item-" (itoa n))) '(1 2 3 4 5))
  24.           '(149 150 151 152 153)))
Not saying it's better than yours. It's just different.
« Last Edit: August 22, 2012, 10:32:17 AM by irneb »
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: Foreah or ...
« Reply #9 on: August 22, 2012, 10:31:12 AM »
Or another alternative using foreach:
Code - Auto/Visual Lisp: [Select]
  1. (defun myFunction4 (/ settings n control)
  2.   (setq settings '(((1 83 122 120) 149)
  3.                    ((123 83 122 120) 150)
  4.                    ((245 83 122 120) 151)
  5.                    ((367 83 122 120) 152)
  6.                    ((1 203 122 120) 153))
  7.         n 0)
  8.   (foreach item settings
  9.     (setq n (1+ n) control (eval (read (strcat "DuctilCAD_DuctilCAD_MainForm_PictureBox" (itoa n)))))
  10.     (apply 'dcl_Control_SetPos (cons control (car item)))
  11.     (dcl_Control_SetPicture control (cadr item))
  12.     (dcl_Control_SetToolTipMainText control (strcat "Item-" (itoa n)))
  13.     (dcl_Control_SetVisible control T))))

Sorry ... noticed a mistake in my codes. Both last posts edited to fix.
« Last Edit: August 22, 2012, 10:37:54 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.