Surprising behavior of dolist with break condition.

Q&A's, tips, howto's
Locked
TedWalther
Posts: 608
Joined: Mon Feb 05, 2007 1:04 am
Location: Abbotsford, BC
Contact:

Surprising behavior of dolist with break condition.

Post by TedWalther »

This affects dotimes, dolist, and the rest of the family.

I had a situation where I needed to scan through a list of numbers, and find the list index of the number that is just less than my target number.

Example:

Code: Select all

(set 'foo '(1 5 10 20 100 105))

(dotimes (i (+ -1 (length foo)) (> (foo i) 50)) (foo i))
=> true
;; was expecting return value of 20 (last value before triggering break condition)
With the break condition, the loop always returns true. This isn't what I expect; I expect the last value evaluated inside the form, not the condition test. I want to abort the loop as soon as I find the value I'm looking for. Is this wrong? It affects pretty much all the looping constructs, and was very surprising to me. Without the break condition, the loops return the last value evaluated in the body.

Also, is there a better idiom to do such scanning, some sort of binary search function?

I understand it is useful to know whether the break condition was triggered or not, couldn't that be saved in a system variable $brk the way regex output is? Also, even leaving $idx behind after a form evaluation would be nice too.
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence. Nine months later, they left with a baby named newLISP. The women of the ivory towers wept and wailed. "Abomination!" they cried.

ralph.ronnquist
Posts: 228
Joined: Mon Jun 02, 2014 1:40 am
Location: Melbourne, Australia

Re: Surprising behavior of dolist with break condition.

Post by ralph.ronnquist »

Surprisingly often I also have that need of getting the index for the element prior to the match, and I've zeroed in on a phrase like the following:

Code: Select all

(let (i (find 50 foo >=)) (if (nil? i) (- (length foo) 1) (zero? i) nil (- i 1)))
In that way the looping is within the primitive, and it avoids indexing, which might be slow especially for longer lists.

Regarding the loop return value I think I would agree that it lacks coherence as is. However, as I use those forms only imperatively, that return value is outside my box.

newBert
Posts: 156
Joined: Fri Oct 28, 2005 5:33 pm
Location: France

Re: Surprising behavior of dolist with break condition.

Post by newBert »

Here is another solution :

Code: Select all

(set 'foo '(1 5 10 20 100 105))
(last (ref-all 50 foo > true))   ;-> 20
BertrandnewLISP v.10.7.6 64-bit on Linux (Linux Mint 20.1)

TedWalther
Posts: 608
Joined: Mon Feb 05, 2007 1:04 am
Location: Abbotsford, BC
Contact:

Re: Surprising behavior of dolist with break condition.

Post by TedWalther »

Ralph and NewBert, thank you for those answers. Ralph, that is amazing, I keep forgetting about the "find" function, that was beautifully done.

I ended up going with this before I read your solutions:

Code: Select all

(let (m 0) (while (> jday (lst m)) (++ m))))
In my application I didn't need a nil for no match, because I had a guarantee that the 0 index always pointed to a value that was less than the value I was testing.
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence. Nine months later, they left with a baby named newLISP. The women of the ivory towers wept and wailed. "Abomination!" they cried.

Locked