• Re: Homework question: LOOP

    From B. Pym@21:1/5 to Kenny Tilton on Thu Sep 12 10:12:05 2024
    XPost: comp.lang.scheme

    Kenny Tilton wrote:

    (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)))

    Peter Seibel wrote:

    Ah, I missed that bit in the maze of twisty, recursive passages, all
    alike. How about this bit of double loop delight:

    (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)))))

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to B. Pym on Thu Sep 12 13:01:49 2024
    XPost: comp.lang.scheme

    On 2024-09-12, B. Pym <Nobody447095@here-nor-there.org> wrote:
    Kenny Tilton wrote:

    (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)))

    Peter Seibel wrote:

    Ah, I missed that bit in the maze of twisty, recursive passages, all
    alike. How about this bit of double loop delight:

    (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.

    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 '()))
    ^^^^^^

    It would behoove you to do.

    (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: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)