* RE: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-25 12:05 ` Michael Heerdegen
@ 2022-10-25 15:59 ` Drew Adams
2022-10-25 17:44 ` Emanuel Berg
0 siblings, 1 reply; 25+ messages in thread
From: Drew Adams @ 2022-10-25 15:59 UTC (permalink / raw)
To: Michael Heerdegen, help-gnu-emacs@gnu.org
> > I would like to test whether a list contains
> > at least one non-nil element?
>
> I'd use `cl-some': (cl-some #'identity my-list).
> There are lots of other possible solutions, like
> using a simple `while' loop or a `dolist' with
> `catch' and `throw'.
IOW:
(setq toto '(nil nil 3 nil nil))
(defun foo (xs)
(while (and (not (car xs)) xs)
(setq xs (cdr xs)))
(car xs))
(defun bar (xs)
(catch 'bar
(dolist (x xs) (when x (throw 'bar t)))
nil))
(foo toto)
(bar toto)
Or just:
(cl-member-if-not #'null toto)
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-25 15:59 ` [External] : " Drew Adams
@ 2022-10-25 17:44 ` Emanuel Berg
2022-10-26 15:39 ` Drew Adams
0 siblings, 1 reply; 25+ messages in thread
From: Emanuel Berg @ 2022-10-25 17:44 UTC (permalink / raw)
To: help-gnu-emacs
Drew Adams wrote:
> (cl-member-if-not #'null toto)
Close
(cl-member-if-not #'null '(nil nil)) ; nil
(cl-member-if-not #'null '(nil 2)) ; (2)
But
(cl-position-if-not #'null '(nil nil)) ; nil
(cl-position-if-not #'null '(nil 2)) ; 1
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-25 19:44 ` Heime
@ 2022-10-25 20:15 ` Drew Adams
2022-10-25 20:19 ` Joost Kremers
0 siblings, 1 reply; 25+ messages in thread
From: Drew Adams @ 2022-10-25 20:15 UTC (permalink / raw)
To: Heime, Joost Kremers; +Cc: help-gnu-emacs@gnu.org
> > > I would like to test whether a list contains at least one
> > > non-nil element? If I do that, then I would be able to
> > > continue a loop where a non-nil element would mean
> > > that a match was found.
> >
> > (seq-some (lambda (e) (not (null e))) mylist)
> >
> > or shorter:
> >
> > (not (seq-every-p #'null mylist))
>
> How does your implementation (not (seq-every-p #'null mylist)) compared to
>
> (elt (delq nil mylist) 0)
`delq' is a "destructive" operation. It can, and
generally does, modify list structure. Is that what
you intend? That's something other than just doing
a "test whether a list contains at least one non-nil
element."
Be very careful and know what you're doing, and why,
if you start writing code that modifies list structure.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-25 20:15 ` [External] : " Drew Adams
@ 2022-10-25 20:19 ` Joost Kremers
2022-10-26 3:00 ` Stefan Monnier via Users list for the GNU Emacs text editor
0 siblings, 1 reply; 25+ messages in thread
From: Joost Kremers @ 2022-10-25 20:19 UTC (permalink / raw)
To: Drew Adams; +Cc: Heime, help-gnu-emacs
On Tue, Oct 25 2022, Drew Adams wrote:
>> > > I would like to test whether a list contains at least one
>> > > non-nil element? If I do that, then I would be able to
>> > > continue a loop where a non-nil element would mean
>> > > that a match was found.
>> >
>> > (seq-some (lambda (e) (not (null e))) mylist)
>> >
>> > or shorter:
>> >
>> > (not (seq-every-p #'null mylist))
>>
>> How does your implementation (not (seq-every-p #'null mylist)) compared to
>>
>> (elt (delq nil mylist) 0)
>
> `delq' is a "destructive" operation.
Oops, I didn't realise that when I wrote my reply... Should've checked...
So hopefully this gives a better idea:
```
(benchmark 100000 '(let ((mylist (list nil nil nil nil nil nil nil nil nil t)))
(elt (delq nil mylist) 0)))
"Elapsed time: 0.493742s (0.379511s in 2 GCs)"
(benchmark 100000 '(let ((mylist (list nil nil nil nil nil nil nil nil nil t)))
(not (seq-every-p #'null mylist))))
"Elapsed time: 0.923503s (0.566131s in 3 GCs)"
(benchmark 100000 '(let ((mylist (list nil t nil nil nil nil nil nil nil nil t)))
(elt (delq nil mylist) 0)))
"Elapsed time: 0.496120s (0.377506s in 2 GCs)"
(benchmark 100000 '(let ((mylist (list nil t nil nil nil nil nil nil nil nil t)))
(not (seq-every-p #'null mylist))))
"Elapsed time: 0.823751s (0.566227s in 3 GCs)"
```
--
Joost Kremers
Life has its moments
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-25 20:19 ` Joost Kremers
@ 2022-10-26 3:00 ` Stefan Monnier via Users list for the GNU Emacs text editor
0 siblings, 0 replies; 25+ messages in thread
From: Stefan Monnier via Users list for the GNU Emacs text editor @ 2022-10-26 3:00 UTC (permalink / raw)
To: help-gnu-emacs
> (benchmark 100000 '(let ((mylist (list nil nil nil nil nil nil nil nil nil t)))
> (elt (delq nil mylist) 0)))
The `elt` call is unnecessary.
And to avoid the destructiveness of `delq` you can use `remq`.
Stefan
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-25 17:44 ` Emanuel Berg
@ 2022-10-26 15:39 ` Drew Adams
2022-10-26 17:43 ` Emanuel Berg
0 siblings, 1 reply; 25+ messages in thread
From: Drew Adams @ 2022-10-26 15:39 UTC (permalink / raw)
To: Emanuel Berg, help-gnu-emacs@gnu.org
> > (cl-member-if-not #'null toto)
>
> Close
> (cl-member-if-not #'null '(nil nil)) ; nil
> (cl-member-if-not #'null '(nil 2)) ; (2)
>
> But
> (cl-position-if-not #'null '(nil nil)) ; nil
> (cl-position-if-not #'null '(nil 2)) ; 1
The request was for a test of whether the input
list contains any non-nil elements. Yes, both
`cl-member-if-not' and `cl-position-if-not' can
do that. Or `cl-member-if' and `cl-position-if',
passing #'identity.
There are lots of ways to do it.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-26 15:39 ` Drew Adams
@ 2022-10-26 17:43 ` Emanuel Berg
0 siblings, 0 replies; 25+ messages in thread
From: Emanuel Berg @ 2022-10-26 17:43 UTC (permalink / raw)
To: help-gnu-emacs
Drew Adams wrote:
>>> (cl-member-if-not #'null toto)
>>
>> Close
>> (cl-member-if-not #'null '(nil nil)) ; nil
>> (cl-member-if-not #'null '(nil 2)) ; (2)
>>
>> But
>> (cl-position-if-not #'null '(nil nil)) ; nil
>> (cl-position-if-not #'null '(nil 2)) ; 1
>
> The request was for a test of whether the input
> list contains any non-nil elements.
Right, I thought the question was identify the spot and loop
the rest of the list from there, and the OP said something to
that extend, but actually it's unclear what solution is best
even then since one can iterate the rest of the list with
`member' as well, and maybe that's even more idiomatic than to
rely on an index to identify the starting point come think
of it.
Okay I was wrong ...
https://www.youtube.com/watch?v=6WOYnv59Bi8
> Yes, both `cl-member-if-not' and `cl-position-if-not' can do
> that. Or `cl-member-if' and `cl-position-if', passing
> #'identity.
>
> There are lots of ways to do it.
That's a good thing, but which way is the best way?
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-26 18:56 ` Jean Louis
@ 2022-10-27 3:54 ` Drew Adams
2022-10-27 4:53 ` Jean Louis
0 siblings, 1 reply; 25+ messages in thread
From: Drew Adams @ 2022-10-27 3:54 UTC (permalink / raw)
To: Jean Louis, help-gnu-emacs@gnu.org
> (defun list-has-non-nil-p (list)
> "Test if list has non nil element."
> (elt (remq nil list) 0))
`remq' can end up needlessly traversing and
copying quite a lot of the argument LIST.
(defun remq (elt list)
(while (and (eq elt (car list))
(setq list (cdr list))))
;; Hey, we're done already. Empty list
;; means no non-nil elements. Nonempty
;; means the car is non-nil.
;;
;; Why do the rest of this?
;; Search for a nil, after the non-nil car?
;; Then copy the entire list. Then delete
;; all nils from it? Why on earth do this?
(if (memq elt list)
^^^^
(delq elt (copy-sequence list))
^^^^^^^^^^^^^
^^^^^^^^^
list))
First that removes all nils from the beginning
of LIST. OK. At that point either the list
is empty, and the answer is nil, or the first
list element is non-nil. Just stop there.
Why go on, with the remainder of the list,
whose car is non-nil, searching for the next
nil?
Why then copy that entire remaining list and
then traverse it to delete all nils from it?
None of that makes sense. Talk about "bloat"?
You're using the wrong function to do the job.
If you have a tiny list, or one where you
don't expect any non-nil elements before a
long way into the list, then maybe such wasted
effort isn't a concern. Otherwise, it might
be. And it's not needed.
Functions others (including I) sent for this
question don't have any of those problems.
They just traverse the list till they find a
non-nil value. At worst, they traverse the
whole list once.
You certainly don't need to remove all nils
from the list. If your list is 100,000,000
elements long and the first element is t, why
would you want to copy the entire list and
then remove all the nils from it? Testing
the first element tells you the answer.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-27 3:54 ` [External] : " Drew Adams
@ 2022-10-27 4:53 ` Jean Louis
2022-10-27 5:30 ` Emanuel Berg
2022-10-27 15:54 ` Drew Adams
0 siblings, 2 replies; 25+ messages in thread
From: Jean Louis @ 2022-10-27 4:53 UTC (permalink / raw)
To: Drew Adams; +Cc: help-gnu-emacs@gnu.org
* Drew Adams <drew.adams@oracle.com> [2022-10-27 06:54]:
> ;; Why do the rest of this?
> ;; Search for a nil, after the non-nil car?
> ;; Then copy the entire list. Then delete
> ;; all nils from it? Why on earth do this?
> (if (memq elt list)
> ^^^^
> (delq elt (copy-sequence list))
> ^^^^^^^^^^^^^
> ^^^^^^^^^
> list))
I get your reasoning. Maybe my function was not named correctly and
then your logic jumps in.
> You certainly don't need to remove all nils
> from the list. If your list is 100,000,000
> elements long and the first element is t, why
> would you want to copy the entire list and
> then remove all the nils from it? Testing
> the first element tells you the answer.
I get the reasoning, you are right, though maybe not in the context of
testing if list has at least one non nil element. I am not sure, you
know is not easy to grasp all.
What I know is that by testing the first element does not tell the
answer if list has any non nil element:
(let ((my-list '(nil nil nil nil)))
(car my-list)) ⇒ nil
(let ((my-list '(nil nil nil nil 1)))
(car my-list)) ⇒ nil
So testing the first element did not give me the answer that
my-list has one non-nil element.
Maybe you can show in Emacs Lisp practically how do you mean it.
--
Jean
Take action in Free Software Foundation campaigns:
https://www.fsf.org/campaigns
In support of Richard M. Stallman
https://stallmansupport.org/
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-27 4:53 ` Jean Louis
@ 2022-10-27 5:30 ` Emanuel Berg
2022-10-27 15:54 ` Drew Adams
1 sibling, 0 replies; 25+ messages in thread
From: Emanuel Berg @ 2022-10-27 5:30 UTC (permalink / raw)
To: help-gnu-emacs
Jean Louis wrote:
>> You certainly don't need to remove all nils from the list.
>> If your list is 100,000,000 elements long and the first
>> element is t, why would you want to copy the entire list
>> and then remove all the nils from it? Testing the first
>> element tells you the answer.
>
> [...] What I know is that by testing the first element does
> not tell the answer if list has any non nil element:
>
> (let ((my-list '(nil nil nil nil)))
> (car my-list)) ⇒ nil
>
> (let ((my-list '(nil nil nil nil 1)))
> (car my-list)) ⇒ nil
>
> So testing the first element did not give me the answer that
> my-list has one non-nil element.
He means that case shows how poor that solution would be,
which means it's just not a good solution.
There are several ways to search a list for a specific element
but they rely on sorting the list first and if that isn't
desired is there really any other way than to examine the
items one by one?
In that case it's the Linear Search algorithm which is at
worst O(n), i.e. the whole list has to be searched until the
element is found at the last element or not found at all.
OTOH if sorting is done then one does Quick Sort first, which
is at worst O(n^2), then Binary Search on the result which is
at worst O(log n), so combined they are, at worst, worse than
O(n^2) or in other words, much worse than Linear Search at
O(n).
However if the data is *kept* sorted after the sorting and
insertions are made in a way that will uphold the order, that
means Binary Search can be used directly, that means we are at
a much better O(log n) again compared to Linear Search at
O(n) ...
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-27 4:53 ` Jean Louis
2022-10-27 5:30 ` Emanuel Berg
@ 2022-10-27 15:54 ` Drew Adams
2022-10-27 20:38 ` Jean Louis
1 sibling, 1 reply; 25+ messages in thread
From: Drew Adams @ 2022-10-27 15:54 UTC (permalink / raw)
To: Jean Louis; +Cc: help-gnu-emacs@gnu.org
> > ;; Why do the rest of this?
> > ;; Search for a nil, after the non-nil car?
> > ;; Then copy the entire list. Then delete
> > ;; all nils from it? Why on earth do this?
> > (if (memq elt list)
> > ^^^^
> > (delq elt (copy-sequence list))
> > ^^^^^^^^^^^^^
> > ^^^^^^^^^
> > list))
>
> I get your reasoning. Maybe my function was not
> named correctly and then your logic jumps in.
>
> > You certainly don't need to remove all nils
> > from the list. If your list is 100,000,000
> > elements long and the first element is t, why
^
IF
> > would you want to copy the entire list and
> > then remove all the nils from it? Testing
> > the first element tells you the answer.
>
> I get the reasoning, you are right, though maybe not in the context of
> testing if list has at least one non nil element. I am not sure, you
> know is not easy to grasp all.
>
> What I know is that by testing the first element does not tell the
> answer if list has any non nil element:...
> So testing the first element did not give me the answer that
> my-list has one non-nil element.
No one said it did. Please reread what I wrote.
In the worst case for your code, the first
element IS non-nil and you spend forever doing
useless stuff. In no case is your code as
efficient as just testing each list element,
starting at the beginning, and STOPPING as
soon as you find a non-nil element.
> Maybe you can show in Emacs Lisp practically how do you mean it.
I think I did. Please reread. Try your code.
Try it in the debugger. Try the code that
others have sent. Look at that code: it just
tests till it finds a non-nil element, then
stops - that's the point. And no list copying.
It's not important perhaps, if your use case
doesn't care about such things. But it's not
a bad idea to look at the code that defines
functions you use - in this case `remq', to
see how it behaves. That's the real point
here.
And yes, the `cl-*' functions can be, and
typically are, implemented in a performant
way.
If you find an Emacs implementation of a CL
function that's not as good as it could be,
please report that.
Emacs's `cl-*' emulation is not a real,
performant CL implementation, nor does it
pretend to be. But in some cases it could
perhaps be improved.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-27 15:54 ` Drew Adams
@ 2022-10-27 20:38 ` Jean Louis
2022-10-28 0:22 ` Emanuel Berg
2022-10-28 4:48 ` tomas
0 siblings, 2 replies; 25+ messages in thread
From: Jean Louis @ 2022-10-27 20:38 UTC (permalink / raw)
To: Drew Adams; +Cc: help-gnu-emacs@gnu.org
* Drew Adams <drew.adams@oracle.com> [2022-10-27 18:54]:
> In the worst case for your code, the first element IS non-nil and
> you spend forever doing useless stuff. In no case is your code as
> efficient as just testing each list element, starting at the
> beginning, and STOPPING as soon as you find a non-nil element.
Of course I agree with the thought in general.
Is it this below?
(defun check-if-any-elt-is-non-nil (list)
(let (there-is)
(while (and list (not there-is))
(when (car list) (setq there-is t))
(setq list (cdr list)))
there-is))
(check-if-any-elt-is-non-nil '(nil nil nil nil nil nil nil nil 1 nil)) ⇒ t
(check-if-any-elt-is-non-nil '(nil nil nil nil nil nil nil nil nil)) ⇒ nil
(benchmark 10000000 (check-if-any-elt-is-non-nil (append (list 1) (make-list 1000000 nil)))) ⇒ "Elapsed time: 0.771948s"
(benchmark 10000000 (check-if-any-elt-is-non-nil (append (make-list 1000000 nil) (list 1) ))) ⇒ "Elapsed time: 0.744323s"
--
Jean
Take action in Free Software Foundation campaigns:
https://www.fsf.org/campaigns
In support of Richard M. Stallman
https://stallmansupport.org/
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-27 20:38 ` Jean Louis
@ 2022-10-28 0:22 ` Emanuel Berg
2022-10-28 4:48 ` tomas
1 sibling, 0 replies; 25+ messages in thread
From: Emanuel Berg @ 2022-10-28 0:22 UTC (permalink / raw)
To: help-gnu-emacs
Jean Louis wrote:
> (defun check-if-any-elt-is-non-nil (list)
> (let (there-is)
> (while (and list (not there-is))
> (when (car list) (setq there-is t))
> (setq list (cdr list)))
> there-is))
(require 'cl-lib)
(defun has-non-nil (lst)
(cl-loop for e in lst
until e
finally return e) )
(benchmark 10000000 (has-non-nil (append (list 1) (make-list 1000000 nil)))) ; "Elapsed time: 0.330502s"
(benchmark 10000000 (has-non-nil (append (make-list 1000000 nil)))) ; "Elapsed time: 0.570150s"
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-27 20:38 ` Jean Louis
2022-10-28 0:22 ` Emanuel Berg
@ 2022-10-28 4:48 ` tomas
2022-10-28 5:19 ` Jean Louis
` (2 more replies)
1 sibling, 3 replies; 25+ messages in thread
From: tomas @ 2022-10-28 4:48 UTC (permalink / raw)
To: help-gnu-emacs; +Cc: Drew Adams
[-- Attachment #1: Type: text/plain, Size: 981 bytes --]
On Thu, Oct 27, 2022 at 11:38:57PM +0300, Jean Louis wrote:
> * Drew Adams <drew.adams@oracle.com> [2022-10-27 18:54]:
> > In the worst case for your code, the first element IS non-nil and
> > you spend forever doing useless stuff. In no case is your code as
> > efficient as just testing each list element, starting at the
> > beginning, and STOPPING as soon as you find a non-nil element.
>
> Of course I agree with the thought in general.
>
> Is it this below?
>
> (defun check-if-any-elt-is-non-nil (list)
> (let (there-is)
> (while (and list (not there-is))
> (when (car list) (setq there-is t))
> (setq list (cdr list)))
> there-is))
If you are doing it "by hand", why not indulge in Lisp's
"classic elegance", like so:
(defun has-non-nil (lst)
(cond
((null lst) nil)
((consp lst) (or (not (null (car lst))) (has-non-nil (cdr lst))))
(t (error "Not a proper list! You cheater!"))))
Cheers
--
t
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [External] : Re: Testing whether a list contains at least one non-nil element
@ 2022-10-28 5:07 Drew Adams
0 siblings, 0 replies; 25+ messages in thread
From: Drew Adams @ 2022-10-28 5:07 UTC (permalink / raw)
To: Jean Louis; +Cc: help-gnu-emacs@gnu.org
> (defun check-if-any-elt-is-non-nil (list)
> (let (there-is)
> (while (and list (not there-is))
> (when (car list) (setq there-is t))
> (setq list (cdr list)))
> there-is))
Sure, that works. That's the same as one of
the functions I sent without bothering with
the `let' variable:
(defun foo (xs)
(while (and (not (car xs)) xs)
(setq xs (cdr xs)))
(car xs))
> (benchmark 10000000 (check-if-any-elt-is-non-nil (append (list 1) (make-
> list 1000000 nil)))) ⇒ "Elapsed time: 0.771948s"
>
> (benchmark 10000000 (check-if-any-elt-is-non-nil (append (make-list
> 1000000 nil) (list 1) ))) ⇒ "Elapsed time: 0.744323s"
I don't see that - at all. Did you really not
quote the second arg to `benchmark'? Anyway,
take the list creations out of your timings.
___
You might compare the times needed to create
your two lists. `append' is not symmetric.
Dunno just how `append' is implemented in C.
But you can see from a recursive Lisp
definition how different the two list args
are treated:
(defun apnd (xs ys)
(if (null xs)
ys
(cons (car xs) (apnd (cdr xs) ys))))
It cdrs down the first list, recursing, but
just conses onto the second list. So it
takes longer with a long first list and a
short second one than with the args reversed.
It traverses only the first of its list args.
Things like this are good to keep in mind.
Compare by hand (apnd nil '(1 2 3)) with
(apnd '(1 2 3) nil). The first is done as
soon as it starts - no recursion at all. The
second recurses once for each element of the
first list: (cons 1 (cons 2 (cons 3 nil)))
(benchmark 1000 '(append (list 1) (make-list 10000 nil)))
"Elapsed time: 2.032493s (1.942879s in 45 GCs)"
(benchmark 1000 '(append (make-list 10000 nil) (list 1)))
"Elapsed time: 4.165245s (3.944074s in 91 GCs)"
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-28 4:48 ` tomas
@ 2022-10-28 5:19 ` Jean Louis
2022-10-28 6:20 ` Michael Heerdegen
2022-10-29 9:19 ` Emanuel Berg
2 siblings, 0 replies; 25+ messages in thread
From: Jean Louis @ 2022-10-28 5:19 UTC (permalink / raw)
To: tomas; +Cc: help-gnu-emacs, Drew Adams
* tomas@tuxteam.de <tomas@tuxteam.de> [2022-10-28 07:50]:
> On Thu, Oct 27, 2022 at 11:38:57PM +0300, Jean Louis wrote:
> > * Drew Adams <drew.adams@oracle.com> [2022-10-27 18:54]:
> > > In the worst case for your code, the first element IS non-nil and
> > > you spend forever doing useless stuff. In no case is your code as
> > > efficient as just testing each list element, starting at the
> > > beginning, and STOPPING as soon as you find a non-nil element.
> >
> > Of course I agree with the thought in general.
> >
> > Is it this below?
> >
> > (defun check-if-any-elt-is-non-nil (list)
> > (let (there-is)
> > (while (and list (not there-is))
> > (when (car list) (setq there-is t))
> > (setq list (cdr list)))
> > there-is))
>
> If you are doing it "by hand", why not indulge in Lisp's
> "classic elegance", like so:
>
> (defun has-non-nil (lst)
> (cond
> ((null lst) nil)
> ((consp lst) (or (not (null (car lst))) (has-non-nil (cdr lst))))
> (t (error "Not a proper list! You cheater!"))))
✔️ Beautiful
--
Jean
Take action in Free Software Foundation campaigns:
https://www.fsf.org/campaigns
In support of Richard M. Stallman
https://stallmansupport.org/
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-28 4:48 ` tomas
2022-10-28 5:19 ` Jean Louis
@ 2022-10-28 6:20 ` Michael Heerdegen
2022-10-28 13:09 ` Stefan Monnier via Users list for the GNU Emacs text editor
2022-10-29 9:20 ` Emanuel Berg
2022-10-29 9:19 ` Emanuel Berg
2 siblings, 2 replies; 25+ messages in thread
From: Michael Heerdegen @ 2022-10-28 6:20 UTC (permalink / raw)
To: help-gnu-emacs
<tomas@tuxteam.de> writes:
> If you are doing it "by hand", why not indulge in Lisp's
> "classic elegance", like so:
>
> (defun has-non-nil (lst)
> (cond
> ((null lst) nil)
> ((consp lst) (or (not (null (car lst))) (has-non-nil (cdr lst))))
> (t (error "Not a proper list! You cheater!"))))
You don't want to implement such functions in Emacs with recursion
because you'll easily hit `max-lisp-eval-depth' when they are called.
Sorry to tell you, but a loop is the preferable way in Elisp.
Michael.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-28 6:20 ` Michael Heerdegen
@ 2022-10-28 13:09 ` Stefan Monnier via Users list for the GNU Emacs text editor
2022-10-29 6:10 ` Michael Heerdegen
` (2 more replies)
2022-10-29 9:20 ` Emanuel Berg
1 sibling, 3 replies; 25+ messages in thread
From: Stefan Monnier via Users list for the GNU Emacs text editor @ 2022-10-28 13:09 UTC (permalink / raw)
To: help-gnu-emacs
Michael Heerdegen [2022-10-28 08:20:45] wrote:
> <tomas@tuxteam.de> writes:
>> If you are doing it "by hand", why not indulge in Lisp's
>> "classic elegance", like so:
>>
>> (defun has-non-nil (lst)
>> (cond
>> ((null lst) nil)
>> ((consp lst) (or (not (null (car lst))) (has-non-nil (cdr lst))))
>> (t (error "Not a proper list! You cheater!"))))
>
> You don't want to implement such functions in Emacs with recursion
> because you'll easily hit `max-lisp-eval-depth' when they are called.
Indeed (and it's significantly slower than the corresponding loop
without function calls).
> Sorry to tell you, but a loop is the preferable way in Elisp.
Luckily, since Emacs-28 you can have your cake and eat it too:
(defun has-non-nil (lst)
(named-let loop ((lst lst))
(cond
((null lst) nil)
((consp lst) (or (not (null (car lst))) (loop (cdr lst))))
(t (error "Not a proper list! You cheater!")))))
Admittedly, here the `named-let` construct gives you only the
elimination of tail-recursion, but in many other circumstances it
also leads to quite elegant code.
Stefan
PS: For the curious, here's what the above `named-let` expands to:
(defalias 'has-non-nil
#'(lambda (lst)
(let ((lst lst))
(let (retval)
(while
(let ((lst lst))
(cond
((null lst) nil)
((consp lst) (let ((val (not (null (car lst)))))
(if val (progn (setq retval val) nil)
(progn (setq lst (cdr lst)) :recurse))))
(t (progn (setq retval (error "Not a proper list! You cheater!"))
nil)))))
retval))))
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-28 13:09 ` Stefan Monnier via Users list for the GNU Emacs text editor
@ 2022-10-29 6:10 ` Michael Heerdegen
2022-10-29 15:25 ` Stefan Monnier via Users list for the GNU Emacs text editor
2022-10-29 6:38 ` tomas
2022-10-30 12:48 ` Emanuel Berg
2 siblings, 1 reply; 25+ messages in thread
From: Michael Heerdegen @ 2022-10-29 6:10 UTC (permalink / raw)
To: help-gnu-emacs
Stefan Monnier via Users list for the GNU Emacs text editor
<help-gnu-emacs@gnu.org> writes:
> PS: For the curious, here's what the above `named-let` expands to:
>
> (defalias 'has-non-nil
> #'(lambda (lst)
> (let ((lst lst))
> (let (retval)
> (while
> (let ((lst lst))
> (cond
> ((null lst) nil)
> ((consp lst) (let ((val (not (null (car lst)))))
> (if val (progn (setq retval val) nil)
> (progn (setq lst (cdr lst)) :recurse))))
> (t (progn (setq retval (error "Not a proper list! You cheater!"))
> nil)))))
> retval))))
Is that :recurse symbol only there to improve readability, or is it
handled specially?
Michael.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-28 13:09 ` Stefan Monnier via Users list for the GNU Emacs text editor
2022-10-29 6:10 ` Michael Heerdegen
@ 2022-10-29 6:38 ` tomas
2022-10-30 12:48 ` Emanuel Berg
2 siblings, 0 replies; 25+ messages in thread
From: tomas @ 2022-10-29 6:38 UTC (permalink / raw)
To: help-gnu-emacs
[-- Attachment #1: Type: text/plain, Size: 1449 bytes --]
On Fri, Oct 28, 2022 at 09:09:12AM -0400, Stefan Monnier via Users list for the GNU Emacs text editor wrote:
> Michael Heerdegen [2022-10-28 08:20:45] wrote:
> > <tomas@tuxteam.de> writes:
> >> If you are doing it "by hand", why not indulge in Lisp's
> >> "classic elegance", like so:
[...]
> > You don't want to implement such functions in Emacs with recursion
> > because you'll easily hit `max-lisp-eval-depth' when they are called.
>
> Indeed (and it's significantly slower than the corresponding loop
> without function calls).
I /knew/ I was missing something. The back of my brain mumbled
"does Emacs have tail recursion elimination or doesn't it?"
> > Sorry to tell you, but a loop is the preferable way in Elisp.
>
> Luckily, since Emacs-28 you can have your cake and eat it too:
>
> (defun has-non-nil (lst)
> (named-let loop ((lst lst))
> (cond
> ((null lst) nil)
> ((consp lst) (or (not (null (car lst))) (loop (cdr lst))))
> (t (error "Not a proper list! You cheater!")))))
Thanks for that :)
> Admittedly, here the `named-let` construct gives you only the
> elimination of tail-recursion, but in many other circumstances it
> also leads to quite elegant code.
Now I think of it, I saw something flashing past emacs-devel; back
then I must have thought "oh, nice, I've got to play with this..."
Now it sank in. So thanks :)
Cheers
--
t
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-28 4:48 ` tomas
2022-10-28 5:19 ` Jean Louis
2022-10-28 6:20 ` Michael Heerdegen
@ 2022-10-29 9:19 ` Emanuel Berg
2 siblings, 0 replies; 25+ messages in thread
From: Emanuel Berg @ 2022-10-29 9:19 UTC (permalink / raw)
To: help-gnu-emacs
tomas wrote:
> If you are doing it "by hand", why not indulge in Lisp's
> "classic elegance", like so:
>
> (defun has-non-nil (lst)
> (cond
> ((null lst) nil)
> ((consp lst) (or (not (null (car lst))) (has-non-nil (cdr lst))))
> (t (error "Not a proper list! You cheater!"))))
Did you mean: recursion
https://www.google.com/search?q=recursion
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-28 6:20 ` Michael Heerdegen
2022-10-28 13:09 ` Stefan Monnier via Users list for the GNU Emacs text editor
@ 2022-10-29 9:20 ` Emanuel Berg
1 sibling, 0 replies; 25+ messages in thread
From: Emanuel Berg @ 2022-10-29 9:20 UTC (permalink / raw)
To: help-gnu-emacs
Michael Heerdegen wrote:
>> If you are doing it "by hand", why not indulge in Lisp's
>> "classic elegance", like so:
>>
>> (defun has-non-nil (lst)
>> (cond
>> ((null lst) nil)
>> ((consp lst) (or (not (null (car lst))) (has-non-nil (cdr lst))))
>> (t (error "Not a proper list! You cheater!"))))
>
> You don't want to implement such functions in Emacs with
> recursion because you'll easily hit `max-lisp-eval-depth'
> when they are called. Sorry to tell you, but a loop is the
> preferable way in Elisp.
He knows that ...
>
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-29 6:10 ` Michael Heerdegen
@ 2022-10-29 15:25 ` Stefan Monnier via Users list for the GNU Emacs text editor
2022-10-30 12:49 ` Emanuel Berg
0 siblings, 1 reply; 25+ messages in thread
From: Stefan Monnier via Users list for the GNU Emacs text editor @ 2022-10-29 15:25 UTC (permalink / raw)
To: help-gnu-emacs
> Is that :recurse symbol only there to improve readability, or is it
> handled specially?
It's just for readability.
Stefan
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-28 13:09 ` Stefan Monnier via Users list for the GNU Emacs text editor
2022-10-29 6:10 ` Michael Heerdegen
2022-10-29 6:38 ` tomas
@ 2022-10-30 12:48 ` Emanuel Berg
2 siblings, 0 replies; 25+ messages in thread
From: Emanuel Berg @ 2022-10-30 12:48 UTC (permalink / raw)
To: help-gnu-emacs
Stefan Monnier via Users list for the GNU Emacs text editor wrote:
>> You don't want to implement such functions in Emacs with
>> recursion because you'll easily hit `max-lisp-eval-depth'
>> when they are called.
>
> Indeed (and it's significantly slower than the corresponding
> loop without function calls).
Recursion is slower because of the continual function calls
and those will also blow the stack eventually ...
>> Sorry to tell you, but a loop is the preferable way
>> in Elisp.
>
> Luckily, since Emacs-28 you can have your cake and eat it too:
>
> (defun has-non-nil (lst)
> (named-let loop ((lst lst))
> (cond
> ((null lst) nil)
> ((consp lst) (or (not (null (car lst))) (loop (cdr lst))))
> (t (error "Not a proper list! You cheater!")))))
`named-let' is from a "[l]ooping construct taken from Scheme."
I love it that Elisp is like a "Lisp dump" area where
everything from the Lisp world worth having seems to end up
eventually ...
> Admittedly, here the `named-let` construct gives you only
> the elimination of tail-recursion, but in many other
> circumstances it also leads to quite elegant code.
Here tail refers to not the `cdr' of the list but the tail of
the function, "a tail call is a subroutine call performed as
the final action of a procedure. If the target of a tail is
the same subroutine, the subroutine is said to be tail
recursive, which is a special case of direct recursion" [1]
I don't know why that is such an important distinction, I also
remember recursion over trees that recursed both ways from the
top and middle nodes if it/they had several child nodes, so
I guess that recursion was both tail and direct LOL.
Elisp direct recursion example anyone?
And what was this continuation-passing style?
CPS 1975 continuation-passing style, coined in "AI Memo 349"
I used it once, but don't remember what file that was ...
> (defalias 'has-non-nil
> #'(lambda (lst) [...]
TIL: In Scheme-inspired Elisp, quoting lambdas is OK ...
[1] https://en.wikipedia.org/wiki/Tail_call
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [External] : Re: Testing whether a list contains at least one non-nil element
2022-10-29 15:25 ` Stefan Monnier via Users list for the GNU Emacs text editor
@ 2022-10-30 12:49 ` Emanuel Berg
0 siblings, 0 replies; 25+ messages in thread
From: Emanuel Berg @ 2022-10-30 12:49 UTC (permalink / raw)
To: help-gnu-emacs
Stefan Monnier via Users list for the GNU Emacs text editor wrote:
>> Is that :recurse symbol only there to improve readability,
>> or is it handled specially?
>
> It's just for readability.
Interesting ...
--
underground experts united
https://dataswamp.org/~incal
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2022-10-30 12:49 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-10-28 5:07 [External] : Re: Testing whether a list contains at least one non-nil element Drew Adams
-- strict thread matches above, loose matches on Subject: below --
2022-10-25 10:16 Heime
2022-10-25 10:50 ` Jean Louis
2022-10-25 12:12 ` Emanuel Berg
2022-10-26 18:56 ` Jean Louis
2022-10-27 3:54 ` [External] : " Drew Adams
2022-10-27 4:53 ` Jean Louis
2022-10-27 5:30 ` Emanuel Berg
2022-10-27 15:54 ` Drew Adams
2022-10-27 20:38 ` Jean Louis
2022-10-28 0:22 ` Emanuel Berg
2022-10-28 4:48 ` tomas
2022-10-28 5:19 ` Jean Louis
2022-10-28 6:20 ` Michael Heerdegen
2022-10-28 13:09 ` Stefan Monnier via Users list for the GNU Emacs text editor
2022-10-29 6:10 ` Michael Heerdegen
2022-10-29 15:25 ` Stefan Monnier via Users list for the GNU Emacs text editor
2022-10-30 12:49 ` Emanuel Berg
2022-10-29 6:38 ` tomas
2022-10-30 12:48 ` Emanuel Berg
2022-10-29 9:20 ` Emanuel Berg
2022-10-29 9:19 ` Emanuel Berg
2022-10-25 12:05 ` Michael Heerdegen
2022-10-25 15:59 ` [External] : " Drew Adams
2022-10-25 17:44 ` Emanuel Berg
2022-10-26 15:39 ` Drew Adams
2022-10-26 17:43 ` Emanuel Berg
2022-10-25 17:25 ` Joost Kremers
2022-10-25 19:44 ` Heime
2022-10-25 20:15 ` [External] : " Drew Adams
2022-10-25 20:19 ` Joost Kremers
2022-10-26 3:00 ` Stefan Monnier via Users list for the GNU Emacs text editor
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.