Lisp 当从另一个包调用时,子包不替换符号
在我的函数中,我从用户处读取输入,该输入应为以字符串形式给出的lisp表单,例如:Lisp 当从另一个包调用时,子包不替换符号,lisp,common-lisp,Lisp,Common Lisp,在我的函数中,我从用户处读取输入,该输入应为以字符串形式给出的lisp表单,例如: (sym1 sym2 (sym3 sym4)) 我的目标是用其他符号替换一些符号,例如: (sublis '((sym1 . sym1%) (sym2 . sym2%)) str) 因为我以字符串的形式获取输入,所以我首先将其转换为lisp表单。下面是最终函数的外观: (defun sublis-when-string (str) (sublis '((sym1 . s
(sym1 sym2 (sym3 sym4))
我的目标是用其他符号替换一些符号,例如:
(sublis '((sym1 . sym1%)
(sym2 . sym2%))
str)
因为我以字符串的形式获取输入,所以我首先将其转换为lisp表单。下面是最终函数的外观:
(defun sublis-when-string (str)
(sublis '((sym1 . sym1%)
(sym2 . sym2%))
(read-from-string str)))
当我编译函数并在REPL中使用(sublis When string“(sym1 sym3(sym2 sym4)))运行它时
我正确地得到:
(SYM1% SYM3 (SYM2% SYM4))
但是,当我运行整个程序时,替换不起作用:
(SYM1 SYM3 (SYM2 SYM4))
这使我相信,问题在于一揽子计划。当我在REPL中更改包时,替换仍然不起作用
我的问题是:如何更改我的函数,使其在从其他包调用时工作?如果您想独立于读取符号的包使用函数,可以通过添加显式测试来更改定义:当要检查的值是符号时,使用
string=
运算符代替默认的eql
。例如:
(defun sublis-when-string (str)
(sublis '((sym1 . sym1%)
(sym2 . sym2%))
(read-from-string str)
:test (lambda (x y)
(if (and (symbolp x) (symbolp y))
(string= x y)
(eql x y)))))
请参阅的定义。您可以为用户定义包:
(defpackage :my-user (:use) (:export #:sym1 #:sym2))
当然,导出的符号是需要添加到替换列表中的符号。然后,在读取之前绑定*package*
变量:
(let ((*package* (find-package :my-user)))
(read-from-string string))
请注意,所有符号都将从:my user
包中读取。
根据您对字符串源的信任程度,您还可以设置为nil并定义一个最低限度的可读表:
- 禁用分配非常大的数组(如
)的数组表示法(恶意用户故意耗尽内存)#n()
- 使
一个终止字符使合格的符号抛出错误(坏用户在其他包中插入符号):
“(sym1 sym2 sym3)”运行函数时,
我得到值(sym1 sym2 sym3)不是类型(或(矢量字符)(矢量零)基字符串符号字符)
,回溯的最后一行是:(字符串=(sym1 sym2 sym3)sym1)
@tsikov,抱歉,我没有检查函数Sublis
通过检查遇到的所有子树来执行“递归”检查,因此我将定义更改为仅在比较两个符号时使用string=
。很漂亮!然而伦佐的解决方案也有效,他是第一个,所以我接受了他的答案。我还必须同步两个符号列表——一个用于导出,另一个用于替换。顺便说一句,你提出了一个很好的观点——我在哪里可以读到更多关于净化用户输入的保护措施?@tsikov接受Renzo的好答案没问题。我没有关于净化用户输入的最佳实践的资源;这可能取决于许多不同的标准,如果你想成为真正的偏执狂,它可以走得很远(添加超时、限制内存等)。对于数组表示法,我想我记得Clojure中的一个潜在问题已经解决了。就像测试一样,我通常会考虑“如果”的情况。真的没什么可说的了,我也想有个导游