【问题标题】:Symbolic Programming and Packages in LISPLISP 中的符号编程和包
【发布时间】:2015-03-12 08:22:25
【问题描述】:

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

(defun animalp (thing)
  (if (member thing '(dog cat snail mouse)) t))

问起来很简单:

(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”并不在意,问题在于大量的动物。)本质上我想问:

(animalia:animalp 'dog)

符号(狗、猫等)没有任何属性 - 基本上我一直将它们用作字符串比较的简写:

(if (member "dog" '("dog" "cat" "mouse" "snail")
            :test #'string-equal) t)

所以我的问题是关于最佳实践。我不喜欢使用字符串——尤其是当 (eq 'dog 'dog) 在单个包中如此简单时。另一方面,我也没有对 (defpackage ... (:export :dog :cat ...)) 的前景感到欣喜若狂,并且需要用一个包(animialia:dog 等)来限定每只动物。最后一个明显的解决方案是让所有的动物关键字,如:

(if (member :dog '(:dog :cat :mouse :snail)) t)

但即使这样看起来也有点脏。

有哪些最佳实践解决方案可以实现我想要的,而不会弄得一团糟或诉诸幻想、丑陋和潜在脆弱解决方案的演变?

【问题讨论】:

    标签: lisp common-lisp


    【解决方案1】:

    请注意,string-equal 接受 字符串指示符,并且符号是字符串指示符。您可以非常灵活地用 string-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 更快,你必须要么

    1. 使用随处可见的关键字;或
    2. 使用在 animalia 包中定义的符号(并且可能在应该可以访问的包中导出和使用它们)。

    您也可以采用id256's answer 中的方法,首先将输入转换为字符串,intern,然后检查成员资格。但是,这必须涉及系统通过实习过程,这可能会破坏目的,尤其是对于这么小的动物名单。它还具有使 animalia 包混乱的潜在不良影响。但是,如果您愿意做 interning 之类的事情,那么从一开始就使用哈希表可能更有意义。

    【讨论】:

      【解决方案2】:

      带有关键字的解决方案非常标准并且工作正常,但是如果您想避免使用它并将符号从任何包传递给animalp,您可以在animalia 包中实习:

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

      请注意,intern 的第二个参数(用于实习符号的 PACKAGE)是可选的,默认情况下其值为*current-package*,即调用时的:cl-user。要解决这个问题,您可以显式使用:animalia

      【讨论】:

      • 这当然有效,但它有一个令人遗憾的副作用,就是将动物包中的符号弄得乱七八糟,而且尚不清楚快速符号比较的好处是否值得在每次调用中进行实习。 (当然,在这样的考虑中,我们也会遇到为什么动物在列表中而不是数组中等问题。)
      • 我不认为由于实习大量符号而导致的潜在堆耗尽是现实生活中的问题,除非有数百万个符号,这是相当罕见的。
      • 不,尺寸可能不是问题,但 do-symbols 的任何功能都可能会对 animalia 包中的大量“额外”符号感到惊讶。
      • 哦,是的,但我认为 do-symbol 与数百万个符号一样罕见,除非从头开始实现 lisp。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-02
      • 2011-08-03
      • 2019-09-28
      • 1970-01-01
      • 2017-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多