Author Topic: Break foreach loop in between  (Read 7352 times)

0 Members and 1 Guest are viewing this topic.

mailmaverick

  • Bull Frog
  • Posts: 493
Break foreach loop in between
« on: March 30, 2014, 08:03:41 AM »
Is there a way to break foreach loop in between ?

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Break foreach loop in between
« Reply #1 on: March 30, 2014, 08:31:16 AM »
You can force a break by wrapping the foreach loop inside a vl-catch-all-apply function and calling the exit function to force an error, however, this is not the best practice.

Here is a simple example:
Code - Auto/Visual Lisp: [Select]
  1. (defun c:test ( / n )
  2.     (setq n 0)
  3.     (vl-catch-all-apply
  4.         (function
  5.             (lambda ( )
  6.                 (foreach x '(0 1 2 3 4 5)
  7.                     (if (= x 3)
  8.                         (exit)
  9.                     )
  10.                     (setq n (1+ n))
  11.                 )
  12.             )
  13.         )
  14.     )
  15.     (princ "\nNumber of items processed: ")
  16.     (princ n)
  17.     (princ)
  18. )

A better approach would be to use a while loop with a condition to nullify the test expression, e.g.:
Code - Auto/Visual Lisp: [Select]
  1. (defun c:test ( / l n x )
  2.     (setq l '(0 1 2 3 4 5)
  3.           n 0
  4.     )
  5.     (while
  6.         (and
  7.             (setq x (car l))
  8.             (/= x 3)
  9.         )
  10.         (setq l (cdr l)
  11.               n (1+ n)
  12.         )
  13.     )
  14.     (princ "\nNumber of items processed: ")
  15.     (princ n)
  16.     (princ)
  17. )

snownut2

  • Swamp Rat
  • Posts: 971
  • Bricscad 22 Ultimate
Re: Break foreach loop in between
« Reply #2 on: March 30, 2014, 08:32:07 AM »
Why break it, redefine the criteria so it completes. (breaking means its done ?)

mailmaverick

  • Bull Frog
  • Posts: 493
Re: Break foreach loop in between
« Reply #3 on: March 30, 2014, 08:38:24 AM »
Dear Lee Mac, as per your reply, it means there is no way to properly break through foreach loop. Am i correct ?

Dear Snownut2, there is no criteria in foreach loop. Then how can we redefine it ?

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Break foreach loop in between
« Reply #4 on: March 30, 2014, 08:56:36 AM »
Dear Lee Mac, as per your reply, it means there is no way to properly break through foreach loop. Am i correct ?

Not without forcing an error.

snownut2

  • Swamp Rat
  • Posts: 971
  • Bricscad 22 Ultimate
Re: Break foreach loop in between
« Reply #5 on: March 30, 2014, 09:55:47 AM »
You can use condition statements (ie; cond, if) within the foreach loop.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Break foreach loop in between
« Reply #6 on: March 30, 2014, 10:14:14 AM »
You can use condition statements (ie; cond, if) within the foreach loop.
It won't break the loop.
Speaking English as a French Frog

parktaeeun

  • Guest
Re: Break foreach loop in between
« Reply #7 on: March 30, 2014, 11:03:36 AM »
i think Foreach function is not possible.

however, vl-every function is used the desired results can be obtained.


MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: Break foreach loop in between
« Reply #8 on: March 30, 2014, 11:13:20 AM »
parktaeeun is correct:

Code: [Select]
(defun c:test ( / n )

    (setq n 0)
   
    (vl-every
        (function
            (lambda ( x )
                (if (/= x 3)
                    (setq n (1+ n))
                )   
            )
        )
       '(0 1 2 3 4 5)
    )   
   
    (princ "\nNumber of items processed: ")
    (princ n)
    (princ)

)

That said, it's not how I would normally approach it and on that Lee already illuminated.
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

Stefan

  • Bull Frog
  • Posts: 319
  • The most I miss IRL is the Undo button
Re: Break foreach loop in between
« Reply #9 on: March 30, 2014, 02:50:00 PM »
In this case, the while function (or vl-every or vl-some) would be a better approach than foreach.
But what about the results? This function returns a list of an action  performed on each element of a list, as long as a condition is met.
Code - Auto/Visual Lisp: [Select]
  1. (defun mapcar_while (_list _function _condition)
  2.   (if
  3.     (and
  4.       _list
  5.       (apply _condition (list (car _list)))
  6.       )
  7.     (cons
  8.       (apply _function  (list (car _list)))
  9.       (mapcar_while (cdr _list) _function _condition)
  10.       )
  11.     )
  12.   )
