我有一个基本上像这样的文件(furniture.lisp)(还有更多条目):
(addgv :furniture 'stove
(make-instance 'stove
:pose (tf:make-pose-stamped
"map" ; frame-id
0.0
(tf:make-3d-vector -3.1 -0.9 0) ; translation/origin
(tf:euler->quaternion :az 0))))
(addgv :furniture 'drawers-cupboard
(make-instance 'cupboard
:pose (tf:make-pose-stamped
"map"
0.0
(tf:make-3d-vector -3.1 0.1 0)
(tf:euler->quaternion :az 0))))
现在,我想拥有一个函数 (get-locations "furniture.lisp" "locations.txt")
,该函数将对象坐标提取在3D-vector中,并将其输出写入文件:
(location stove -3.1 -0.9 9)
(location drawers-cupboard -3.1 0.1 0)
...
我首先写一个表达式,该表达式在文件中读取(到目前为止没有参数化)按行:
(ql:quickload "split-sequence")
(with-open-file (stream "furniture.lisp")
(do ((line (read-line stream nil)
(read-line stream nil)))
((null line))
(princ (split-sequence::split-sequence #Space line)) ; Just for demonstration
))
,但我意识到我没有机会/想法"连接"对象的名称(例如炉灶)及其坐标。我需要在"(addgv"的名称和变量"单词距离"之后的第二个符号。
(defun make-list-from-text (fn)
(with-open-file (stream fn)
(loop for line = (read-line stream nil nil)
while line
collect
(split-sequence::split-sequence #Space line))))
每行都是一个冠军(我不知道这个子结构是否是一个优势,也许我应该"弄平"结果)。现在我被困了。此外,我有一种感觉,我的方法不知所措。
编辑:
我遵循了Svante的方法,最后获得了所需的输出!除了创建虚拟软件包外,我还必须为软件包创建虚拟导出(例如:export :make-3d-vector
)。此外,:key #'car
不起作用,因为我的列表是一个"混合"列表,由标准列表(例如(make-instance ...)
)和符号(例如addgv
)组成。因此,我创建了一个辅助功能:
(defun find-helper (list-or-symbol)
(if (listp list-or-symbol)
(car list-or-symbol)
list-or-symbol))
并由#'find-helper
替换#'car
。
我的想法是创建一个虚拟tf
软件包,然后read
表格并分析您需要的任何内容。这样的东西(未经测试):
(eval-when (:compile-toplevel :load-toplevel :execute)
(unless (find-package #:tf)
(defpackage #:tf)))
(defun extract-location-file ()
(let ((*read-eval* nil))
(with-open-file (in "furniture.lisp")
(with-open-file (out "locations.txt"
:direction :output
:if-exists :supersede
:if-does-not-exist :create)
(loop :for form := (read in nil)
:while form
:do (print (extract-location form) out)
(terpri)))))
(defun extract-location (form)
`(location ,(third form)
,@(rest (find 'tf::make-3d-vector
(find 'tf::make-pose-stamped
(find 'make-instance
form
:key #'car)
:key #'car)
:key #'car))))
请确保不要省略将*read-eval*
绑定到nil
。
一般方法是:
- 将整个文件读为字符串(请参阅此处)
-
(cl-ppcre:regex-replace-all "tf::?" content "")
,即更换所有引用到软件包tf
以避免与软件包相关的错误 - 将
'()
放在内容周围 -
read
it并分配到变量 - 现在您已经有了可以使用各种列表操纵功能来处理的结构化数据
不幸的是,这将是一个不可携带的解决方案:
- 处理读者错误
- 做解决问题所需的事情
- 继续
例如,在lispworks中,我可以做这样的事情(只是草图):
CL-USER 60 > (defun test ()
(handler-bind ((conditions:package-not-found-reader
(lambda (c)
(continue c)))
(conditions:simple-reader-error
(lambda (c)
(continue c))))
(read-from-string "'(foo27:bar19 bar18:foo44)")))
TEST
CL-USER 61 > (test)
(QUOTE (FOO27::BAR19 BAR18::FOO44))
它调用继续重新启动丢失的软件包错误,然后将符号未导出的错误。重新启动创建软件包,另一个正在返回一个非出口符号...