TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: EWCAD on May 22, 2023, 03:24:19 PM

Title: Setting a variable based on contents on list.
Post by: EWCAD on May 22, 2023, 03:24:19 PM
I am trying to set a variable to one of the values returned by an intersection function, The issue I'm having is that I need to always set the variable with the item that has the highest Y value. Now to be honest I can think of a few ways I could do this but I'm certain there has to be a better way than say, setting a variable for each member of the list and comparing them then setting the final variable to the one with the highest value... I'm hoping maybe I can filter through the resulting list before any variables are set, maybe with a foreach? I'm still learning the ins and outs of list manipulation so any help is appreciated!

for reference I'm feeding 2 selection sets to a function that creates a list of all intersecting points between the 2. first ss is just a 90 degree line and second one is a closed arc polygon. So at most I only end up with 2-3 intersections with the shapes I'm working with, if more context is needed I will happily oblige!

Title: Re: Setting a variable based on contents on list.
Post by: JohnK on May 22, 2023, 03:41:07 PM
Trying to understand; you have a list of numbers and you want to find the entry that is the largest?

Example (I understand you may be talking about a point list, but the principle is the same):
If you have a list of numbers: (1 2 3 4 5 6 7)
you want to find the number 7. correct?

What does your list look like?
(1 2 (3 4) 5 6 (7)) ; nested lists
(1 2 nil 3 4 5 nil 6 7) ; null entries
etc.
Title: Re: Setting a variable based on contents on list.
Post by: EWCAD on May 22, 2023, 03:55:56 PM
Exactly, I really just need to check the Y value of each item and compare them to find the one with the greatest value. Here is a sample of what the returned lists look like, This is 4 intersection points.

((288.252 -56.8312 0.0) (288.252 -57.3313 0.0) (288.252 -57.9562 0.0) (288.252 -58.4563 0.0))

So in this example the point I would want is the last one in the list.
Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 22, 2023, 04:29:50 PM
Perhaps,
from my library :

Code - Auto/Visual Lisp: [Select]
  1. (defun kdub:miny (pointlist)
  2.   (apply (function min) (mapcar (function cadr) pointlist))
  3. )
  4.  


