TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: Mark on November 22, 2005, 09:17:56 AM

Title: mapcar or foreach
Post by: Mark on November 22, 2005, 09:17:56 AM
Can you tell me when it's appropriate to use either mapcar or foreach?
Title: Re: mapcar or foreach
Post by: LE on November 22, 2005, 09:42:27 AM
I use;

- MAPCAR when I need to get LIST.
- FOREACH when I want to do several things to each item on a LIST and I do not require any output and simple I want to process ALL of them to certain function.

HTMSS
Title: Re: mapcar or foreach
Post by: JohnK on November 22, 2005, 10:00:10 AM
^^ Same for me.

Here is a quick demo.

Command:

Command: (setq starterlist '(1 2 3 4 5 6 7 8 9))
(1 2 3 4 5 6 7 8 9)

Command: (setq mylist starterlist)
(1 2 3 4 5 6 7 8 9)

Command: (repeat 7 (setq mylist (append starterlist mylist)))
(1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3
4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9)

Command: (setq mapcarlist (mapcar '(lambda (x) (1+ x)) mylist))
(2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2
3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10)

Command: (setq foreachlist (foreach x mylist (1+ x)))
10

Command: !mapcarlist
(2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2
3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10 2 3 4 5 6 7 8 9 10)

Command: !foreachlist
10

Command:
Title: Re: mapcar or foreach
Post by: MP on November 22, 2005, 10:03:59 AM
Lile Luis said / implied, generally speaking -- when I want the result of an operation on a list to return a list I use mapcar, otherwise foreach.

Interestingly, mapcar is frequently used when foreach is actually more appropriate.

Classic example --

(start_list "key")
(mapcar 'add_list items)
(end_list)

Result is discarded.

I suspect it's the brevity of mapcar that frequently inspires it's use.

The foreach equivalent just for completeness --

(start_list "key")
(foreach item items (add_list item))
(end_list)

:)
Title: Re: mapcar or foreach
Post by: LE on November 22, 2005, 10:19:01 AM
It is somehow what happens to the function REPEAT  that [according to my impression] not many are using it anymore [including me] or maybe very little.
Title: Re: mapcar or foreach
Post by: MP on November 22, 2005, 10:22:18 AM
I use repeat often --

(if (setq ss (ssget))
    (repeat (setq i (sslength ss))
        (foo (ssname ss (setq i (1- i))))
    )
)
Title: Re: mapcar or foreach
Post by: Joe Burke on November 22, 2005, 10:27:17 AM
In addition to what's been said, the other key difference between foreach and mapcar is mapcar accepts multiple list arguments. Foreach only takes one list argument.

As Michael said, which one to use given a single list argument is sometimes a toss up.
Title: Re: mapcar or foreach
Post by: Mark on November 22, 2005, 12:46:16 PM
How about some examples of working with an entity list?
Title: Re: mapcar or foreach
Post by: MP on November 22, 2005, 12:48:44 PM
Context please.

:)
Title: Re: mapcar or foreach
Post by: Mark on November 22, 2005, 01:04:15 PM
May not be the best example.

Code: [Select]
(setq ss (ssget "X" '((0 . "TEXT")))
      cntr 0
      )

(while (setq ent (ssname ss cntr))
  ;
  ; pull out significant parts, DXF values 1, 8, 40, 410, etc.
  ;
  (setq cntr (1+ cntr))
  )
Title: Re: mapcar or foreach
Post by: LE on November 22, 2005, 01:09:22 PM
In example change the ename's color to RED

Code: [Select]
(foreach
       ename  (list ent1 ent2 ent3)
  (setq elist (entget ename))
  ;; if they are bylayer change to color 1
  (if (not (cdr (assoc 62 elist)))
    (entmod (append elist (list (cons 62 1))))))

Code: [Select]
(mapcar '(lambda (obj) (vla-put-color obj acRed))
(mapcar 'vlax-ename->vla-object (list ent1 ent2 ent3)))
Title: Re: mapcar or foreach
Post by: MP on November 22, 2005, 01:11:27 PM
Code: [Select]
(if (setq ss (ssget "x" '((0 . "text"))))
    (repeat (setq i (sslength ss))
        (setq data (entget (ssname ss (setq i (1- i)))))
        ;;
        ;;  pull out significant parts, DXF values 1, 8, 40, 410, etc.
        ;;
        (print
            (mapcar
               '(lambda (key) (assoc key data))
               '(1 8 40 410)
            )
        )
        (princ)
    )
)
Title: Re: mapcar or foreach
Post by: MP on November 22, 2005, 01:35:32 PM
Sorry don't have time to pen something pretty but perhaps this will illuminate ...

Code: [Select]
(defun c:CatSkinning ( / ss i keys data )

    (setq keys '(8 40 1))

    (if (setq ss (ssget "x" '((0 . "text"))))

        (repeat (setq i (sslength ss))

            (setq data (entget (ssname ss (setq i (1- i)))))

            ;;  mapcar 1

            (princ "\nmapcar 1 ...")

            (print
                (mapcar
                   '(lambda (key) (assoc key data))
                    keys
                )
            )

            ;;  mapcar 2 (not desirable, but for
            ;;  demonstration purposes included)

            (princ "\nmapcar 2 ...")

            (print
                (apply 'append
                    (mapcar
                       '(lambda (pair)
                            (if (member (car pair) keys)
                                (list pair)
                            )
                        )
                        data
                    )
                )
            )   

            ;;  vl-remove-if-not

            (princ "\nvl-remove-if-not ...")

            (print
                (vl-remove-if-not
                   '(lambda (pair) (member (car pair) keys))
                    data
                )
            )

            ;;  foreach

            (princ "\nforeach ...")

            (print
                (   (lambda ( / result )
                        (foreach pair data
                            (if (member (car pair) keys)
                                (setq result
                                    (cons
                                        pair
                                        result
                                    )
                                )
                            )
                        )
                        (reverse result)
                    )
                )
            )

            (princ "\n\n")

        )
    )

    (princ)

)
Title: Re: mapcar or foreach
Post by: paulmcz on November 22, 2005, 03:06:10 PM
"foreach" can speed things up significantly. Compare these two ways of drawing an ellipse:

This one with use of "foreach"

Code: [Select]
(defun c:lll (/ osn a b x ip clv clh p1 p2 cpy cpx e1 e2 z plp cc)
; draws ellipse out of polyline
  (setq osn (getvar "OSMODE"))
  (setvar "cmdecho" 0)
;====================================================================
  (if aa
    ()
    (setq aa 1.0)
  )
  (princ "\n A radius < ") ; short radius
  (princ aa)
  (princ " > ?? : ")
  (initget 6)
  (setq a (getdist))
  (if (= a nil)
    (setq a aa)
  )
  (setq aa a)
;==================================================================
  (if bb
    ()
    (setq bb 2.0)
  )
  (princ "\n B radius < ")
  (princ bb)
  (princ " > ?? : ")
  (initget 6)
  (setq b (getdist))
  (if (= b nil)
    (setq b bb)
  )
  (setq bb b)
;==================================================================

  (setq x (/ pi 90))

;====================================================================
  (setq ip (getpoint "\n Insert : "))

; calc

  (setvar "osmode" 0)
  (command "line"
   (polar ip (* pi 0.5) (* a 1.125))
   (polar ip (* pi 1.5) (* a 1.125))
   ""
  )
  (setq clv (entlast))
  (command "change"
   clv
   ""
   "p"
   "c"
   3
   "lt"
   "center"
   "s"
   (/ a 4)
   ""
  )
  (command "line"
   (polar ip 0 (* b 1.125))
   (polar ip pi (* b 1.125))
   ""
  )
  (setq clh (entlast))
  (command "change"
   clh
   ""
   "p"
   "c"
   3
   "lt"
   "center"
   "s"
   (/ a 4)
   ""
  )
  (setq p1 (polar ip (* pi 1.5) a))
  (setq cpy (polar ip (* pi 1.5) (* a (cos x))))
  (setq cpx (polar ip 0 (* b (sin x))))
  (setq p2 (list (car cpx) (cadr cpy)))
  (command "pline" p1 p2 "")
  (setq e1 (entlast))
  (setq z (* 2 x))
  (while (< z (+ (* 2 pi) (/ z 2)))
    (setq plp (cons p2 plp))
    (setq cpy (polar ip (* pi 1.5) (* a (cos z))))
    (setq cpx (polar ip 0 (* a (sin z) (/ b a))))
    (setq p2 (list (car cpx) (cadr cpy)))
    (setq z (+ z x))
  )
  (setq plp (reverse plp))
  (command "pline")
  (foreach cc plp (command cc))
  (command "")
  (setq e2 (entlast))
  (command "pedit" e1 "j" e2 "" "")


  (prompt "\nRotation Angle: ")
  (setvar "osmode" osn)
  (command "ROTATE" e1 clv clh "" IP pause)

  (princ)
)
(prompt "\n Type < LLL > for ellipse")

... and this one stepping through segments of polyline:

Code: [Select]
(defun c:lll (/ osn a b x ip clv clh p1 p2 cpy cpx e1 e2 z) ; draws ellipse out of polyline
  (setq osn (getvar "OSMODE"))
  (setvar "cmdecho" 0)
;====================================================================
  (if aa
    ()
    (setq aa 1.0)
  )
  (princ "\n A radius < ") ; short radius
  (princ aa)
  (princ " > ?? : ")
  (initget 6)
  (setq a (getdist))
  (if (= a nil)
    (setq a aa)
  )
  (setq aa a)
;==================================================================
  (if bb
    ()
    (setq bb 2.0)
  )
  (princ "\n B radius < ")
  (princ bb)
  (princ " > ?? : ")
  (initget 6)
  (setq b (getdist))
  (if (= b nil)
    (setq b bb)
  )
  (setq bb b)
;==================================================================
  ;|(if xp ()(setq xp 2))
  (princ "\n Precision in degrees [1 - 10] < ")
  (princ xp)
  (princ " > ?? : ")
  (setq x (getint))
  (if (> x 10)(setq x 10))
  (if (or (= x 3) (= x 4))(setq x 2))
  (if (or (= x 7) (= x 8) (= x 9))(setq x 6))
  (if (= x nil)(setq x (/ (* xp pi) 180))(setq x (/ (* x pi) 180)))
  (setq xp (/ (* x 180) pi))|;
  (setq x (/ pi 90))

;====================================================================
  (setq ip (getpoint "\n Insert : "))

; calc

  (setvar "osmode" 0)
  (command "line"
   (polar ip (* pi 0.5) (* a 1.125))
   (polar ip (* pi 1.5) (* a 1.125))
   ""
  )
  (setq clv (entlast))
  (command "change"
   clv
   ""
   "p"
   "c"
   3
   "lt"
   "center"
   "s"
   (/ a 4)
   ""
  )
  (command "line"
   (polar ip 0 (* b 1.125))
   (polar ip pi (* b 1.125))
   ""
  )
  (setq clh (entlast))
  (command "change"
   clh
   ""
   "p"
   "c"
   3
   "lt"
   "center"
   "s"
   (/ a 4)
   ""
  )
  (setq p1 (polar ip (* pi 1.5) a))
  (setq cpy (polar ip (* pi 1.5) (* a (cos x))))
  (setq cpx (polar ip 0 (* b (sin x))))
  (setq p2 (list (car cpx) (cadr cpy)))
  (command "pline" p1 "w" 0 0 p2 "")
  (setq e1 (entlast))
  (setq z (* 2 x))
  (while (< z (* 2 pi))
    (setq cpy (polar ip (* pi 1.5) (* a (cos z))))
    (setq cpx (polar ip 0 (* a (sin z) (/ b a))))
    (setq p2 (list (car cpx) (cadr cpy)))
    (command "pline" "" "w" 0 0 p2 "")
    (setq e2 (entlast))
    (command "pedit" e1 "j" e2 "" "")
    (setq z (+ z x))
  )

  (prompt "\nRotation Angle: ")
  (setvar "osmode" osn)
  (command "ROTATE" e1 clv clh "" IP pause)
 
  (princ)
)
(prompt "\n Type < LLL > for ellipse")
Title: Re: mapcar or foreach
Post by: whdjr on November 22, 2005, 04:15:30 PM
Foreach his own, but alas for me is mapcar.

As MP stated I use mapcar more for its brevity.


[sidebar]
MP,  it's rather ironic that you used 'ssget' to show that you use 'repeat' quite often when 'foreach' and 'mapcar' can't get 'ssget' entities.
[/sidebar]
Title: Re: mapcar or foreach
Post by: MP on November 22, 2005, 04:22:15 PM
[sidebar]
MP, it's rather ironic that you used 'ssget' to show that you use 'repeat' quite often when 'foreach' and 'mapcar' can't get 'ssget' entities.
[/sidebar]

In the spirit of fun ...

Code: [Select]
(defun PicksetToEnames ( ss / i result )
    (if (eq 'pickset (type ss))
        (repeat (setq i (sslength ss))
            (setq result
                (cons
                    (ssname ss (setq i (1- i)))
                    result
                )
            )
        )
    )
)

:)
Title: Re: mapcar or foreach
Post by: CAB on November 22, 2005, 06:43:15 PM
And more fun.
Well you used the "can't" word. he he he
Code: [Select]
;;  CAB 03/22/2005
;;  just an exersize
(defun c:TxtC (/ ss elst)
  (prompt "\nSelect text objects... <exit> ")
  (and (setq ss (ssget '((0 . "TEXT") (72 . 1) (73 . 2))))
       (repeat (sslength ss)
         (setq elst (entget (ssname ss 0)))
         (entmake (list '(0 . "CIRCLE")
                        (cons 10 (cdr (assoc 11 elst)))
                        (cons 40 (* (cdr (assoc 40 elst)) 0.06))
                  )
         )
         (ssdel (ssname ss 0) ss)
       )
  )
  (princ)
)
Title: Re: mapcar or foreach
Post by: LE on November 22, 2005, 07:03:14 PM
Here are some samples using mapcar and foreach:

Code: [Select]
(defun rcmd-divide-vector  (pt1 pt2 n)
  (mapcar '+
  pt1
  (mapcar '/
  (mapcar '- pt2 pt1)
  (list n n n))))

(defun rcmd-transpts  (lst pt / p0 p1 ang pin ang pt1 lst1)
  (setq p0  (car lst)
p1  (cadr lst)
ang (angle p0 p1)
pin (inters p0 p1 pt (polar pt (+ ang (/ pi 2)) 1) nil)
ang (angle pin pt))
  (foreach
p
  lst
    (setq pt1  (polar p ang (distance pt pin))
  lst1 (append lst1 (list pt1))))
  lst1)

(defun rcmd-point-member  (p lst / tst)
  (foreach
pt
   lst
    (if pt
      (if (equal (distance p pt) 0 0.00001)
(setq tst T))))
  tst)

(defun rcmd-list->ss  (lst / ss0)
  (setq ss0 (ssadd))
  (foreach n lst (ssadd n ss0))
  ss0)

(defun rcmd-point-member  (p lst / tst)
  (foreach
pt
   lst
    (if pt
      (if (equal (distance p pt) 0 0.00001)
(setq tst T))))
  tst)

(defun rcmd-no-equal-points  (lst / lst2)
  (foreach
pt
   lst
    (if (not (rcmd-point-member pt lst2))
      (setq lst2 (append lst2 (list pt)))))
  lst2)
Title: Re: mapcar or foreach
Post by: LE on November 22, 2005, 07:21:08 PM
another one:

Code: [Select]
;; string_lst = list of strings
;; flag = max or' min
(defun strlenmm (string_lst flag / lst)
  (setq lst (mapcar (function (lambda (x) (cons (strlen x) x)))
    string_lst))
  (cdr (assoc (apply 'flag (mapcar 'car lst)) lst)))

_$ (strlenmm (list "10000.0" "1.0" "2300000000" "1.234") min)
"1.0"
_$
_$ (strlenmm (list "10000.0" "1.0" "2300000000" "1.234") max)
"2300000000"
_$
Title: Re: mapcar or foreach
Post by: Jeff_M on November 22, 2005, 07:30:52 PM
Will you used the "can't" word. he he he
Maybe I misread that, but I thought he was referring to the fact you can't do
Code: [Select]
(foreach ent ss ....)
(mapcar 'entget ss)
Title: Re: mapcar or foreach
Post by: MP on November 22, 2005, 07:44:25 PM
That's the way I read it too Jeff, ergo ..

(foreach ename (PicksetToEnames (ssget "x"))
    (foo ename)
)


Or ...

(mapcar 'foo (PicksetToEnames (ssget "x")))

:)
Title: Re: mapcar or foreach
Post by: CAB on November 22, 2005, 08:44:56 PM
OK, i understand now, Little slow on the uptake lately. :lol:

And this doesn't count, right?
Code: [Select]
(foreach ename (mapcar (function cadr) (ssnamex ss))
Well I tried, :-o
Title: Re: mapcar or foreach
Post by: Kerry on November 22, 2005, 08:54:33 PM
Hi CAB,

In your c:TxtC, I found it interesting that you progressively popped the first item from the selection set
at each iteration and called (entget (ssname ss 0)))
rather than create an incrementing counter as an index into a static selectionset. 
Title: Re: mapcar or foreach
Post by: CAB on November 22, 2005, 09:27:44 PM
Thanks for noticing.
That was the deference I was trying to show from Michael's example above it.
I should have dressed it up a bit as pretty does not come naturally to me as you know.
It was an exercise in ways to iterate through a selection set and that was one of them.
As you know deleting from a selection set if you are not careful can produce unwanted results.
Title: Re: mapcar or foreach
Post by: whdjr on November 23, 2005, 09:20:56 AM
Will you used the "can't" word. he he he
Maybe I misread that, but I thought he was referring to the fact you can't do
Code: [Select]
(foreach ent ss ....)
(mapcar 'entget ss)


Thanks for helping me get my point across Jeff.

I thought I was gonna have to clarify myself.

 :-)
Title: Re: mapcar or foreach
Post by: whdjr on November 23, 2005, 09:35:28 AM
Quote

In the spirit of fun ...

Code: [Select]
(defun PicksetToEnames ( ss / i result )
    (if (eq 'pickset (type ss))
        (repeat (setq i (sslength ss))
            (setq result
                (cons
                    (ssname ss (setq i (1- i)))
                    result
                )
            )
        )
    )
)

:)
That's the way I read it too Jeff, ergo ..

(foreach ename (PicksetToEnames (ssget "x"))
    (foo ename)
)


Or ...

(mapcar 'foo (PicksetToEnames (ssget "x")))

:)

Michael,

This just further illustrates my point that you can't get at the entities in a 'ssget' selection set.

I just thought it was funny that LE's statement that 'repeat' seems to be forgotten by most people was responded by you with an example of a situation of where the only way to get at the data in the example was to use repeat- which was the only method of the 3 previously mentioned methods that would have worked in this occurance.

 :-)
Title: Re: mapcar or foreach
Post by: LE on November 23, 2005, 09:46:44 AM
Code: [Select]
(setq ss (ssget))
(vlax-for
       item
      (vla-get-activeselectionset
(vla-get-activedocument (vlax-get-acad-object)))
  (print item))
Title: Re: mapcar or foreach
Post by: LE on November 23, 2005, 09:51:04 AM
Code: [Select]
(vlax-for
       item
      (vla-get-activeselectionset
(vla-get-activedocument (vlax-get-acad-object)))
  (print (entget (vlax-vla-object->ename item))))
Title: Re: mapcar or foreach
Post by: Fatty on November 23, 2005, 10:05:39 AM
Code: [Select]
(setq ss (ssget))
(vlax-for
       item
      (vla-get-activeselectionset
(vla-get-activedocument (vlax-get-acad-object)))
  (print item))
I use the following one without declaring of ss:
Code: [Select]
(ssget)
(vlax-for
       item
      (vla-get-activeselectionset
(vla-get-activedocument (vlax-get-acad-object)))
  (print item))
Title: Re: mapcar or foreach
Post by: LE on November 23, 2005, 10:13:58 AM
I use the following one without declaring of ss:
Code: [Select]
(ssget)

Quote

Yep... we don't need the ss var..... I am doing this samples on the fly [like mickey-mouse code]....  :-)

Thanks.
Title: Re: mapcar or foreach
Post by: MP on November 23, 2005, 10:16:06 AM
Michael,

This just further illustrates my point ...

Ok Will, thanks. All our awareness are belong to us.

:)

I just thought it was funny that LE's statement that 'repeat' seems to be forgotten ...

Ok x 2. I just thought Luis was talking general diminished use of repeat, so I spewed the first example that thrust itself into my nuggin. Sorry if it was a curious example.

:oops:
Title: Re: mapcar or foreach
Post by: whdjr on November 23, 2005, 10:29:29 AM
Ok now I feel bad for carrying this out to far. :cry:


Just general observations and no hard feelings ok. :-)
Title: Re: mapcar or foreach
Post by: MP on November 23, 2005, 10:33:56 AM
I'd rather you speak your mind than have your contributions / thoughts stifled, so you observe away my friend, and of course, no hard feelings.

And please forgive my twisted sense of humour which manisfests itself in posts like the previous.

:)
Title: Re: mapcar or foreach
Post by: whdjr on November 23, 2005, 11:11:48 AM
 :-)
Title: Re: mapcar or foreach
Post by: Sdoman on November 23, 2005, 12:38:27 PM
If it doesn't matter which function to use, then I use Mapcar when I want to be cool and Foreach when not.  :lol:
Title: Re: mapcar or foreach
Post by: SMadsen on November 24, 2005, 10:11:31 AM
I use the following one without declaring of ss:
Code: [Select]
(ssget)
(vlax-for ...
)

Might wanna do a little more like (if (ssget)(vlax-for ... )) or check PICKFIRST for further tests and such ...

Anyway, interesting thread, folks.