From: Daniel Colascione <daniel@censorshipresearch.org>
To: Geoff Gole <geoffgole@gmail.com>
Cc: Emacs development discussions <emacs-devel@gnu.org>
Subject: [PATCH] use tail pointer for LOOP (Was: Re: O(N^2) behavior in LOOP)
Date: Sat, 29 May 2010 19:58:32 -0400 [thread overview]
Message-ID: <4C01AA28.6030002@censorshipresearch.org> (raw)
In-Reply-To: <AANLkTilFHHbnZbffRpqQGpYJVN0sZsCZ2vx-QYrUP_EH@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 5755 bytes --]
We do this only for the anonymous-variable case, but it's still an
improvement.
---
/Applications/Emacs.app/Contents/Resources/lisp/emacs-lisp/cl-macs.el
2008-01-06 20:07:45.000000000 -0500
+++ cl-macs2.el 2010-05-29 19:52:09.000000000 -0400
@@ -625,6 +625,7 @@
(defvar loop-initially) (defvar loop-map-form) (defvar loop-name)
(defvar loop-result) (defvar loop-result-explicit)
(defvar loop-result-var) (defvar loop-steps) (defvar loop-symbol-macs)
+(defvar loop-accum-tailptr)
(defmacro loop (&rest args)
"The Common Lisp `loop' macro.
@@ -650,7 +651,8 @@
(loop-accum-var nil) (loop-accum-vars nil)
(loop-initially nil) (loop-finally nil)
(loop-map-form nil) (loop-first-flag nil)
- (loop-destr-temps nil) (loop-symbol-macs nil))
+ (loop-destr-temps nil) (loop-symbol-macs nil)
+ (loop-accum-tailptr nil))
(setq args (append args '(cl-end-loop)))
(while (not (eq (car args) 'cl-end-loop)) (cl-parse-loop-clause))
(if loop-finish-flag
@@ -984,28 +986,49 @@
((memq word '(collect collecting))
(let ((what (pop args))
- (var (cl-loop-handle-accum nil 'nreverse)))
+ (var (cl-loop-handle-accum nil :use-tailptr)))
(if (eq var loop-accum-var)
- (push (list 'progn (list 'push what var) t) loop-body)
- (push (list 'progn
- (list 'setq var (list 'nconc var (list 'list what)))
- t) loop-body))))
+ ;; Anonymous case; we can use a tail pointer here
+ (push `(progn
+ (if ,var
+ (setq ,loop-accum-tailptr
+ (setcdr ,loop-accum-tailptr (list ,what)))
+ (setq ,var (list ,what))
+ (setq ,loop-accum-tailptr ,var))
+ t)
+ loop-body)
+
+ ;; 'into' case. We have to use nconc here instead of
+ ;; tail-ptr setup or push-then-nreverse because user code
+ ;; can inspect and modify the given variable at any time.
+ (push `(progn
+ (setq ,var (nconc ,var (list ,what)))
+ t)
+ loop-body))))
- ((memq word '(nconc nconcing append appending))
+ ((memq word '(nconc noncing append appending))
(let ((what (pop args))
- (var (cl-loop-handle-accum nil 'nreverse)))
- (push (list 'progn
- (list 'setq var
- (if (eq var loop-accum-var)
- (list 'nconc
- (list (if (memq word '(nconc nconcing))
- 'nreverse 'reverse)
- what)
- var)
- (list (if (memq word '(nconc nconcing))
- 'nconc 'append)
- var what))) t) loop-body)))
+ (var (cl-loop-handle-accum nil :use-tailptr)))
+ (push (if (eq var loop-accum-var)
+ (let ((func (if (memq word '(nconc noncing))
+ 'identity 'copy-sequence)))
+
+ ;; use tail pointer
+ `(if ,var
+ (setq ,loop-accum-tailptr
+ (last (setcdr ,loop-accum-tailptr
+ (,func ,what))))
+ (setq ,var (,func ,what))
+ (setq ,loop-accum-tailptr (last ,var))))
+
+ ;; visible variable; no tail pointer
+ (let ((func
+ (if (memq word '(nconc nconcing)) 'nconc append)))
+ `(setq ,var (,func ,var ,what))))
+ loop-body)
+ (push t loop-body)))
+
((memq word '(concat concating))
(let ((what (pop args))
(var (cl-loop-handle-accum "")))
@@ -1144,20 +1167,36 @@
(list* (if par 'let 'let*)
(nconc (nreverse temps) (nreverse new)) body))))
-(defun cl-loop-handle-accum (def &optional func) ; uses args, loop-*
- (if (eq (car args) 'into)
- (let ((var (cl-pop2 args)))
- (or (memq var loop-accum-vars)
- (progn (push (list (list var def)) loop-bindings)
- (push var loop-accum-vars)))
- var)
- (or loop-accum-var
- (progn
- (push (list (list (setq loop-accum-var (make-symbol "--cl-var--")) def))
- loop-bindings)
- (setq loop-result (if func (list func loop-accum-var)
- loop-accum-var))
- loop-accum-var))))
+(defun cl-loop-handle-accum (def &optional listp) ; uses args, loop-*
+ (cond ((eq (car args) 'into) ; accumulate into visible variable
+ (let ((var (cl-pop2 args)))
+ (or (memq var loop-accum-vars)
+ (progn (push (list (list var def)) loop-bindings)
+ (push var loop-accum-vars)))
+ var))
+
+ ;; Otherwise, if we've already configured our anonymous
+ ;; accumulation variable so just return it.
+ (loop-accum-var)
+
+ ;; We're accumulating a list, so in addition to setting up
+ ;; loop-accum-var, set up loop-accum-tailptr.
+ (listp
+ (push (list (list (setq loop-accum-var (make-symbol
"--cl-accum--")) def))
+ loop-bindings)
+ (push (list (list (setq loop-accum-tailptr
+ (make-symbol "--cl-tailptr--")) def))
+ loop-bindings)
+ (setq loop-result loop-accum-var)
+ loop-accum-var)
+
+ ;; We're accumulating something else.
+ (t
+ (push (list (list (setq loop-accum-var (make-symbol
"--cl-var--")) def))
+ loop-bindings)
+ (setq loop-result (if func (list func loop-accum-var)
+ loop-accum-var))
+ loop-accum-var)))
(defun cl-loop-build-ands (clauses)
(let ((ands nil)
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
next prev parent reply other threads:[~2010-05-29 23:58 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-05-29 21:56 O(N^2) behavior in LOOP Daniel Colascione
2010-05-29 22:06 ` Daniel Colascione
2010-05-29 22:14 ` Lennart Borgman
2010-05-29 22:35 ` Geoff Gole
2010-05-29 23:58 ` Daniel Colascione [this message]
2010-05-30 0:45 ` [PATCH] use tail pointer for LOOP (Was: Re: O(N^2) behavior in LOOP) Ken Raeburn
2010-05-30 0:49 ` Daniel Colascione
2010-06-16 17:44 ` tomas
2010-06-16 18:10 ` [PATCH] use tail pointer for LOOP David Kastrup
2010-06-17 5:10 ` tomas
2010-06-17 7:18 ` Thien-Thi Nguyen
2010-06-17 9:22 ` tomas
2010-06-17 10:03 ` Thien-Thi Nguyen
2010-06-17 14:05 ` tomas
2010-06-17 15:16 ` Thien-Thi Nguyen
2010-06-17 10:12 ` Thien-Thi Nguyen
2010-06-17 20:48 ` Stefan Monnier
2010-06-18 7:07 ` David Kastrup
2010-06-18 13:40 ` Stefan Monnier
2010-05-30 17:05 ` Štěpán Němec
2010-05-30 17:09 ` Daniel Colascione
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4C01AA28.6030002@censorshipresearch.org \
--to=daniel@censorshipresearch.org \
--cc=emacs-devel@gnu.org \
--cc=geoffgole@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).