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

在我的函数中,我从用户处读取输入,该输入应为以字符串形式给出的lisp表单,例如:

(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()
    )的数组表示法(恶意用户故意耗尽内存)
  • 使
    一个终止字符使合格的符号抛出错误(坏用户在其他包中插入符号)

在SBCL上使用
“(sym1 sym2 sym3)”运行函数时,
我得到
值(sym1 sym2 sym3)不是类型(或(矢量字符)(矢量零)基字符串符号字符)
,回溯的最后一行是:
(字符串=(sym1 sym2 sym3)sym1)
@tsikov,抱歉,我没有检查函数
Sublis
通过检查遇到的所有子树来执行“递归”检查,因此我将定义更改为仅在比较两个符号时使用
string=
。很漂亮!然而伦佐的解决方案也有效,他是第一个,所以我接受了他的答案。我还必须同步两个符号列表——一个用于导出,另一个用于替换。顺便说一句,你提出了一个很好的观点——我在哪里可以读到更多关于净化用户输入的保护措施?@tsikov接受Renzo的好答案没问题。我没有关于净化用户输入的最佳实践的资源;这可能取决于许多不同的标准,如果你想成为真正的偏执狂,它可以走得很远(添加超时、限制内存等)。对于数组表示法,我想我记得Clojure中的一个潜在问题已经解决了。就像测试一样,我通常会考虑“如果”的情况。真的没什么可说的了,我也想有个导游