It occurs to me that while the best solution for this problem is probably one using parse-integer as mentioned in malisper's answer, there is a sense in which this could be solved with a mapping based approach.
When we write something like #xB1
, we're not explicitly invoking a function. Instead, we're using the fact that #
is a dispatching read macro character, and that there's a function installed for the subcharacter x
that reads numbers written in hexadecimal. That means that by the time the evaluator or compiler gets a form, the number is already there. However, we do have access to the function that is doing the processing of the hexadecimal string, using get-dispatch-macro-character. Viz.:
CL-USER> (get-dispatch-macro-character #\# #\x)
#<FUNCTION SB-IMPL::SHARP-X> ; in SBCL
CL-USER> (get-dispatch-macro-character #\# #\x)
#<SYSTEM-FUNCTION SYSTEM::HEXADECIMAL-READER> ; in CLISP
What can we do with that function? How would we use it?
… If a character is a dispatching macro character C1, its reader macro
function is a function supplied by the implementation. This function
reads decimal digit characters until a non-digit C2 is read. If any
digits were read, they are converted into a corresponding integer
infix parameter P; otherwise, the infix parameter P is nil. The
terminating non-digit C2 is a character (sometimes called a
``sub-character'' to emphasize its subordinate role in the
dispatching) that is looked up in the dispatch table associated with
the dispatching macro character C1. The reader macro function
associated with the sub-character C2 is invoked with three arguments:
the stream, the sub-character C2, and the infix parameter P. For more
information about dispatch characters, see the function
set-dispatch-macro-character.
That means that when we write something like #xB1
, the function above is getting called with a stream from which it can read B1
, the character x
, and nil
. We can try calling that function with arguments like that, although we can't be quite sure what will happen, because implementations might make different assumptions about where the function will be called from.
For instance, this works without a problem in CLISP, but SBCL assumes that the function should be called recursively from read (which we're not doing):
CL-USER> (funcall (get-dispatch-macro-character #\# #\x)
(make-string-input-stream "B1")
#\x
nil)
177 ; in CLISP
CL-USER> (funcall (get-dispatch-macro-character #\# #\x)
(make-string-input-stream "B1")
#\x
nil)
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "~A was invoked
; with RECURSIVE-P being true outside of a recursive read operation."
; {1005F245B3}>. ; in SBCL
That said, for implementations where this will work, we can easily write a mapcar-like function to extract a dispatch macro character function and map it over some strings. Thus, in an implementation where this works:
(defun map-dispatch-macro-character (disp-char
sub-char
list
&optional (readtable *readtable*))
"Retrieve the dispatch macro character for DISP-CHAR and SUB-CHAR and
map it over the elements in LIST. Each element in LIST is either a
string designator or a two-element list of a string-designator and a
prefix argument."
(flet ((to-list (x)
(if (listp x) x
(list x))))
(let ((fn (get-dispatch-macro-character disp-char sub-char readtable)))
(mapcar (lambda (x)
(destructuring-bind (str &optional prefix) (to-list x)
(with-input-from-string (in (string str))
(funcall fn in sub-char prefix))))
list))))
CL-USER> (map-dispatch-macro-character #\# #\x '(B1 "A5" (FF nil)))
(177 165 255)
And of course, if you really want to be able to write #x
, you could of course define a version that just extracts the characters from a string of length two, so that you could do:
CL-USER> (map-dispatch-macro-character* "#x" '(B1 A5 FF))
(177 165 255)
#x...
doesn't get the decimal value of a hex string; it's a notation for writing numbers. When the reader reads#xB1
, it returns the number 177. Then the REPL is evaluating the form 177, which evaluates to itself, and then prints that. The way it's printed depends on a number of things. E.g., see what happens if you(setf *print-base* 16)
and then evaluate#xB1
. You'll seeB1
printed. – Denude