具有不同相等谓词的Lisp案例

时间:2018-12-01 22:31:46

标签: common-lisp equality predicate

作为Tic Tac Toe玩机器人的一部分,我需要一个函数来评估图块到点的组合。代码看起来像这样:

(case combination
    ("EEEEE" 0)
    ("EEEEP" 1)
    ("EEEPE" 1)
    ("EEEPP" 2)
    ("EEPEE" 1)
    ("EEPEP" 2)
    ("EEPPE" 2)
    ("EEPPP" 3)
    ("EPEEE" 1)
    ("EPEEP" 2)
    ("EPEPE" 2)
    ("EPEPP" 3)
    ("EPPEE" 2)
    ("EPPEP" 3)
    ("EPPPE" 3)
    ("EPPPP" 4)
    ("PEEEE" 1)
    ("PEEEP" 2)
    ("PEEPE" 2)
    ("PEEPP" 3)
    ("PEPEE" 2)
    ("PEPEP" 3)
    ("PEPPE" 3)
    ("PEPPP" 4)
    ("PPEEE" 2)
    ("PPEEP" 3)
    ("PPEPE" 3)
    ("PPEPP" 4)
    ("PPPEE" 3)
    ("PPPEP" 4)
    ("PPPPE" 4)
    ("PPPPP" 5))

(这里不是讨论这种方法的价值的地方,因为它用于与问题无关的原因)

问题在于,这种情况使用的谓词对于不是同一对象的相同字符串不会返回true(很难确定它是eq还是eql)。您该如何更改?

编辑:我通过将字符串转换为相应的二进制数解决了原始问题,可以使用eql比较该数字或将其用作列表中的索引。

5 个答案:

答案 0 :(得分:7)

使用alexandria:switch库中的alexandria(可从quicklisp获取)。

(switch (combination :test #'string=)
  ("FOO" …)
  …)

答案 1 :(得分:3)

您的代码仅计算出(count #\P combination)

通常,我将字符串转换为数字并进行计算。使用LOGCOUNT获取接通位或其他内容。即使我使用类似CASE的大型开关,我也会将字符串一次转换为数字,而不是进行大量字符串比较。

答案 2 :(得分:2)

您可以编写一个宏:

(defmacro string-case (key &rest forms)
  (let ((k (gensym "KEY")))
    `(let ((,k ,key))
       (cond
         ,@(loop for (str . body) in forms
                 collect `((string= ,k ,str) ,@body))))))

然后像情况一样使用它。请注意,此宏将一次检查每个子字符串(在您的情况下,最多检查32个分支),其效率比例如查看第一个字符并确定要做什么,然后查看下一个字符等等(5在您的情况下为-10个分支),其效率不如您实际打算的(例如,对#\P进行计数)(可以通过5-6个容易预测的分支来完成,或者根据实现的不同而选择10个)。在这些选项中,第二个生成最多的代码,然后是第一个,然后是第三个。

答案 3 :(得分:2)

另一种方法是将combination转换为符号。生成的代码将如下所示:

(case (intern combination)
  (EEEEE 0)
  (EEEEP 1)
  (EEEPE 1)
  ...)

但是请记住,intern在当前程序包(*package*)的上下文中运行,这意味着如果这是函数的一部分,则该函数在程序包外部被调用在定义的地方将无法使用。有两种方法可以解决此问题(基本上是一种方法的两个变体):打包中的实习生((intern combination <your-package>))或作为关键字的实习生。在后一种情况下,整个表单将如下所示:

(case (intern combination :keyword)
  (:EEEEE 0)
  (:EEEEP 1)
  (:EEEPE 1)
  ...)

同样值得注意的是性能方面的考虑。尽管intern ing是一项相当繁重的操作,但它们应该不成问题,最初,当它在已经被中断的符号上重复调用时,基本上只是一个字典查找。

答案 4 :(得分:1)

另一种解决方案可能是将规则定义为列表,然后在列表中搜索匹配的字符串。

(defun match-combination (combination)
  (let ((rules '(("EEEEE" 0)
                 ("EEEEP" 1)
                 ("EEEPE" 1)
                         ...)))
    (cadr (find combination rules :key #'car :test #'string=))))
相关问题