Author Topic: Divide List  (Read 5179 times)

0 Members and 1 Guest are viewing this topic.

Lee Mac

  • Seagull
  • Posts: 12929
  • London, England
Divide List
« on: June 05, 2009, 07:18:23 PM »
Is there a quick way to split a list into two lists such that all the elements in the first satisfy a certain criteria (or predicate function) and the second list contains all the elements that fail this criteria.

Obviously there are the functions vl-remove-if and vl-remove-if-not, but this means using these one after the other on the same list, and is quite repetitive. So I wondered if there was a function to perform this task in one operation.

As an example to clarify my intentions:

Before:
Code: [Select]
(1 2 3 4 5 6 7 8 9)
Test Function:
Code: [Select]
(zerop (rem x 3))
Return:
Code: [Select]
((3 6 9) (1 2 4 5 7 8))

Thanks,

Lee

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Re: Divide List
« Reply #1 on: June 05, 2009, 08:05:43 PM »
Code: [Select]
(mapcar '(lambda(x)
           (if (zerop (rem x 3))
             (setq l1 (cons x l1))
             (setq l2 (cons x l2))))
        '(1 2 3 4 5 6 7 8 9))
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

Lee Mac

  • Seagull
  • Posts: 12929
  • London, England
Re: Divide List
« Reply #2 on: June 05, 2009, 08:27:42 PM »
Nice solution CAB - can't believe I hadn't thought of something like that  :oops:

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Re: Divide List
« Reply #3 on: June 05, 2009, 08:34:54 PM »
There will be days like that.  8-)
Tomorrow may be mine.  :lol:
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

Joe Burke

  • Guest
Re: Divide List
« Reply #4 on: June 06, 2009, 10:32:19 AM »
I think foreach would be more approrpriate in this case.

Lee Mac

  • Seagull
  • Posts: 12929
  • London, England
Re: Divide List
« Reply #5 on: June 06, 2009, 11:13:11 AM »
I think foreach would be more approrpriate in this case.

Good Suggestion - I always miss the simple option.   :angel:

gile

  • Gator
  • Posts: 2520
  • Marseille, France
Re: Divide List
« Reply #6 on: June 06, 2009, 11:22:54 AM »
Hi,

Someyhing like this ?

Code: [Select]
(defun divideList (lst fun / l1 l2)
  (foreach ele (reverse lst)
    (if ((eval fun) ele)
      (setq l1 (cons ele l1))
      (setq l2 (cons ele l2))
    )
  )
  (list l1 l2)
)

(divideList '(1 2 3 4 5 6 7 8 9) '(lambda (x) (zerop (rem x 3)))) -> ((3 6 9) (1 2 4 5 7 8 ))
(divideList '(3 -9 2 6 -8 -4) 'minusp) -> ((-9 -8 -4) (3 2 6))
Speaking English as a French Frog

Lee Mac

  • Seagull
  • Posts: 12929
  • London, England
Re: Divide List
« Reply #7 on: June 06, 2009, 11:45:50 AM »
Perfect Gile  8-)

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: Divide List
« Reply #8 on: June 06, 2009, 11:51:13 AM »
Code: [Select]
[color=green];; You're scaring me Gile! We coded nearly the exact same
;; thing, including reversing the list at the point of iteration:[/color]

(defun _SubDivide ( lst foo / a b )
    (foreach item (reverse lst)
        (if (foo item)
            (setq a (cons item a))
            (setq b (cons item b))
        )
    )
    (list a b)
)

Code: [Select]
[color=green];; The performance difference surprised me ...[/color]

(BenchMark
   '(
        (_SubDivide
           '(1 2 3 4 5 6 7 8 9)
            (lambda (x) (zerop (rem x 3)))
        )
        (DivideList
           '(1 2 3 4 5 6 7 8 9)
           '(lambda (x) (zerop (rem x 3)))
        )
    )
)

[color=green];; (_SUBDIVIDE (QUOTE (1 2 3 4 5 6 7 8 ...).....1545 / 6.17 <fastest>
;; (DIVIDELIST (QUOTE (1 2 3 4 5 6 7 8 ...).....9532 / 1.00 <slowest>
;;
;; !![/color]
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

Lee Mac

  • Seagull
  • Posts: 12929
  • London, England
Re: Divide List
« Reply #9 on: June 06, 2009, 11:56:18 AM »
Michael, what is this indicating?

Code: [Select]
1545 / 6.17

gile

  • Gator
  • Posts: 2520
  • Marseille, France
Re: Divide List
« Reply #10 on: June 06, 2009, 12:11:27 PM »
MP,
During a challenge on a French web site, we had the similar benchmark results with ElpanovEvgeniy. He used the following trick to avoid evaluating 'eval' at each iteration and keep the quoted form in the function calling.
I forgot to implement it :oops:

Code: [Select]
(defun divideList (lst fun / l1 l2)
(setq fun (eval fun))
  (foreach ele (reverse lst)
    (if (fun ele)
      (setq l1 (cons ele l1))
      (setq l2 (cons ele l2))
    )
  )
  (list l1 l2)
)

Speaking English as a French Frog

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: Divide List
« Reply #11 on: June 06, 2009, 12:59:52 PM »
Michael, what is this indicating?

Code: [Select]
1545 / 6.17

Elapsed milliseconds / relative speed for 16384 iteration(s)

:)
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: Divide List
« Reply #12 on: June 06, 2009, 01:01:46 PM »
MP,
During a challenge on a French web site, we had the similar benchmark results with ElpanovEvgeniy. He used the following trick to avoid evaluating 'eval' at each iteration and keep the quoted form in the function calling.

I've done the same, and posted somewhere here at the swamp if I'm not mistaken, I'll see if I can find it. You have an excellent memory Gile, tho Evgeniy's (and yours) code tends to be that way. :)
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: Divide List
« Reply #13 on: June 06, 2009, 01:13:34 PM »
I've done the same, and posted somewhere here at the swamp if I'm not mistaken, I'll see if I can find it.

See this post. Check the foop argument, which is a "foo predicate" function. If it's a list it's assumed to be passed in the quoted form '(lambda (x) ...) and a "pre" eval is performed, otherwise it's assumed to be passed in unquoted form (lambda (x) ...) and left as is. I generally use the latter form for my code because it's more concise and assumed it would perform better; didn't realize how much better until today.

:)
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

Lee Mac

  • Seagull
  • Posts: 12929
  • London, England
Re: Divide List
« Reply #14 on: June 06, 2009, 02:12:47 PM »
Michael, could the speed not be improved by adding a simple:

Code: [Select]
(DivideList
           '(1 2 3 4 5 6 7 8 9)
           [color=red](function[/color] (lambda (x) (zerop (rem x 3))))
        )
    )