Code: [Select]
(mapcar_while
  '(1 2 3 4 5 6 7)
  '(lambda (x) (* x x))
  '(lambda (x) (/= x 4))
  )
-->(1 4 9)

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: Break foreach loop in between
« Reply #10 on: March 30, 2014, 03:15:41 PM »
Nice idea Stefan  :-)

Here is another way to write it (none too efficient - just for fun):
Code - Auto/Visual Lisp: [Select]
  1. (defun mapcar_while ( lst fun con / foo bar )
  2.     (setq
  3.         foo
  4.         (eval
  5.             (list 'lambda '( x )
  6.                 (list 'list (list (eval fun) 'x))
  7.             )
  8.         )
  9.         bar
  10.         (eval
  11.             (list 'lambda '( x )
  12.                 (list 'if (list (eval con) 'x)
  13.                     '(foo x)
  14.                     '(not (setq foo (lambda ( x ) nil)))
  15.                 )
  16.             )
  17.         )
  18.     )
  19.     (apply 'append (mapcar 'bar lst))
  20. )

Marc'Antonio Alessi

  • Swamp Rat
  • Posts: 1453
  • Marco
Re: Break foreach loop in between
« Reply #11 on: March 31, 2014, 12:28:14 PM »
parktaeeun is correct:

Code: [Select]
(defun c:test ( / n )

    (setq n 0)
   
    (vl-every
        (function
            (lambda ( x )
                (if (/= x 3)
                    (setq n (1+ n))
                )   
            )
        )
       '(0 1 2 3 4 5)
    )   
   
    (princ "\nNumber of items processed: ")
    (princ n)
    (princ)

)

That said, it's not how I would normally approach it and on that Lee already illuminated.
...vl-every also seems faster...
Code: [Select]
(progn
  (princ "\Posit: ")
  (princ (setq posit 100000))
  (setq aListInt '("Last") Countr 0)
  (repeat 1000000 (setq aListInt (cons (setq Countr (1+ Countr)) aListInt)))
  (princ "\nLength aListInt: ") (princ (length aListInt)) (princ "\n ") (princ)
)

Posit: 100
Length aListInt: 1000001
Elapsed milliseconds / relative speed for 4096 iteration(s):
   (ALE_LIST_EVERYBYLEN POSIT ALISTINT).......1108 / 2.76 <fastest>
    (ALE_LIST_FOREACHBYLEN POSIT ALISTINT).....1108 / 2.76
    (ALE_LIST_EVERYBYLEN POSIT ALISTINT).......1295 / 2.36
    (ALE_LIST_FOREACHBYLEN POSIT ALISTINT).....1342 / 2.28
    (ALE_LIST_MAPCARBYLEN POSIT ALISTINT)......1623 / 1.88
    (ALE_LIST_MAPCARBYLEN POSIT ALISTINT)......3057 / 1 <slowest>


Posit: 100000
Length aListInt: 1000001
Elapsed milliseconds / relative speed for 16 iteration(s):
    (ALE_LIST_EVERYBYLEN POSIT ALISTINT).......1716 / 2.27 <fastest>
    (ALE_LIST_FOREACHBYLEN POSIT ALISTINT).....1841 / 2.12
    (ALE_LIST_MAPCARBYLEN POSIT ALISTINT)......2184 / 1.79
    (ALE_LIST_EVERYBYLEN POSIT ALISTINT).......3354 / 1.16
    (ALE_LIST_FOREACHBYLEN POSIT ALISTINT).....3869 / 1.01
    (ALE_LIST_MAPCARBYLEN POSIT ALISTINT)......3900 / 1 <slowest>
Code: [Select]
; 03-01-2014
(defun ALE_List_ForeachByLen (n l / c r)
  (setq c 1)
  (vl-catch-all-apply
    (function
      (lambda ( )
        (foreach x l
          (if (> c n)
            (exit)
            (setq r (cons x r) c (1+ c))
          )
        )
      )
    )
  )
  (reverse r)
)
; 03-01-2014
(defun ALE_List_MapcarByLen (n l / c r)
  (setq c 1)
  (vl-catch-all-apply
    'mapcar
    (list
     '(lambda (x)
        (if (> c n)
          (exit)
          (setq r (cons x r) c (1+ c))
        )
      )
      l
    )
  )
  (reverse r)
)
;
(defun ALE_List_EveryByLen (n l / c r)
  (setq c 1)
  (vl-every
    (function
      (lambda (x)
        (if (<= c n)
          (setq r (cons x r) c (1+ c))
        )
      )
    )
    l
  )
  (reverse r)
)

