LISP语言中的符号编程与软件包

LISP语言中的符号编程与软件包,lisp,common-lisp,Lisp,Common Lisp,给定以下函数,在REPL处输入: (defun animalp (thing) (if (member thing '(dog cat snail mouse)) t)) 问起来相当简单: (animalp 'dog) (animalia:animalp 'dog) 拆分成包后,事情变得更加复杂: (in-package :common-lisp-user) (defpackage :animalia (:use :common-lisp) (:export :animalp)

给定以下函数,在REPL处输入:

(defun animalp (thing)
  (if (member thing '(dog cat snail mouse)) t))
问起来相当简单:

(animalp 'dog)
(animalia:animalp 'dog)
拆分成包后,事情变得更加复杂:

(in-package :common-lisp-user)
(defpackage :animalia
  (:use :common-lisp)
  (:export :animalp))

(in-package :animalia)
(defun animalp (thing)
  (if (member thing '(dog cat snail mouse)) t))
现在,人们仍然可以问同样的问题:

(animalia:animalp 'animalia::dog)
但它越来越乱了。(我对“animalia:animalp”并不在意,问题出在大量的动物身上。)基本上,我想问:

(animalp 'dog)
(animalia:animalp 'dog)
这些符号(狗、猫等)没有任何属性——基本上我一直在使用它们作为字符串比较的简写:

(if (member "dog" '("dog" "cat" "mouse" "snail")
            :test #'string-equal) t)
所以我的问题是关于最佳实践。我不喜欢使用字符串,尤其是当(eq'dog'dog)在单个包中非常简单时。另一方面,我也不为(defpackage…(:export:dog:cat…)的前景和需要用一个包(animalia:dog等)来鉴定每只动物而感到高兴。最后一个显而易见的解决方案是让所有动物都成为关键词,比如:

(if (member :dog '(:dog :cat :mouse :snail)) t)
但即使这样看起来也有点脏


有哪些最佳实践解决方案可以实现我想要的,而不造成完全混乱或求助于奇幻、丑陋和潜在脆弱解决方案的演变?

带有关键字的解决方案非常标准,效果很好,但是如果您想避免使用它,并将符号从任何包传递到
animalp
,则,您可以在
animalia
软件包中实习:

(in-package :animalia)
(defun animalp (thing)
   (and (member (intern (string thing) :animalia)
                '(dog cat snail mouse))
        t))

请注意,
intern
(PACKAGE to intern the symbol in)的第二个参数是可选的,默认情况下它的值是
*当前PACKAGE*
,即调用时的
:cl user
。为了解决这个问题,您可以显式地使用
:animalia

注意字符串相等接受字符串指示符,并且符号是字符串指示符。您可以非常灵活地使用字符串equal定义animalp。这意味着您正在进行字符串比较,但不需要在代码的其他位置使用实际字符串

(defpackage #:animalia
  (:use "COMMON-LISP"))

(in-package #:animalia)

(defun animalp (x)
  (if (member x '(cat dog fish)
              :test 'string-equal)
      t))

(in-package #:cl-user)

(animalia::animalp :fish)            ;=> T  (keyword)
(animalia::animalp 'cl-user::fish)   ;=> T  (non-animalia package)
(animalia::animalp 'animalia::fish)  ;=> T  (animalia package)
(animalia::animalp '#:fish)          ;=> T  (no package)

(animalia::animalp "fish")           ;=> T  (strings, any      
(animalia::animalp "FISH")           ;=> T   case is OK)
如果你想让animalp更快,你必须

  • 使用随处可见的关键词;或
  • 使用animalia软件包中定义的符号(可能会导出并在应该可以访问的软件包中使用它们)

  • 您还可以采用这样的方法,首先将输入转换为字符串,internit,然后检查成员资格。然而,这必须涉及到整个系统的实习过程,这可能会破坏目的,特别是对于这样一小部分动物。它还有潜在的不良影响,会弄乱你的animalia软件包。不过,如果你愿意做一些类似于实习的事情,那么首先使用哈希表可能更有意义

    这当然有效,但它有一个不幸的副作用,那就是把animalia软件包中的符号弄得乱七八糟,而且还不清楚快速比较符号的好处是否值得在每次通话中都进行实习。(当然,在这样的考虑中,我们也会有一些问题,为什么动物会出现在一个列表中而不是一个数组中,等等)我不认为由于插入大量符号而导致的潜在堆溢出是现实生活中的问题,除非有数以百万计的符号,这是相当罕见的。不,大小可能不是一个问题,但是,任何这样做的事情都可能会对animalia包中的大量“额外”符号感到惊讶。哦,是的,但我认为do符号和数百万符号一样罕见,除非一个人是从头开始实现lisp。