Lisp:如何MAPCAR"#x"在HEX列表?

时间:2014-08-27 03:45:43

标签: common-lisp

使用#x ...如下所示获得十六进制值的小数

> #xB1 
177
> #xA5
165
> #xFF
255

假设我们有一个十六进制列表,在列表中使用mapcar #x ...的正确语法是什么?以下不起作用:

> (mapcar #'(lambda (hex) `(#x,hex)) '(B1 A5 FF))
  

读卡器错误:#b / #o / #x /#r宏中的格式错误。      [SIMPLE-ERROR类型的条件]

感谢。

2 个答案:

答案 0 :(得分:8)

#x被称为"读取器宏"。它与使用引号(即"")表示字符串非常相似。它们在读取/编译代码时执行。你真正想要的是一个可以在运行时从十六进制字符串转换的过程。您正在寻找的过程是parse-integer,它接受一个字符串并返回它所代表的值。带有它的mapcar看起来应该是这样的:

(mapcar (lambda (hex) 
           (parse-integer hex :radix 16))
        '("B1" "A5" "FF"))

请注意,这是使用字符串,如果您想在建议中使用符号,则必须执行以下操作:

(mapcar (lambda (hex) 
           (parse-integer (symbol-name hex) :radix 16))
        '(B1 A5 FF))

如果你不知道符号和字符串之间的区别,我建议你阅读:What exactly is a symbol in lisp/scheme?

答案 1 :(得分:3)

我觉得虽然最好解决这个问题的方法可能是malisper's answer中提到的 parse-integer ,但这种感觉可以通过基于映射的方法来解决。

当我们写#xB1之类的内容时,我们不是显式地调用函数。相反,我们使用的事实是#是一个调度读取宏字符,并且为子字符x安装了一个读取以十六进制编写的数字的函数。这意味着,当评估者或编译器获得表单时,该数字已经存在。但是,我们 do 可以使用get-dispatch-macro-character访问正在处理十六进制字符串的函数。即:

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

我们可以用这个功能做什么?我们将如何使用它?

  

2.1.4.4 Macro Characters

     

...如果一个字符是一个调度宏字符C1,它的读取器宏   function是由实现提供的函数。这个功能   读取十进制数字字符,直到读取非数字C2。如果有的话   读取数字后,将它们转换为相应的整数   中缀参数P;否则,中缀参数P为零。该   终止非数字C2是一个字符(有时称为a   “子角色”强调其在下属中的作用   在与之关联的调度表中查找的调度   调度宏字符C1。 读者宏功能   与子字符C2相关联的调用有三个参数:   流,子字符C2和中缀参数P. 更多   有关调度字符的信息,请参阅该函数   设置调度宏字符。

这意味着,当我们编写#xB1之类的内容时,上面的函数会被调用一个流,可以从中读取B1,字符x和{{1} }。我们可以尝试使用这样的参数调用该函数,尽管我们不能确定会发生什么,因为实现可能会对函数的调用位置做出不同的假设。

例如,这在CLISP中没有问题,但SBCL假定该函数应该从读取(我们没有这样做)递归调用:

nil

CL-USER> (funcall (get-dispatch-macro-character #\# #\x)
                  (make-string-input-stream "B1")
                  #\x 
                  nil)
177                     ; in CLISP

也就是说,对于可以工作的实现,我们可以轻松编写一个 mapcar 类函数来提取调度宏字符函数并将其映射到某些字符串上。因此,在一个有效的实现中:

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

(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) ,你当然可以定义一个只从长度为2的字符串中提取字符的版本,这样就可以了:

#x