Marc'Antonio Alessi

  • Swamp Rat
  • Posts: 1453
  • Marco
Re: Break foreach loop in between
« Reply #12 on: March 31, 2014, 12:52:50 PM »
Maybe this a better test:
Code: [Select]
Benchmark.lsp | © 2005 Michael Puckett | All Rights Reserved
Posit: 100
Length aListInt: 1000001
Elapsed milliseconds / relative speed for 8192 iteration(s):
    (ALE_LIST_EVERYBYLEN2 POSIT ALISTINT).......1170 / 5.52 <fastest>
    (ALE_LIST_EVERYBYLEN2 POSIT ALISTINT).......2574 / 2.51
    (ALE_LIST_FOREACHBYLEN2 POSIT ALISTINT).....3260 / 1.98
    (ALE_LIST_FOREACHBYLEN2 POSIT ALISTINT).....3729 / 1.73
    (ALE_LIST_MAPCARBYLEN2 POSIT ALISTINT)......5522 / 1.17
    (ALE_LIST_MAPCARBYLEN2 POSIT ALISTINT)......6459 / 1 <slowest>


Posit: 100000
Length aListInt: 1000001
Elapsed milliseconds / relative speed for 16 iteration(s):
    (ALE_LIST_EVERYBYLEN2 POSIT ALISTINT).......1107 / 2.76 <fastest>
    (ALE_LIST_EVERYBYLEN2 POSIT ALISTINT).......2216 / 1.38
    (ALE_LIST_MAPCARBYLEN2 POSIT ALISTINT)......2340 / 1.31
    (ALE_LIST_MAPCARBYLEN2 POSIT ALISTINT)......2496 / 1.22
    (ALE_LIST_FOREACHBYLEN2 POSIT ALISTINT).....2995 / 1.02
    (ALE_LIST_FOREACHBYLEN2 POSIT ALISTINT).....3057 / 1 <slowest>
Code: [Select]
(defun ALE_List_ForeachByLen2 (n l / c r)
  (setq c 1)
  (vl-catch-all-apply
    (function
      (lambda ( ) (foreach x l (if (> c n) (exit) (setq c (1+ c)))))
    )
  )
)
(defun ALE_List_MapcarByLen2 (n l / c r)
  (setq c 1)
  (vl-catch-all-apply
    'mapcar
    (list '(lambda (x) (if (> c n) (exit) (setq c (1+ c)))) l)
  )
)
(defun ALE_List_EveryByLen2 (n l / c r)
  (setq c 1)
  (vl-every
    (function
      (lambda (x) (if (<= c n) (setq c (1+ c))))
    )
    l
  )
)

reltro

  • Guest
Re: Break foreach loop in between
« Reply #13 on: April 02, 2014, 12:28:36 PM »
Hey People

I think my current try seems to fit in here.
At the moment I try to code some kind of generator-function, wich yield's a Value on the Runtime, and after recalling it start over on that position.

The code is very complicated and I had'nt any time yet to add comments.
Its not even finished and can handle not all kind of stuff.

At the moment it can handle yield's nested in:
- if
- while
- yield
- and I think all kind of operators wich aren't any special.

If the operator is a list it will output a message, beause I have'nt any idea how to handle thi situation.

*********************
Here a small example:
Code: [Select]
(generator
   'TEST
   '(lambda (a b / c )
      (while (> a 0)
         (setq c b)
         (while (> b 0)
         (print b)
            (if (> (yield a) 5)
               (yield (cons a b))
            )
            (setq b (1- b))
         )
         (setq a (1- a))
         (setq b c)
       "This is the end"
      )
   )
   '(10 5)
)
The function 'generator' parses the given lambda-expression to a "generator-function" and stores it in the given Symbol (here 'TEST).
It also stores the namespace of the lambda-expression. The third argument are the InputValues for the future generator.


Use it like
Code: [Select]
(test) ;-> (T . 10)
(test) ;-> (T 10 . 5)
.
.
.
(test) ;-> (nil . "This is the end")

The function return a dotted-pair.
* The car-value
   'T   ... Value was yield-ed
   'nil ... Function is finished
   
Maybe someone has a use for it ;)
or some ideas to make it better....


** Code in the attachment
   
greets
reltro