help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How do lisp gurus truncate?


From: Giorgos Keramidas
Subject: Re: How do lisp gurus truncate?
Date: Thu, 23 Jul 2009 20:05:10 +0300
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1.50 (berkeley-unix)

On Thu, 23 Jul 2009 08:32:56 +0200, Lennart Borgman <lennart.borgman@gmail.com> 
wrote:
> I want to truncate an ordered list if the rest of the values are
> bigger than some limit. I just wrote some code like this one to do
> that, but there must be some more standard way of doing that, or?
>
>           (when nxml-where-first-change-pos
>             (setq nxml-where-path 'dummy nxml-where-path)
>             (let ((path nxml-where-path))
>               (while (cdr path)
>                 (when (> (nth 1 (nth 1 path)) nxml-where-first-change-pos)
>                   (setcdr path nil))
>                 (setq path (cdr path))))
>             (setq nxml-where-path (cdr nxml-where-path)))

I'd probably use `every' to perform the test and start iterating over
the list at the first location returned by `member' or `find', i.e. from
a CLISP session:

    [9]> (every (lambda (number)
                  (> number 3))
                (list 4 5 6))
    T

    [10]> (member 3 (list 1 2 3 4 5 6 7))
    (3 4 5 6 7)

    [11]> (every (lambda (number)
                   (> number 3))
            (rest (member 3 (list 1 2 3 4 5 6 7 8 9))))
    T

Locating the first item that is probably easier to do with `find' than
with `member', because it lets you find the first number *larger* than
the value you are looking for, i.e.:

    [25]> (find 3 (list 1 2 3 4 5 6 7) :test #'<)
    4

Since you are looking for the nth-cdr minus 1, you can find the location
of the first item larger than 3 with:

    (let ((mylist (list 1 2 3 4 5 6 7))
          (item 3))
      (let ((first-large-item (find item mylist :test #'<)))
        (when first-large-item
          (nthcdr (1- first-large-item) mylist))))
    => (4 5 6 7)

This is easy to pass to `every' then:

    (let ((mylist (list 1 2 3 4 5 6 7))
          (item 3))
      (let ((first-large-item (find item mylist :test #'<)))
        (when first-large-item
          (every (lambda (number)
                   (> number item))
            (nthcdr (1- first-large-item) mylist)))))
    => T

Using *that* result with the (nthcdr (- first-large-item 2) mylist) to
setcdr the CDR of the appropriate cons cell is probably easy then.  In
Common Lisp, using (setf (cdr ...)) the code would look like this:

    [37]> (defun trim-sorted-list (item sequence)
            (let ((first-large-index (find item sequence :test #'<)))
              (when first-large-index
                (let ((last-node (nthcdr (- first-large-index 2) sequence)))
                  (when (every (lambda (number)
                                 (> number item))
                               (cdr last-node))
                    (setf (cdr last-node) nil)
                    sequence)))))
    TRIM-SORTED-LIST

    [38]> (trim-sorted-list 3 (list 1 2 3 4 5 6 7))
    (1 2 3)

    [39]> (trim-sorted-list 8 (list 1 2 3 4 5 6 7))
    NIL

A similar function for Emacs Lisp, using (setcdr ...) would look like
this, I guess:

    (defun trim-sorted-list (item sequence)
      (let ((first-large-index (find item sequence :test #'<)))
        (when first-large-index
          (let ((last-node (nthcdr (- first-large-index 2))))
            (when (every (lambda (number)
                           (> number item))
                         (cdr last-node))
              (setcdr last-node nil)
              sequence)))))

My only concern about find/nthcdr is that if the list can get very very
long, you are essentially going to iterate over it twice.  But I am not
sure if there's a function like `find' that will return the cons cell
instead of the value at position N.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]