Your question mentions "bit vectors", but you're actually working with lists of bits. Common Lisp actually provides a bit-vector
type, though. It's a specialized type of array. It's still a vector, though, so you can use sequence functions that work on arbitrary sequences (vectors or lists), and so you could write a solution to this that works for bit vectors as well as any other sequences whose elements are bits by using map
:
(defun distance (bits1 bits2)
(reduce '+ (map 'bit-vector 'logxor bits1 bits2)))
It works as expected, but note that you can use bit vectors (which you can write easily with the #*…
notation), as well as lists. You don't get this flexibility with mapcar
, which works only on lists. Also note the use of (reduce '+ …)
rather than (apply '+ …)
. There are two reasons for this
reduce
works on arbitrary sequences, so you can use it with vectors as well as lists.apply
is subject tocall-arguments-limit
which is the maximum number of arguments that can be passed to a function. While the small cases here aren't going to run afoul of that, if you had larger bit vectors, you could run into that problem.
CL-USER> (distance #*1011 #*0101)
3
CL-USER> (distance '(1 0 1 1) '(0 1 0 1))
3
CL-USER> (distance #*1011 '(0 1 0 1))
3
Rainer Joswig's answer pointed out that you can also do bit operations with integers. Since that's a pretty reasonable thing to do (especially since you can then use the binary integer notation), it's worth making a version that works with that too. Here's an implementation that converts all of its arguments to integers (whether they're already integers or sequences of bits) and uses (logcount (logxor … …))
:
(defun distance (bits1 bits2)
(flet ((to-int (bits)
(if (integerp bits) bits
(reduce (lambda (int bit)
(logior (ash int 1) bit))
bits))))
(logcount (logxor (to-int bits1) (to-int bits2)))))
CL-USER> (distance #b1011 '(0 1 0 1))
3
CL-USER> (distance #*1011 '(0 1 0 1))
3
CL-USER> (distance #*1011 5)
3
CL-USER> (distance 11 5)
3