Are there non-lazily evaluated "and" or "or" operations in Common Lisp?
Asked Answered
V

3

5

The usual and and or operators in Common Lisp will lazily evaluate their operands, e.g. the and will stop once it encounters the first nil. I am searching for an operator that does not work this way, but instead always evaluates all operands before returning a result. Is there something like this?

In C for example you have the lazy && and you have the bitwise &, which can be used as a non-lazy alternative. I know about logand and bit-and, but those do not work on boolean operands.

E.g.:

(and NIL (not-defined))

would not throw an error, but i want it to throw one.

Vincent answered 13/8, 2019 at 12:54 Comment(1)
In C for example you have the lazy && - it's not laziness but short-circuitUtilitarianism
K
3
(defun and* (&rest l)
  (every #'identity l))

or returning the last value if all true

(defun and& (&rest l)
  (or (null l)
      (loop for b in l
            unless b
              do (return nil)
            finally (return b))))

or

(defun and& (&rest l)
  (or (null l)
      (loop for b in l
            always b
            finally (return b))))
Klondike answered 13/8, 2019 at 13:5 Comment(2)
With this definition (and 1 2) gives 2, (and* 1 2) gives TLeeward
@6502: see and&Klondike
L
2

A possible implementation is

(defun and* (&rest x)
  (let ((res T))
    (dolist (item x)
      (if (null item) (return-from and* item))
      (setf res item))
    res))

Explanation:

  • We initialize the result to T (this is needed because (and)T)
  • We loop over the arguments
  • If an argument is NIL then we return it
  • Otherwise we store the item as result
  • If we get to the end of the loop then we return last item

The implementation of or* is simpler because the only "falsy" value in Common Lisp is NIL so there's no need to remember what is the last falsy element and you can just use some...

(defun or* (&rest x)
  (some #'identity x))
Leeward answered 13/8, 2019 at 13:42 Comment(0)
R
1

Alternative implementation for and*:

(defun and* (&rest values &aux (res t))
  (every (lambda (v) (setf res v)) values)
  res)

This returns the last non-nil value or nil.

Rajah answered 13/8, 2019 at 18:45 Comment(1)
+1 for elegance, but I think you need to use &aux (res T) because (and*) is supposed to return T, not NIL.Leeward

© 2022 - 2024 — McMap. All rights reserved.