Discussion:
Homework question: LOOP
(too old to reply)
B. Pym
2024-09-12 10:12:05 UTC
Permalink
(defun prior-sib-if (self list &optional (test-fn #'true-that))
"Find nearest preceding sibling passing TEST-FN"
(labels ((check-priors (sibs)
(if (eql self (first sibs))
nil
(or (check-priors (rest sibs))
(when (funcall test-fn (first sibs))
(first sibs))))))
(check-priors list)))
Ah, I missed that bit in the maze of twisty, recursive passages, all
(defun prior-sib-if (self list &optional (test-fn #'true-that))
"Find nearest preceding sibling passing TEST-FN"
(loop with candidates = nil
for node in list
until (eql node self) do (push node candidates)
finally (return (loop for c in candidates when (funcall test-fn c) retur
n c))))
Gauche Scheme

(use srfi-1) ;; take-while

(define (prior-sib-if self the-list test-fn)
(find test-fn
(reverse (take-while (^x (not (equal? self x))) the-list))))

gosh> (prior-sib-if 8 '(0 2 3 4 5 6 8 2 8) even?)
6
gosh> (prior-sib-if 8 '(0 2 3 4 5 6 8 2 8) odd?)
5

Another way:

(define (prior-sib-if self the-list test-fn)
(let go ((lst the-list) (seen '()))
(if (equal? self (car lst))
(find test-fn seen)
(go (cdr lst) (cons (car lst) seen)))))
Kaz Kylheku
2024-09-12 13:01:49 UTC
Permalink
Post by B. Pym
(defun prior-sib-if (self list &optional (test-fn #'true-that))
"Find nearest preceding sibling passing TEST-FN"
(labels ((check-priors (sibs)
(if (eql self (first sibs))
nil
(or (check-priors (rest sibs))
(when (funcall test-fn (first sibs))
(first sibs))))))
(check-priors list)))
Ah, I missed that bit in the maze of twisty, recursive passages, all
(defun prior-sib-if (self list &optional (test-fn #'true-that))
"Find nearest preceding sibling passing TEST-FN"
(loop with candidates = nil
for node in list
until (eql node self) do (push node candidates)
finally (return (loop for c in candidates when (funcall test-fn c) retur
n c))))
Gauche Scheme
(use srfi-1) ;; take-while
(define (prior-sib-if self the-list test-fn)
(find test-fn
(reverse (take-while (^x (not (equal? self x))) the-list))))
There is no need to accumulate the candidates into a reverse
list which is then searched, like in Seibel's silly solution.

1. Iterate over the input.

a. Stop when you see an item equal to the self object.

b. Or else, whenever you see an item satisfying the predicate,
remember it in the recent match variable.

2. If stopped via 1 (a), return the recent match variable, or else your
not-found indication if nothing was assigned to that variable.
(It could be that the variable is initialized to nil, and nil is the
slightly ambiguous not-found indication.)

Why would you treat every preceding item as a "candidate", whether
it matches the predicate or not? If you only treated predicate-matching
elements as candidates, then the closest one would be (car candidates)
which would immediately inform you: why the heck am I keeping the whole
stack of them, only to end up peeking at the top element?

On top of that, you've ruined the elegance of using push to
get a reverse list.
Post by B. Pym
gosh> (prior-sib-if 8 '(0 2 3 4 5 6 8 2 8) even?)
6
gosh> (prior-sib-if 8 '(0 2 3 4 5 6 8 2 8) odd?)
5
(define (prior-sib-if self the-list test-fn)
(let go ((lst the-list) (seen '()))
^^^^^^

It would behoove you to do.
Post by B. Pym
(if (equal? self (car lst))
(find test-fn seen)
(go (cdr lst) (cons (car lst) seen)))))
This is not equivalent. You're only calling (find test-fn seen)
when the self object has appeared in the list.

When the object does not appear in the list, you hit (car lst)
for an empty list, which blows up in Scheme.

The previous functions return the rightmost predicate-matching
element in the case when the self object has no appeared in the list.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Loading...