(kdub:miny '((288.252 -56.8312 0.0) (288.252 -57.3313 0.0) (288.252 -57.9562 0.0) (288.252 -58.4563 0.0)))

;;-> -58.4563
Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 22, 2023, 04:32:32 PM

oh, wait, Did you want the axis or the point ??
Title: Re: Setting a variable based on contents on list.
Post by: JohnK on May 22, 2023, 04:48:01 PM
Kerry already has a good framework but here is another approach.

I would try something like:
Code - Auto/Visual Lisp: [Select]
  1. (defun return-if (proced lst sofar)
  2.   (if (null lst)                                                ; -If the list is empty, return what we have.
  3.     sofar
  4.     (return-if                                                  ; -Keep looking through the list...
  5.       proced
  6.       (cdr lst)                                                 ;  Of the next item in the list
  7.       (if (eval (list proced (cadr (car lst)) (cadr sofar)))    ;  if the current item in the list is bigger/smaller/etc.
  8.         (car lst)                                               ;  then set it to the `sofar` procedure.
  9.         sofar))) )                                              ;  else just keep using the `sofar` variable.

Code: [Select]
(setq aList '((288.252 -56.8312 0.0) (288.252 -57.3313 0.0) (288.252 -57.9562 0.0) (288.252 -58.4563 0.0)))
(return-if '< aList (car aList)) ; set `sofar` to the first item in the list
> (288.252000 -58.456300 0.000000)

Study functions and ask questions.
Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 22, 2023, 06:05:20 PM

This may suit ?

Code - Auto/Visual Lisp: [Select]
  1.  
  2. (setq pointlist '( (1 -56.8312 8) (2 -57.3313 7) (3 -57.9562 6) (4 -58.4563 5)))
  3.  
  4. (setq smallest_y
  5.        (car
  6.          (vl-sort pointlist
  7.                   '(lambda (a b) (< (cadr a) (cadr b)))
  8.          )
  9.        )
  10. )
  11.  
  12.  

==> (4 -58.4563 5)
Title: Re: Setting a variable based on contents on list.
Post by: EWCAD on May 22, 2023, 06:24:28 PM
My mistake, I said what I meant but was in a hurry with my reply and pointed out the wrong example. I did in fact mean the Y value that is the largest, but I pointed out the smallest one. However yes, I believe all of the examples above are something I could work in to sort through the data. I am assuming that I can flip the less than to a greater than in the vl-sort and get the desired results. If nothing else, I'll find out lol

Thank you both for your help, I appreciate it!
Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 22, 2023, 06:32:09 PM
>>>  I am assuming that I can flip the less than to a greater than in the vl-sort and get the desired results. If nothing else, I'll find out lol

Yes, that will work.

Stay well
Title: Re: Setting a variable based on contents on list.
Post by: gile on May 23, 2023, 02:09:49 AM
Hi,

Another approch using some functions mimic the F# List.fold, List.reduce, List.minBy, ListmaxBy funtions.
Code - Auto/Visual Lisp: [Select]
  1. ;; gc:fold
  2. ;; Applies a function to each element of the list,
  3. ;; threading an accumulator argument through the computation.
  4. ;;
  5. ;; Arguments
  6. ;; folder: function that updates the state with each element from the sequence.
  7. ;;         If the input function is f and the elements are i0...iN then computes
  8. ;;         (f ... (f s i0) ... iN)
  9. ;; state: initial state.
  10. ;; source: input list.
  11. (defun gc:fold (folder state source / f)
  12.   (setq f (eval folder))
  13.   (foreach n source (setq state (f state n)))
  14. )
  15.  
  16. ;; gc:reduce
  17. ;; Applies a function to each element of the list,
  18. ;; threading an accumulator argument through the computation.
  19. ;; Begin by applying the function to the first two elements.
  20. ;; Then feed this result into the function along with the third element and so on.
  21. ;; Return the final result.
  22. ;;
  23. ;; Arguments
  24. ;; reduction: function that takes in the current accumulated result and the next
  25. ;;            element of the sequence to produce the next accumulated result.
  26. ;; source: input list.
  27. (defun gc:reduce (reduction source)
  28.   (gc:fold reduction (car source) (cdr source))
  29. )
  30.  
  31. ;; gc:minBy
  32. ;; Returns the lowest of all elements of the list,
  33. ;; compared via < operator on the function result.
  34. ;;
  35. ;; Arguments
  36. ;; projection: function to transform items from the input sequence into comparable keys.
  37. ;; source: input list.
  38. (defun gc:minBy (projection source)
  39.   (setq p (eval projection))
  40.   (gc:reduce (function (lambda (a b) (if (< (p b) (p a)) b a))) source)
  41. )
  42.  
  43. ;; gc:maxBy
  44. ;; Returns the greatest of all elements of the list,
  45. ;; compared via > operator on the function result.
  46. ;;
  47. ;; Arguments
  48. ;; projection: function to transform items from the input sequence into comparable keys.
  49. ;; source: input list.
  50. (defun gc:maxBy (projection source)
  51.   (setq p (eval projection))
  52.   (gc:reduce (function (lambda (a b) (if (> (p b) (p a)) b a))) source)
  53. )

Code - Auto/Visual Lisp: [Select]
  1. _$ (gc:minBy 'cadr '((1 -56.8312 8) (2 -57.3313 7) (3 -57.9562 6) (4 -58.4563 5)))
  2. (4 -58.4563 5)
  3. _$ (gc:maxBy 'cadr '((1 -56.8312 8) (2 -57.3313 7) (3 -57.9562 6) (4 -58.4563 5)))
  4. (1 -56.8312 8)

@Kerry, the (function min) and (function cadr) are useless because 'min' and 'cadr' are already compiled function. IOW, the 'function' function is only usefull with 'lambda' expressions.
Title: Re: Setting a variable based on contents on list.
Post by: gile on May 23, 2023, 02:13:54 AM
Without using the upper functions, minBy and maxBy could have been written like this:
Code - Auto/Visual Lisp: [Select]
  1. (defun minBy (projection source / f a)
  2.   (setq f (eval projection)
  3.         a (car source)
  4.   )
  5.   (foreach x (cdr source)
  6.     (if (< (f x) (f a))
  7.       (setq a x)
  8.     )
  9.   )
  10.   a
  11. )
  12.  
  13. (defun maxBy (projection source / f a)
  14.   (setq f (eval projection)
  15.         a (car source)
  16.   )
  17.   (foreach x (cdr source)
  18.     (if (> (f x) (f a))
  19.       (setq a x)
  20.     )
  21.   )
  22.   a
  23. )
Title: Re: Setting a variable based on contents on list.
Post by: BIGAL on May 23, 2023, 02:20:00 AM
Why not just sort the list based on the Y value it can be either min or max depending on use of < or >

Code: [Select]
;x only
(setq lst (vl-sort lst '(lambda (j k) (< (car j)(car k)))))
; Y
;x only
(setq lst (vl-sort lst '(lambda (j k) (< (cadr j)(cadr k)))))
Title: Re: Setting a variable based on contents on list.
Post by: gile on May 23, 2023, 03:01:45 AM
Why not just sort the list based on the Y value it can be either min or max depending on use of < or >

Code: [Select]
;x only
(setq lst (vl-sort lst '(lambda (j k) (< (car j)(car k)))))
; Y
;x only
(setq lst (vl-sort lst '(lambda (j k) (< (cadr j)(cadr k)))))

This is Kerry's proposal on reply #6.

I wanted to show another way with some generic higher order funtions.
Theorically aggregating (folding) a list is cheaper than sorting it because it iterates the list only once.
Title: Re: Setting a variable based on contents on list.
Post by: bruno_vdh on May 23, 2023, 06:00:25 AM
Hi,
My code proposal
(setq lst '((288.252 -56.8312 0.0) (288.252 -57.3313 0.0) (288.252 -57.9562 0.0) (288.252 -58.4563 0.0)))


The dot with the largest Y
Code - Auto/Visual Lisp: [Select]
  1. _$ (foreach p lst (if (< (cadr pmaxY)  (cadr p)) (setq pmaxY p) pmaxY))
  2. (288.252 -56.8312 0.0)

The dot with the smallest Y
Code - Auto/Visual Lisp: [Select]
  1. _$ (foreach p lst (if (< (cadr pminY) (- (cadr p))) (setq pminY p) pminY))
  2. (288.252 -58.4563 0.0)

To make a more generic filtering function
Code - Auto/Visual Lisp: [Select]
  1. (defun filter_rank_size (rank size l / p)
  2.   (setq rank (eval rank)
  3.         size (eval size)
  4.   )
  5.   (foreach x l
  6.     (if (< (rank p) (size (rank x)))
  7.       (setq p x)
  8.       p
  9.     )
  10.   )
  11. )

Search for the largest and smallest Y
Code - Auto/Visual Lisp: [Select]
  1. _$ (filter_rank_size 'cadr '+ lst)
  2. (288.252 -56.8312 0.0)
  3. _$ (filter_rank_size 'cadr '- lst)
  4. (288.252 -58.4563 0.0)

Title: Re: Setting a variable based on contents on list.
Post by: EWCAD on May 23, 2023, 07:29:40 AM
These are all extremely helpful examples, I've just implemented one of the sorting methods above and I appreciate everyone's input! It's great to see so many different methods to get the same result. Again, thank you all for your help!
Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 23, 2023, 04:53:28 PM
Quote from:  @ gilles

@Kerry, the (function min) and (function cadr) are useless because 'min' and 'cadr' are already compiled function. IOW, the 'function' function is only usefull with 'lambda' expressions.

You're correct. The redundant function is wasted, even though it works.  I have some similar methods in my library that I've been using for 20+ years.

That's time I'll never get back   :cry:   :-D

The corrected method in Post Reply #3 would be :

Code - Auto/Visual Lisp: [Select]
  1. // return the smallest Y value from a list of points
  2.  
  3. (defun kdub:miny (pointlist)
  4.   (apply 'min (mapcar 'cadr pointlist))
  5. )
  6.  
  7.  


(setq pointlist '( ( 1 -56.8312 8 ) ( 2 -57.3313 7 ) ( 3 -57.9562 6 ) ( 4 -58.4563 5 )))

(kdub:miny pointlist)

;;==>  -58.4563

Title: Re: Setting a variable based on contents on list.
Post by: bruno_vdh on May 24, 2023, 05:28:21 AM
Hi,

I've just implemented one of the sorting methods above and I appreciate everyone's input!

Based on the following idea:
I wanted to show another way with some generic higher order funtions.
Theorically aggregating (folding) a list is cheaper than sorting it because it iterates the list only once.

And taking up the previous proposals of (gile) on reply #10, It is possible to adapt a generic search function based on the model of the function (VL-sort list comparison-function), without having to sort:
Code - Auto/Visual Lisp: [Select]
  1. (defun wanted (l f / a)
  2.   (setq f (eval f)
  3.         a   (car l)
  4.   )
  5.   (foreach x (cdr l)
  6.     (if (f a x) a (setq a x))
  7.   )
  8. )

Search for the largest and smallest Y
Code: [Select]
_$ (setq lst '((288.252 -56.8312 0.0) (288.252 -57.3313 0.0) (288.252 -57.9562 0.0) (288.252 -58.4563 0.0)))
((288.252 -56.8312 0.0) (288.252 -57.3313 0.0) (288.252 -57.9562 0.0) (288.252 -58.4563 0.0))

_$ (wanted lst (function (lambda (a b) (> (cadr a) (cadr b)))))
(288.252 -56.8312 0.0)
_$ (wanted lst (function (lambda (a b) (< (cadr a) (cadr b)))))
(288.252 -58.4563 0.0)



Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 24, 2023, 05:45:18 PM
For giggles.

Code - Auto/Visual Lisp: [Select]
  1. Benchmarking-V2 :Boundary=500 [M.P.2005 <revised kdub 2005,2014>] .................
  2. Elapsed milliseconds for 16384 iteration(s)/ relative Timing :
  3.  
  4.     (CAR (VL-SORT POINTLIST (QUOTE (LAMB...).....937 / 1.8159 <slowest>: 0.05718994ms Per iteration
  5.     (WANTED POINTLIST (FUNCTION (LAMBDA ...).....797 / 1.5446
  6.     (FOREACH P POINTLIST (IF (< (CADR PM...).....766 / 1.4845
  7.     (FILTER_RANK_SIZE (QUOTE CADR) (QUOT...).....672 / 1.3023
  8.     (GC:MINBY (QUOTE CADR) POINTLIST)............640 / 1.2403
  9.     (MINBY (QUOTE CADR) POINTLIST)...............516 / 1.0000 <fastest>: 0.03149414ms Per iteration
  10.  


Gilles takes the speed prize, again  . . . a bag of black (virtual) jellybeans  :)

Well done Sir !!
Title: Re: Setting a variable based on contents on list.
Post by: JohnK on May 24, 2023, 07:03:04 PM
Did mine make the list?

However, I reserve the right to edit your post/results if mine registers too high up the list--which, considering the company, is highly likely--(putting code up for a speed test against some of you people is like purposely banging your head against a wall).
Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 24, 2023, 07:41:57 PM
Did mine make the list?

However, I reserve the right to edit your post/results if mine registers too high up the list--which, considering the company, is highly likely--(putting code up for a speed test against some of you people is like purposely banging your head against a wall).

Sorry John, I rushed that a bit too much.
I'll add your return-if shortly.
Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 24, 2023, 07:50:18 PM
Ta da da dahhh

Code - Auto/Visual Lisp: [Select]
  1. Benchmarking-V2 :Boundary=500 [M.P.2005 <revised kdub 2005,2014>] .................
  2. Elapsed milliseconds for 16384 iteration(s)/ relative Timing :
  3.  
  4.     (RETURN-IF (QUOTE <) POINTLIST (CAR ...).....1781 / 3.2559 <slowest>: 0.10870361ms Per iteration
  5.     (CAR (VL-SORT POINTLIST (QUOTE (LAMB...)......969 / 1.7715
  6.     (WANTED POINTLIST (FUNCTION (LAMBDA ...)......828 / 1.5137
  7.     (FOREACH P POINTLIST (IF (< (CADR PM...)......781 / 1.4278
  8.     (FILTER_RANK_SIZE (QUOTE CADR) (QUOT...)......704 / 1.2870
  9.     (GC:MINBY (QUOTE CADR) POINTLIST).............687 / 1.2559
  10.     (MINBY (QUOTE CADR) POINTLIST)................547 / 1.0000 <fastest>: 0.03338623ms Per iteration
  11.  


 :wink:
Title: Re: Setting a variable based on contents on list.
Post by: JohnK on May 24, 2023, 08:17:03 PM
See wall. Bang head!

300 times worse!?
Title: Re: Setting a variable based on contents on list.
Post by: It's Alive! on May 24, 2023, 08:18:01 PM
a bag of black (virtual) jellybeans  :)
Title: Re: Setting a variable based on contents on list.
Post by: kdub_nz on May 24, 2023, 08:44:05 PM
See wall. Bang head!

300 times worse!?

No !
3.2559 times as long to run. Still pretty good @  0.10870361ms Per iteration

So stay chilly
Title: Re: Setting a variable based on contents on list.
Post by: bruno_vdh on May 25, 2023, 06:03:36 AM
Hello,

See wall. Bang head!

300 times worse!?

No!
The approach was the right one because you had not sorted
After a slight rewrite:
Code - Auto/Visual Lisp: [Select]
  1. (defun return-if-V2 (proced lst / foo)
  2.   (defun foo (f l a)
  3.     (cond ((null l) a)
  4.           ((f (cadr a) (cadar l)) (foo f (cdr l) a))
  5.           (T (foo f (cdr l) (car l)))
  6.     )
  7.   )  
  8. (foo (eval proced) (cdr lst) (car lst))
  9. )
  10.  
  11. _$ (return-if-V2 '< aList )
  12. (288.252 -58.4563 0.0)
  13.  

Code: [Select]
Benchmarking ...................Elapsed milliseconds / relative speed for 65536 iteration(s):

    (RETURN-IF-V2 (QUOTE <) ALIST)..............1515 / 3.35 <fastest>
    (RETURN-IF (QUOTE <) ALIST (CAR ALIST)).....5079 / 1 <slowest>
Title: Re: Setting a variable based on contents on list.
Post by: VovKa on May 25, 2023, 06:54:00 AM
i would benchmark all functions compiled
and i guess bruno's simple foreach will win