Destructively moves the "best" element (according to the predicate function passed in) to the front of a list. Feel free to improve the code by using other functions, recursion, etc, but it must only visit each element once. i.e. you can't just find the best and call (remove best lst :test #'equal :count 1) and then cons best onto lst.
(defun nbest-to-front (lst pred) (let ((best lst) (par nil)) (do* ((prev lst (cdr prev)) (chk (cdr prev) (cdr prev))) ((endp chk)) (if (funcall pred (car chk) (car best)) (setq best chk par prev))) (if par (setf (cdr par) (cdr best))) (setf lst (cons (car best) lst)))) ;; last line is redundant if par = nil, but I'd like lst returned...
The alternative I thought of to that last line is: (when par (setf (cdr par) (cdr best)) (setf lst (cons (car best) lst))) lst))
becase I'd like the list returned, and the latter uses more lines, I stuck with the first.
> jrwats wrote: > >> (setf lst (cons (car best) lst)))) > >> ;; last line is redundant if par = nil, but I'd like lst returned...
> > Actually - this is buggy and will duplicate the front element if par > > (parent) = nil
> >>The alternative I thought of to that last line is: > >>(when par > >> (setf (cdr par) (cdr best)) > >> (setf lst (cons (car best) lst))) > >> lst))
> > Not an alternative - requirement...
> Can we use loop and rotatef? (hint)
> kt
Why would I do that?
(sort list pred)
And it gets all the others in the right order as a bonus!
(defun nmove-best-to-front2 (lst pred) (loop for i on lst do (if (funcall pred (car i) (car lst)) (rotatef (car lst) (car i)))))
I'll have to remember this rotatef voodoo...
However... it's still doing swapping on each best find. I'd imagine it's markedly slower when using a list sorted reverse of the given predicate (descending?). It doesn't change the runtime O(n), but ah what the heck, slime is open, I'll run it now:
scratch that - way to slow...
CL-USER> (time (test-nmove-best-to-front2 1000000)) Evaluation took: 2.446 seconds of real time 2.424151 seconds of total run time (2.396150 user, 0.028001 system) 99.10% CPU 3,827,875,897 processor cycles 0 bytes consed NIL CL-USER> (time (test-nmove-best-to-front 1000000)) Evaluation took: 0.048 seconds of real time 0.040003 seconds of total run time (0.040003 user, 0.000000 system) 83.33% CPU 49,030,586 processor cycles 0 bytes consed
How about this:
(defun nmove-best-to-front3 (lst pred) (let ((best (car lst)) (best-lst lst)) (loop for i on (cdr lst) do (if (funcall pred (car i) best) (setq best (car i) best-lst i))) (rotatef (car lst) (car best-lst))))
CL-USER> (time (test-nmove-best-to-front3 1000000)) Evaluation took: 2.902 seconds of real time 2.876180 seconds of total run time (2.856178 user, 0.020002 system) 99.10% CPU 3,792,683,484 processor cycles 0 bytes consed NIL
(defun nmove-best-to-front2 (lst pred) (loop for i on lst do (if (funcall pred (car i) (car lst)) (rotatef (car lst) (car i)))))
I'll have to remember this rotatef voodoo...
However... it's still doing swapping on each best find. I'd imagine it's markedly slower when using a list sorted reverse of the given predicate (descending?). It doesn't change the runtime O(n), but ah what the heck, slime is open, I'll run it now:
scratch that - way to slow...
CL-USER> (time (test-nmove-best-to-front2 1000000)) Evaluation took: 2.446 seconds of real time 2.424151 seconds of total run time (2.396150 user, 0.028001 system) 99.10% CPU 3,827,875,897 processor cycles 0 bytes consed NIL CL-USER> (time (test-nmove-best-to-front 1000000)) Evaluation took: 0.048 seconds of real time 0.040003 seconds of total run time (0.040003 user, 0.000000 system) 83.33% CPU 49,030,586 processor cycles 0 bytes consed
How about this:
(defun nmove-best-to-front3 (lst pred) (let ((best (car lst)) (best-lst lst)) (loop for i on (cdr lst) do (if (funcall pred (car i) best) (setq best (car i) best-lst i))) (rotatef (car lst) (car best-lst))))
CL-USER> (time (test-nmove-best-to-front3 1000000)) Evaluation took: 2.902 seconds of real time 2.876180 seconds of total run time (2.856178 user, 0.020002 system) 99.10% CPU 3,792,683,484 processor cycles 0 bytes consed NIL
> (defun nmove-best-to-front2 (lst pred) > (loop for i on lst do > (if (funcall pred (car i) (car lst)) > (rotatef (car lst) (car i)))))
> I'll have to remember this rotatef voodoo...
> However... it's still doing swapping on each best find. I'd imagine > it's markedly slower when using a list sorted reverse of the given > predicate (descending?). It doesn't change the runtime O(n), but ah > what the heck, slime is open, I'll run it now:
> scratch that - way to slow...
> CL-USER> (time (test-nmove-best-to-front2 1000000)) > Evaluation took: > 2.446 seconds of real time > 2.424151 seconds of total run time (2.396150 user, 0.028001 system) > 99.10% CPU > 3,827,875,897 processor cycles > 0 bytes consed > NIL > CL-USER> (time (test-nmove-best-to-front 1000000)) > Evaluation took: > 0.048 seconds of real time > 0.040003 seconds of total run time (0.040003 user, 0.000000 system) > 83.33% CPU > 49,030,586 processor cycles > 0 bytes consed
> How about this:
> (defun nmove-best-to-front3 (lst pred) > (let ((best (car lst)) > (best-lst lst)) > (loop for i on (cdr lst) do > (if (funcall pred (car i) best) > (setq best (car i) best-lst i))) > (rotatef (car lst) (car best-lst))))
> CL-USER> (time (test-nmove-best-to-front3 1000000)) > Evaluation took: > 2.902 seconds of real time > 2.876180 seconds of total run time (2.856178 user, 0.020002 system) > 99.10% CPU > 3,792,683,484 processor cycles > 0 bytes consed > NIL
The last post didn't make any sense and I re-tested again (after making sure EVERYTHING was compiled). Luckily the universe makes sense again and rotatef isn't too expensive (even when run all all the elements of the list
CL-USER> (time (test-nmove-best-to-front3 100000000)) Evaluation took: 2.583 seconds of real time 2.560160 seconds of total run time (2.540159 user, 0.020001 system) 99.11% CPU 4,386,136,174 processor cycles 8,720 bytes consed NIL CL-USER> (time (test-nmove-best-to-front2 100000000)) Evaluation took: 2.663 seconds of real time 2.640165 seconds of total run time (2.624164 user, 0.016001 system) 99.14% CPU 3,986,463,619 processor cycles 0 bytes consed NIL CL-USER> (time (test-nmove-best-to-front 100000000)) Evaluation took: 2.984 seconds of real time 2.964185 seconds of total run time (2.948184 user, 0.016001 system) 99.33% CPU 4,185,056,895 processor cycles 0 bytes consed
> On Dec 8, 9:33 pm, jrwats <jrw...@gmail.com> wrote:
> > > Can we use loop and rotatef? (hint)
> > > kt
> > Hmmm... a welcome hint:
> > (defun nmove-best-to-front2 (lst pred) > > (loop for i on lst do > > (if (funcall pred (car i) (car lst)) > > (rotatef (car lst) (car i)))))
> > I'll have to remember this rotatef voodoo...
> > However... it's still doing swapping on each best find. I'd imagine > > it's markedly slower when using a list sorted reverse of the given > > predicate (descending?). It doesn't change the runtime O(n), but ah > > what the heck, slime is open, I'll run it now:
> > scratch that - way to slow...
> > CL-USER> (time (test-nmove-best-to-front2 1000000)) > > Evaluation took: > > 2.446 seconds of real time > > 2.424151 seconds of total run time (2.396150 user, 0.028001 system) > > 99.10% CPU > > 3,827,875,897 processor cycles > > 0 bytes consed > > NIL > > CL-USER> (time (test-nmove-best-to-front 1000000)) > > Evaluation took: > > 0.048 seconds of real time > > 0.040003 seconds of total run time (0.040003 user, 0.000000 system) > > 83.33% CPU > > 49,030,586 processor cycles > > 0 bytes consed
> > How about this:
> > (defun nmove-best-to-front3 (lst pred) > > (let ((best (car lst)) > > (best-lst lst)) > > (loop for i on (cdr lst) do > > (if (funcall pred (car i) best) > > (setq best (car i) best-lst i))) > > (rotatef (car lst) (car best-lst))))
> > CL-USER> (time (test-nmove-best-to-front3 1000000)) > > Evaluation took: > > 2.902 seconds of real time > > 2.876180 seconds of total run time (2.856178 user, 0.020002 system) > > 99.10% CPU > > 3,792,683,484 processor cycles > > 0 bytes consed > > NIL
a) you are NOT allowed to destructively modify literal data. das ist verboten! fingerkloppen! b) Common Lisp has FIRST, SECOND, THIRD, ... and REST c) You can name a variable LIST. No need to write LST, LS, or L. Try to use real variable names. No need to encrypt or compress your code.
anonymous.c.lis...@gmail.com wrote: > On Dec 8, 9:53 pm, Kenny <kentil...@gmail.com> wrote:
>>jrwats wrote:
>>>> (setf lst (cons (car best) lst)))) >>>> ;; last line is redundant if par = nil, but I'd like lst returned...
>>>Actually - this is buggy and will duplicate the front element if par >>>(parent) = nil
>>>>The alternative I thought of to that last line is: >>>>(when par >>>> (setf (cdr par) (cdr best)) >>>> (setf lst (cons (car best) lst))) >>>> lst))
> I'd imagine >>it's markedly slower when using a list sorted reverse of the given >>predicate (descending?). It doesn't change the runtime O(n), but ah >>what the heck, slime is open, I'll run it now:
>>scratch that - way to slow...
>>CL-USER> (time (test-nmove-best-to-front2 1000000)) >>Evaluation took: >> 2.446 seconds of real time >> 2.424151 seconds of total run time (2.396150 user, 0.028001 system) >> 99.10% CPU >> 3,827,875,897 processor cycles >> 0 bytes consed >>NIL >>CL-USER> (time (test-nmove-best-to-front 1000000)) >>Evaluation took: >> 0.048 seconds of real time >> 0.040003 seconds of total run time (0.040003 user, 0.000000 system) >> 83.33% CPU >> 49,030,586 processor cycles >> 0 bytes consed
>>How about this:
>>(defun nmove-best-to-front3 (lst pred) >> (let ((best (car lst)) >> (best-lst lst)) >> (loop for i on (cdr lst) do >> (if (funcall pred (car i) best) >> (setq best (car i) best-lst i))) >> (rotatef (car lst) (car best-lst))))
>>CL-USER> (time (test-nmove-best-to-front3 1000000)) >>Evaluation took: >> 2.902 seconds of real time >> 2.876180 seconds of total run time (2.856178 user, 0.020002 system) >> 99.10% CPU >> 3,792,683,484 processor cycles >> 0 bytes consed >>NIL
>>Hmm... that didn't help... what's going on?
I don't know but you have managed to get almost two orders magnitude difference out of equivalent code, I'd head for Vegas.
> (defun nmove-best-to-front2 (lst pred) > (loop for i on (cdr lst) do > (if (funcall pred (car i) (car lst)) > (rotatef (car lst) (car i)))))
> (defun nmove-best-to-front3 (lst pred) > (let ((best (car lst)) > (best-lst lst)) > (loop for i on (cdr lst) do > (if (funcall pred (car i) best) > (setq best (car i) best-lst i))) > (rotatef (car lst) (car best-lst))))
Ewwww! with? finally? Helllooooooo? And is your car really so slow? Remember, car is a machine language opcode so it should be faster than an extra setf which is a CLOS <spit> generic function and goes through method combination*.
OTKB:
(loop with best = lst for cm on (cdr lst) when (funcall pred (car cm) (car best)) do (setf best cm) finally (rotatef (car lst) (car best)))
> And it gets all the others in the right order as a bonus!
For the rare case when you don't want the added cost of sorting everything and want the "best in the front" :)
Now - why it's beneficial to destructively change the input list and put the best in the front rather than simply returning the best element... well that's a good question...
> On Dec 8, 9:53 pm, Kenny <kentil...@gmail.com> wrote: >> jrwats wrote: >> >> (setf lst (cons (car best) lst)))) >> >> ;; last line is redundant if par = nil, but I'd like lst returned...
>> > Actually - this is buggy and will duplicate the front element if par >> > (parent) = nil
>> >>The alternative I thought of to that last line is: >> >>(when par >> >> (setf (cdr par) (cdr best)) >> >> (setf lst (cons (car best) lst))) >> >> lst))
>> > Not an alternative - requirement...
>> Can we use loop and rotatef? (hint)
>> kt
> Why would I do that?
> (sort list pred)
No cigar:
(setf list (sort list pred))
The identity of the first cons of the list may change under sort, so you must always retain the return value in place of the original list.
Sorting the list in a function like this is probably silly.
If you are going to extract the ``best'' item regularly, you will probably maintain the list in sorted order. The efficient way to do that is an ordered insert. A full sort will have to scan the whole list once, even if it's optimized for the case when the list is nearly sorted.
By keeping the list in sorted order, you will get O(1) on extracting the best value, but insertions are O(N). Still, in the average case the , this is a constant factor better than inserting in random order and then having to walk the entire list to find the best item, because insertion doesn't always have to walk the entire list.
> a) you are NOT allowed to destructively modify literal data. das ist > verboten! fingerkloppen!
blinkenlights? SBCL only gave me warnings and then did what I told it to do. I laugh in the face of danger!
> b) Common Lisp has FIRST, SECOND, THIRD, ... and REST
all of those have at least 1 character extra to type! Some even 3! That's at least 1/2 a second of typing.
> c) You can name a variable LIST. No need to write LST, LS, or L. Try > to use real variable names.
OK - I concede
> No need to encrypt or compress your code.
What? Oh I'm assuming this is in reference to car rather than first and lst as opposed to list. I blame lst on reading one of these ancient books I have that used the naming convention. I blame car on... McCarthy... and communists.
On Dec 9, 3:29 am, jrwats <jrw...@gmail.com> wrote:
> Destructively moves the "best" element (according to the predicate > function passed in) to the front of a list. Feel free to improve the > code by using other functions, recursion, etc, but it must only visit > each element once. i.e. you can't just find the best and call (remove > best lst :test #'equal :count 1) and then cons best onto lst.
Kenny Tilton posted a code snippet that is close to what I would write, but were I to take your approach (replacing conses) and style (Graham), I would first write a utility function, MAPL2, that is similar to MAPL, but also passes the predecessor sublist (or NIL if there isn't one).
(defun nbest-to-front-4 (lst pred) (iter (for i in lst) (for o on lst) (:for maybe-cut-point previous o initially nil) (with best-i = (car lst)) (:with cut-point = nil) (unless (first-time-p) (when (funcall pred best-i i) (setf best-i i cut-point maybe-cut-point))) (finally (return (cond ((null cut-point) lst) ((eq cut-point lst) lst) (t (let ((new-lst (cdr cut-point))) (shiftf (cdr cut-point) (cdr new-l) lst))) ))) ))
(compile 'nbest-to-front-4)
(defun nbest-to-front-5 (lst pred) (loop with best = lst for cm on (cdr lst) when (funcall pred (car cm) (car best)) do (setf best cm) finally (rotatef (car lst) (car best))))
(compile 'nbest-to-front-5)
ITER> (time (test-best-to-front 1000000 'nbest-to-front-4)) Evaluation took: 5.239 seconds of real time 5.212325 seconds of total run time (5.124320 user, 0.088005 system) [ Run times consist of 0.196 seconds GC time, and 5.017 seconds non- GC time. ] 99.48% CPU 7,833,665,384 processor cycles 400,698,840 bytes consed
NIL ITER> (time (test-best-to-front 1000000 'nbest-to-front-5)) Evaluation took: 5.164 seconds of real time 5.152323 seconds of total run time (5.048316 user, 0.104007 system) [ Run times consist of 0.224 seconds GC time, and 4.929 seconds non- GC time. ] 99.77% CPU 7,720,695,931 processor cycles 400,698,904 bytes consed
Note that last Kenny's function don't keep the order of other elements. Maybe it is right... You see, speed is comparable. I agree my version is not elegant at all. It would be no worse than Kenny's if
BTW, here is a useful abstraction of "returning the best value in iteration". It can be used separatedly. This is a (completely non- tested) implementation of iterate clause for that:
And the function would change to: ;;; iterate-keywords loaded (defun nbest-to-front-7 (lst pred) (iter:iter (:for place :on lst) (:for elt :in lst) (:finding place :yielding-best-of elt :by pred :into best) (:finally (rotatef (car lst) (car best)))) lst) ---------- 4$/hour lisp freelancer. Hire me!
I noticed I pasted a wrong version of nbest-to-front-4 here. Here is a fix:
(defun nbest-to-front-4 (lst pred) (iter (for i in lst) (for o on lst) (for maybe-cut-point previous o initially nil) (with best-i = (car lst)) (with cut-point = nil) (unless (first-time-p) (when (funcall pred best-i i) (setf best-i i cut-point maybe-cut-point))) (finally (return (cond ((null cut-point) lst) ((eq cut-point lst) lst) (t (let ((new-lst (cdr cut-point))) (shiftf (cdr cut-point) (cdr new-lst) lst))) ))) ))
Now: (defun nbest-to-front-5 (lst pred) (loop with best = lst for cm on (cdr lst) when (funcall pred (car cm) (car best)) do (setf best cm) finally (rotatef (car lst) (car best))) lst)
jrwats <jrw...@gmail.com> writes: > > a) you are NOT allowed to destructively modify literal data. das ist > > verboten! fingerkloppen! > blinkenlights? SBCL only gave me warnings and then did what I told it > to do. I laugh in the face of danger!
blinkenlights is okay. Its the spitzensparken you need to watch out for. And modifying literal data gets into that realm.
-- Thomas A. Russ, USC/Information Sciences Institute