结果如何将宏的列表参数转换为经过大量修改但引用的列表?
How do I turn a list argument to a macro into a heavily modified but quoted list as a result?
如何将宏的列表参数转换为经过大量修改但引用的列表?
我将 John Foster 将 win32 WM_* 消息转换为有用文本的示例翻译成 Lisp。
Convert Windows Message IDs to Text
我需要定义 win32 常量,一个宏似乎非常适合调用 (defconstant WM_PAINT 15) 并自动注册 WM_PAINT 和“WM_PAINT”。
工作测试代码:
(defmacro def-windows-message (&rest rest )
"Converts a list of windows messages into defconstant and a string lookup in GetMessageText."
`(block nil
,@(loop for pair in rest collect
`(defconstant ,(car pair) ,(cadr pair)))
(defparameter *windows-message-lookup* #(
,@(loop for pair in rest collect (cons (cadr pair) (string (car pair) )))
))
(defun GetMessageText (id)
(destructuring-bind (index text)
(msgid_to_index id *windows-message-lookup* 0 (length *windows-message-lookup*))
(if (= index -1)
(format nil "(WM_? ~a)" id)
; else
text)))
(defun msgid_to_index (target_id array min_idx max_idx)
(let* ( (cur_idx (floor (/ (+ max_idx min_idx) 2)))
(cur_pair (aref array cur_idx))
(cur_id (car cur_pair))
(cur_text (cdr cur_pair)))
(cond
((= target_id cur_id) (list cur_idx cur_text))
((> target_id cur_id)
(if (= cur_idx min_idx)
'(-1 nil)
; else
(msgid_to_index target_id array cur_idx max_idx)))
((< target_id cur_id)
(if (= cur_idx max_idx)
'(-1 nil)
;else
(msgid_to_index target_id array min_idx cur_idx)))
)
)
)
))
; Test it!
(def-windows-message
(WM_NULL 0)
(WM_CREATE 1))
(format t "~a~%" (GetMessageText 0))
(format t "~a~%" (GetMessageText 1))
(format t "~a~%" (GetMessageText -1))
(format t "~a~%" (GetMessageText 99))
工作输出:
C:\lisphack>clisp test_macro_long_list.lisp
WM_NULL
WM_CREATE
(WM_? -1)
(WM_? 99)
非常好。这正是我想要的。
但是,当我向列表中添加 240 多条 windows 条消息时,它失败了。
长列表失败输出:*** - VALUES-LIST: too many return values
此消息表示传递给 #() 函数的函数参数过多。
我最终只创建了一个空数组,然后循环遍历列表,将每个元素插入到数组中。
工作但粗略的片段:
(defparameter *windows-message-looup* (make-array ,(length rest) :initial-element '(-1 Nil)))
,@(loop for pair in rest for idx from 0 collect `(setf (aref *windows-message-lookup* ,idx) (cons ,(cadr pair) ,(string (car pair) ))))
我真正想要的是将 &rest 列表转换为正确的形式,然后将该列表作为初始值发送到数组构造函数中:
优雅但有瑕疵
(defparameter *windows-message-lookup* (make-array ,(length rest)
,@(loop for pair in rest collect (cons (cadr pair) (string (car pair) )))
错误: *** - EVAL: 0 is not a function name; try using a symbol instead
我认为这意味着 make-array
的最终参数正在执行而不是作为初始化程序引用。
我需要在结果列表中添加一个额外的引号。
再引述
(defparameter *windows-message-lookup* (make-array ,(length rest)
`,@(loop for pair in rest collect ,(cons (cadr pair) (string (car pair) )))))
错误: *** - READ: the syntax
,@表单无效`
我尝试了一些变体,添加额外的反引号或移动括号,但它们导致我的列表或参数对的列表执行而不是读取。
[编辑]
谢谢。这看起来比我尝试的要好。对于我的教育,这个宏的正确形式是什么?
(defmacro def-windows-message (&rest rest )
`(defparameter *windows-message-lookup*
(make-array
,(length rest)
`(,@(loop for pair in rest collect ,(cons (cadr pair) (string (car pair) )))))))
对于(def-windows-message '((onefish 1) (twofish 2) .. ))
的输入
我想要 (make-array 999 '( (onefish "onefish") (twofish "twofish") ... ))
作为结果。
我在正确引用结果列表并仍在宏中处理它时遇到问题。
我试图理解这个宏在做什么,但当我意识到它在数组上使用二进制搜索来查找消息时就放弃了。这就是为什么将 C 语言翻译成 Lisp 语言几乎从来都不是正确答案的原因。
我不太确定,但我想你可能想要这个:
(defvar *windows-message-lookup*
;; Unless you have vast numbers of messages (thousands) and/or the
;; code is very performance sensitive, this could as well be an
;; alist
(make-hash-table))
(defmacro def-windows-messages (&body messages)
"Define a bunch of windows messages, each message is (name id)"
`(progn
,@(loop for (name id) in messages
collect `(defconstant ,name ,id)
collect `(setf (gethash ,id *windows-message-lookup*)
(string ',name)))
(values)))
(defun get-message-text (id)
"Return the text associated with ID and T if it's found, or a string
saying it's not found and NIL if it's not found."
(multiple-value-bind (text foundp)
(gethash id *windows-message-lookup*)
(values (if foundp text (format nil (format nil "(WM_? ~a)" id)))
foundp)))
现在:
> (def-windows-messages
(WM_NULL 0)
(WM_CREATE 1))
> (get-message-text 0)
"WM_NULL"
t
> (get-message-text 1)
"WM_CREATE"
t
> (get-message-text -1)
"(WM_? -1)"
nil
> (get-message-text 99)
"(WM_? 99)"
nil
请注意,现在的代码(之前略有不同)可以这样说
(defconstant some-id 12)
(define-windows-messages
(WM_FOO some-id))
作为原始宏中引用问题的一个示例,这里有一个名为 define-message-array
的宏,它可用于以与原始宏类似的方式定义消息数组。它与原始宏的不同之处在于您可以指定数组的名称。
(defmacro define-message-array (name &body from)
`(defparameter ,name
(make-array ,(length from)
:initial-contents
'(,@(loop for (n v) in from
collect (cons v (string n)))))))
请注意,这里没有嵌套的反引号:您并不经常需要它。
有了这个,那么
(define-message-array *foo*
(x 1)
(y 2))
扩展到
(defparameter *foo*
(make-array 2 :initial-contents '((1 . "X") (2 . "Y"))))
如何将宏的列表参数转换为经过大量修改但引用的列表?
我将 John Foster 将 win32 WM_* 消息转换为有用文本的示例翻译成 Lisp。 Convert Windows Message IDs to Text
我需要定义 win32 常量,一个宏似乎非常适合调用 (defconstant WM_PAINT 15) 并自动注册 WM_PAINT 和“WM_PAINT”。
工作测试代码:
(defmacro def-windows-message (&rest rest )
"Converts a list of windows messages into defconstant and a string lookup in GetMessageText."
`(block nil
,@(loop for pair in rest collect
`(defconstant ,(car pair) ,(cadr pair)))
(defparameter *windows-message-lookup* #(
,@(loop for pair in rest collect (cons (cadr pair) (string (car pair) )))
))
(defun GetMessageText (id)
(destructuring-bind (index text)
(msgid_to_index id *windows-message-lookup* 0 (length *windows-message-lookup*))
(if (= index -1)
(format nil "(WM_? ~a)" id)
; else
text)))
(defun msgid_to_index (target_id array min_idx max_idx)
(let* ( (cur_idx (floor (/ (+ max_idx min_idx) 2)))
(cur_pair (aref array cur_idx))
(cur_id (car cur_pair))
(cur_text (cdr cur_pair)))
(cond
((= target_id cur_id) (list cur_idx cur_text))
((> target_id cur_id)
(if (= cur_idx min_idx)
'(-1 nil)
; else
(msgid_to_index target_id array cur_idx max_idx)))
((< target_id cur_id)
(if (= cur_idx max_idx)
'(-1 nil)
;else
(msgid_to_index target_id array min_idx cur_idx)))
)
)
)
))
; Test it!
(def-windows-message
(WM_NULL 0)
(WM_CREATE 1))
(format t "~a~%" (GetMessageText 0))
(format t "~a~%" (GetMessageText 1))
(format t "~a~%" (GetMessageText -1))
(format t "~a~%" (GetMessageText 99))
工作输出:
C:\lisphack>clisp test_macro_long_list.lisp
WM_NULL
WM_CREATE
(WM_? -1)
(WM_? 99)
非常好。这正是我想要的。
但是,当我向列表中添加 240 多条 windows 条消息时,它失败了。
长列表失败输出:*** - VALUES-LIST: too many return values
此消息表示传递给 #() 函数的函数参数过多。
我最终只创建了一个空数组,然后循环遍历列表,将每个元素插入到数组中。
工作但粗略的片段:
(defparameter *windows-message-looup* (make-array ,(length rest) :initial-element '(-1 Nil)))
,@(loop for pair in rest for idx from 0 collect `(setf (aref *windows-message-lookup* ,idx) (cons ,(cadr pair) ,(string (car pair) ))))
我真正想要的是将 &rest 列表转换为正确的形式,然后将该列表作为初始值发送到数组构造函数中:
优雅但有瑕疵
(defparameter *windows-message-lookup* (make-array ,(length rest)
,@(loop for pair in rest collect (cons (cadr pair) (string (car pair) )))
错误: *** - EVAL: 0 is not a function name; try using a symbol instead
我认为这意味着 make-array
的最终参数正在执行而不是作为初始化程序引用。
我需要在结果列表中添加一个额外的引号。 再引述
(defparameter *windows-message-lookup* (make-array ,(length rest)
`,@(loop for pair in rest collect ,(cons (cadr pair) (string (car pair) )))))
错误: *** - READ: the syntax
,@表单无效`
我尝试了一些变体,添加额外的反引号或移动括号,但它们导致我的列表或参数对的列表执行而不是读取。
[编辑] 谢谢。这看起来比我尝试的要好。对于我的教育,这个宏的正确形式是什么?
(defmacro def-windows-message (&rest rest )
`(defparameter *windows-message-lookup*
(make-array
,(length rest)
`(,@(loop for pair in rest collect ,(cons (cadr pair) (string (car pair) )))))))
对于(def-windows-message '((onefish 1) (twofish 2) .. ))
的输入
我想要 (make-array 999 '( (onefish "onefish") (twofish "twofish") ... ))
作为结果。
我在正确引用结果列表并仍在宏中处理它时遇到问题。
我试图理解这个宏在做什么,但当我意识到它在数组上使用二进制搜索来查找消息时就放弃了。这就是为什么将 C 语言翻译成 Lisp 语言几乎从来都不是正确答案的原因。
我不太确定,但我想你可能想要这个:
(defvar *windows-message-lookup*
;; Unless you have vast numbers of messages (thousands) and/or the
;; code is very performance sensitive, this could as well be an
;; alist
(make-hash-table))
(defmacro def-windows-messages (&body messages)
"Define a bunch of windows messages, each message is (name id)"
`(progn
,@(loop for (name id) in messages
collect `(defconstant ,name ,id)
collect `(setf (gethash ,id *windows-message-lookup*)
(string ',name)))
(values)))
(defun get-message-text (id)
"Return the text associated with ID and T if it's found, or a string
saying it's not found and NIL if it's not found."
(multiple-value-bind (text foundp)
(gethash id *windows-message-lookup*)
(values (if foundp text (format nil (format nil "(WM_? ~a)" id)))
foundp)))
现在:
> (def-windows-messages
(WM_NULL 0)
(WM_CREATE 1))
> (get-message-text 0)
"WM_NULL"
t
> (get-message-text 1)
"WM_CREATE"
t
> (get-message-text -1)
"(WM_? -1)"
nil
> (get-message-text 99)
"(WM_? 99)"
nil
请注意,现在的代码(之前略有不同)可以这样说
(defconstant some-id 12)
(define-windows-messages
(WM_FOO some-id))
作为原始宏中引用问题的一个示例,这里有一个名为 define-message-array
的宏,它可用于以与原始宏类似的方式定义消息数组。它与原始宏的不同之处在于您可以指定数组的名称。
(defmacro define-message-array (name &body from)
`(defparameter ,name
(make-array ,(length from)
:initial-contents
'(,@(loop for (n v) in from
collect (cons v (string n)))))))
请注意,这里没有嵌套的反引号:您并不经常需要它。
有了这个,那么
(define-message-array *foo*
(x 1)
(y 2))
扩展到
(defparameter *foo*
(make-array 2 :initial-contents '((1 . "X") (2 . "Y"))))