i've got this recorded and ready to be pushed. unless soemone has additional comments, i'll push it eventually.
ok, seems like i wasn't really that happy with it after all. i almost pushed it when i tried to use it in a situation where the sequence argument was a bigger form and the literal length was lost far away in the noise. so i wanted to transpose the two arguments as the = sign in the name suggests, but obviously i couldn't. then i thought of turning length= into a function that takes &rest arguments and each argument can be either an integer or a sequence. you can find the current implementation at the end of the mail, but i'm not sure vetoers will like it... it's somewhat dwim-ish in that it accepts both integers and sequences at any position, but on the other hand anything else feels crippled from the user point of view. -- attila (defun length= (&rest sequences) "Takes any number of sequences or integers in any order. Returns true iff the length of all the sequences and the integers are equal. Hint: there's a compiler macro that expands into more efficient code if the first argument is a literal integer." (declare (dynamic-extent sequences) (inline sequence-of-length-p) (optimize speed)) (unless (cdr sequences) (error "You must call LENGTH= with at least two arguments")) ;; There's room for optimization here: multiple list arguments could be ;; traversed in parallel. (let* ((first (pop sequences)) (current (if (integerp first) first (length first)))) (declare (type array-index current)) (dolist (el sequences) (if (integerp el) (unless (= el current) (return-from length= nil)) (unless (sequence-of-length-p el current) (return-from length= nil))))) t) (define-compiler-macro length= (&whole form length &rest sequences) (cond ((zerop (length sequences)) form) (t (let ((optimizedp (integerp length))) (with-unique-names (tmp current) (declare (ignorable current)) `(locally (declare (inline sequence-of-length-p)) (let ((,tmp) ,@(unless optimizedp `((,current ,length)))) ,@(unless optimizedp `((unless (integerp ,current) (setf ,current (length ,current))))) (and ,@(loop for sequence :in sequences collect `(progn (setf ,tmp ,sequence) (if (integerp ,tmp) (= ,tmp ,(if optimizedp length current)) (sequence-of-length-p ,tmp ,(if optimizedp length current))))))))))))) some macroexpansions: DEV> (pprint (swank::compiler-macroexpand-1 `(length= 1 '(1 2)))) (LOCALLY (DECLARE (INLINE SEQUENCE-OF-LENGTH-P)) (LET ((#:TMP3027)) (AND (PROGN (SETF #:TMP3027 '(1 2)) (IF (INTEGERP #:TMP3027) (= #:TMP3027 1) (SEQUENCE-OF-LENGTH-P #:TMP3027 1)))))) ; No value DEV> (pprint (swank::compiler-macroexpand-1 `(length= '(1 2) 1))) (LOCALLY (DECLARE (INLINE SEQUENCE-OF-LENGTH-P)) (LET ((#:TMP3029) (#:CURRENT3030 '(1 2))) (UNLESS (INTEGERP #:CURRENT3030) (SETF #:CURRENT3030 (LENGTH #:CURRENT3030))) (AND (PROGN (SETF #:TMP3029 1) (IF (INTEGERP #:TMP3029) (= #:TMP3029 #:CURRENT3030) (SEQUENCE-OF-LENGTH-P #:TMP3029 #:CURRENT3030)))))) ; No value DEV>