在表中执行逐行操作(实现为列表列表)时,可以通过各自的列名称来引用单元格。我决定写一个快捷方式,这样我就不用写了
(elt row 5)
等等,可以写
(:col "Relevant Header")
代替。我没有太多使用Common Lisp的经验,我写了一个宏
(defmacro with-named-columns! (sequence-of-rows-symbol body &key headers)
(labels
((replace-col (form headers row-symbol)
(if (listp form)
(if (eql (car form) :col)
`(elt ,row-symbol ,(position (cadr form) headers :test #'equal))
(mapcar #'(lambda (x) (replace-col x headers row-symbol)) form))
form)))
(let ((row (gensym "ROW")))
`(map 'list
(lambda (,row) ,(replace-col body headers row))
,sequence-of-rows-symbol))))
虽然不完美,但似乎运作良好:
(defparameter *table* '(("h1" "h2") ("foo" "bar") ("" "baz") ("foo" "qux")))
(defparameter *sequence-of-rows* (cdr *table*))
(with-named-columns! *sequence-of-rows*
(when (equal (:col "h1") "")
'do-nothing-but-make-a-note)
:headers ("h1" "h2"))
=> (NIL DO-NOTHING-BUT-MAKE-A-NOTE NIL)
它宏观扩展了我的意图:
(macroexpand-1
'(with-named-columns! *sequence-of-rows*
(when (equal (:col "h1") "")
'do-nothing-but-make-a-note)
:headers ("h1" "h2")))
=>
(MAP 'LIST
(LAMBDA (#1=#:ROW724)
(WHEN (EQUAL (ELT #1# 0) "") 'DO-NOTHING-BUT-MAKE-A-NOTE))
*SEQUENCE-OF-ROWS*)
到目前为止一切顺利。但是,标题通常以第一行的形式附加到数据。对于这种情况,拥有一个单独的设施是很自然的:
(defmacro process-rows (symbol-bound-to-table-with-exactly-one-header-row body)
(let ((symbol-for-sequence-of-rows (gensym "SEQUENCE-OF-ROWS")))
`(let ((,symbol-for-sequence-of-rows (rest ,symbol-bound-to-table-with-exactly-one-header-row)))
(with-named-columns! ,symbol-for-sequence-of-rows
,body
:headers ,(first (symbol-value symbol-bound-to-table-with-exactly-one-header-row))))))
再次,似乎工作正常:
(process-rows *table*
(when (equal (:col "h1") "")
'do-nothing-but-make-a-note))
=> (NIL DO-NOTHING-BUT-MAKE-A-NOTE NIL)
Macroexpansion导致let表单中的宏,并且这个
似乎没有问题(macroexpand-1
'(process-rows *table*
(when (equal (:col "h1") "")
'do-nothing-but-make-a-note)))
=>
(LET ((#1=#:SEQUENCE-OF-ROWS726 (REST *TABLE*)))
(WITH-NAMED-COLUMNS! #1#
(WHEN (EQUAL (:COL "h1") "")
'DO-NOTHING-BUT-MAKE-A-NOTE)
:HEADERS ("h1" "h2")))
旁注:希望这个宏扩展能够说明为什么我发现向WITH-NAMED-COLUMNS!
提供明确的标题列表同时隐藏符号中的其余行是有益的。
然而,
(let ((table '(("h1" "h2") ("foo" "bar") ("" "baz") ("foo" "qux"))))
(process-rows table
(when (equal (:col "h1") "")
'do-nothing-but-make-a-note)))
调用调试器(SBCL),并显示消息“变量TABLE
未绑定。” - 在PROCESS-ROWS
的宏扩展期间。
我还不太了解Common Lisp评估过程。似乎PROCESS-ROWS
看不到词汇变量,我听说过这个问题。但我的另一个宏
(let ((sequence-of-rows '(("foo" "bar") ("" "baz") ("foo" "qux"))))
(with-named-columns! sequence-of-rows
(when (equal (:col "h1") "")
'do-nothing-but-make-a-note)
:headers ("h1" "h2")))
=> (NIL DO-NOTHING-BUT-MAKE-A-NOTE NIL)
在LET
的正文中评估得很好。我认为这两次扩展没有任何区别。有办法看吗?另外,我写了一个不同的宏(我打算主要使用的那个宏,而不是在顶层写LET
形式;事实上,它是最初在问题中表现出来的一个宏),宏观扩展到
(LET ((#1=#:TABLE727 (READ-CSV #2=#P"~/test.csv")))
(WRITE-CSV
(PROGN
(PROCESS-ROWS #1#
(WHEN (EQUAL (:COL "h1") "")
'DO-NOTHING-BUT-MAKE-A-NOTE))
#1#)
:STREAM #2#))
表示评估时出现相同的错误:“变量#:TABLENNN
未绑定。”我对PROCESS-ROWS
无法看到变量的方式感到困惑,因为那些#1=
和#1#
是对同一个地方的直接引用(很像PROCESS-ROWS
的宏展开)。显然,这并不保证任何事情。
如果有办法确保词汇捕获,我该怎么做,如果没有 - 我在哪个阶段进入了未指明行为的土地?或者也许我错过了一些简单的东西?
答案 0 :(得分:2)
我认为您的问题是一个相对常见的问题,通常可以通过将“低级宏”(with-named-columns!
)转换为函数来解决宏观扩展问题。从“高级宏”(process-rows
)明确调用它时。参看ensure-generic-function
vs defgeneric
。
我建议您尝试使用macrolet
or flet
并实际定义本地函数或宏(例如(h1)
)而不是非常非常lispy语法(:col "h1")
。
答案 1 :(得分:2)
第一个错误
使用像这样的宏是一个典型的错误。只需将其写成功能风格 - 它就会容易得多。
第二个错误
window.onerror
您无法遍历代码树并替换类似的内容。您实现的树步骤完全不尊重Lisp语法。人们需要一个代码walker,它实际上理解Lisp语法。或者使用类似try {
}
catch (error)
{
const e = new ErrorEvent('error', {message:'my error', error:error})
window.dispatchEvent(e)
}
或(defmacro with-named-columns! (sequence-of-rows-symbol body &key headers)
(labels
((replace-col (form headers row-symbol)
(if (listp form)
(if (eql (car form) :col)
`(elt ,row-symbol ,(position (cadr form) headers :test #'equal))
(mapcar #'(lambda (x) (replace-col x headers row-symbol)) form))
form)))
(let ((row (gensym "ROW")))
`(map 'list
(lambda (,row) ,(replace-col body headers row))
,sequence-of-rows-symbol))))
的内容(由用户sds提及)。
错误:绑定可能不存在,因此您无法访问
macrolet
编译时<{em>}的flet
的符号值是什么?在宏扩展时间?
编译时<{em>}的(defmacro process-rows (symbol-bound-to-table-with-exactly-one-header-row body)
(let ((symbol-for-sequence-of-rows (gensym "SEQUENCE-OF-ROWS")))
`(let ((,symbol-for-sequence-of-rows (rest ,symbol-bound-to-table-with-exactly-one-header-row)))
(with-named-columns! ,symbol-for-sequence-of-rows
,body
:headers ,(first (symbol-value symbol-bound-to-table-with-exactly-one-header-row))))))
的符号值是什么?在宏扩展时间?请注意,如果编译代码并且*table*
无法访问不存在的词法绑定,则table
尚未执行。 LET
实际上根本无法访问词汇绑定。
注意:对symbol-value
的调用由宏在宏扩展时间运行,因为在反引号表单的子表达式前面有一个逗号。
在宏扩展期间,您根本不能或不应该使用使用运行时值。因此,当您编写和使用宏时,您需要传递实际数据,以便在宏扩展时知道它。
备选方案:
摘要:词法绑定不是符号的值。动态绑定可能不存在。