Question

How can EVENP or ODDP used in a :KEY be compared to integers?

On the page of the SEARCH function in the Specifications, one can read the example:

(search '(0 1) '(2 4 6 1 3 5) :key #'oddp) =>  2

Can you explain how the comparison could work?

Indeed, the ODDP function returns a "generalized boolean", which is

"an object used as a truth value, where the symbol nil represents false and all other objects represent true."

I've tried several things to check my understanding (under SBCL):

CL-USER> (coerce (oddp 6) 'integer)
; Debugger entered on #<SIMPLE-TYPE-ERROR expected-type: INTEGER datum: NIL>
CL-USER> (eql 0 nil)
NIL
CL-USER> (equal 0 nil)
NIL
CL-USER> (equalp 0 nil)
NIL
CL-USER> (type-of (oddp 6))
NULL
CL-USER> (eql 0 (oddp 6))
NIL
CL-USER> (equal 0 (oddp 6))
NIL
CL-USER> (equalp 0 (oddp 6))
NIL

So that means, that default :test function of SEARCH is in theory not able to compare nil to 0.

Am I to understand this behavior of the SEARCH function is in fact specified by the example itself? Or is there something behind that implicitly compare a NULL object (or a nil value) to 0 and a true (t) value as 1? How does it work?

Update :

I now understand that the ODDP function is applied to the first sequence in the call to SEARCH. But it is not the same behavior as the other sequence functions like ASSOC for example :

CL-USER> (assoc 1 '((2 . A) (4 . B) (5 . C)) :key #'oddp)
NIL
CL-USER> (assoc t '((2 . A) (4 . B) (5 . C)) :key #'oddp)
(5 . C)

Where is defined the behavior of the :KEY arguments to sequence functions in the specifications ? I can't find it...

 5  71  5
1 Jan 1970

Solution

 5

The SEARCH function does not directly compare numbers to booleans. Instead, it compares the results of applying the ODDP function to the elements of both sequences.

Specifically in the case of (search '(0 1) '(2 4 6 1 3 5) :key #'oddp) firstly, the key function ODDP is performed on both the arguments, namely (0, 1) becomes (NIL, T) and (2 4 6 1 3 5) becomes (NIL, NIL, NIL, T, T, T) then the position of (NIL, T) is searched for in (NIL, NIL, NIL, T, T, T) hence 2 is returned since that is the first index (NIL, T) is found in (NIL, NIL, NIL, T, T, T).

You can verify what ODDP does by using mapcar:

(format t "~a~%" (mapcar #'oddp '(0 1))) outputs (nil, T)

2024-07-06
Sash Sinha