Search code examples
performancecomparisonelisp

Is there an elisp function like "cl-every" that returns nil on lists of unequal length?


I want to compare two lists in elisp using (cl-every #'eq list1 list2). However, this can return t if one of the lists is longer than the other, and I don't want that. I could call length on the lists, but then I'm traversing each list twice, which is needlessly inefficient. Is there a function like cl-every that also checks for equal lengths as well?


Solution

  • OOTB

    I don't think there is such a function OOTB.

    Roll your own

    I don't think it is hard to implement one, just modify cl-every.

    Length

    Note that length is implemented in C and, unless your lists are huge, should not noticeably affect performance:

    (defun list-elements-eq (l1 l2)
      (and (= (length l1)
              (length l2))
           (every #'eq l1 l2)))
    

    Equal

    You can also use equal for your lists: since equal starts with testing for eq, it will be a weaker relationship than what you want.

    Beware that in Common Lisp equal may not terminate for circular structures. Emacs Lisp is "smarter": it detects cicularity:

    (setq x (list 1))
    (setcdr x x)
    (setq y (list 1))
    (setcdr y y)
    (eq x y)
    (equal x y)
    Debugger entered--Lisp error: (circular-list #1=(1 . #1#))
      equal(#1=(1 . #1#) #2=(1 . #2#))
    

    (arguably, it should returns t instead).

    Common Lisp

    Generally speaking, Common Lisp is a better language for "heavy lifting".

    If your lists are huge, you might want to use it instead of Emacs Lisp:

    (defun list-elements-eq (l1 l2)
      (if (endp l1)
          (endp l2)
          (and (not (endp l2))
               (eq (pop l1) (pop l2))
               (list-elements-eq l1 l2))))
    

    This is tail-recursive, and any decent CL will optimize recursion away (possibly depending on the optimize setting).

    You don't want to use deep recursion in ELisp (see Why is there no tail recursion optimization in Emacs lisp, not but like other scheme?).