Common lisp 具有不同等式谓词的Lisp case

Common lisp 具有不同等式谓词的Lisp case,common-lisp,equality,predicate,Common Lisp,Equality,Predicate,作为Tic-Tac-Toe-playing机器人的一部分,我需要一个函数来计算点的瓷砖组合。代码如下所示: (case combination ("EEEEE" 0) ("EEEEP" 1) ("EEEPE" 1) ("EEEPP" 2) ("EEPEE" 1) ("EEPEP" 2) ("EEPPE" 2) ("EEPPP" 3) ("EPEEE" 1) ("EPEEP" 2) ("EPEPE" 2)

作为Tic-Tac-Toe-playing机器人的一部分,我需要一个函数来计算点的瓷砖组合。代码如下所示:

(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))
(case (intern combination)
  (EEEEE 0)
  (EEEEP 1)
  (EEEPE 1)
  ...)
(case (intern combination :keyword)
  (:EEEEE 0)
  (:EEEEP 1)
  (:EEEPE 1)
  ...)
这里不是讨论这种方法价值的地方,因为它用于与问题无关的原因

问题是case使用的谓词对于相同的字符串不返回true,而这些字符串不是很难找到的eq或eql对象。你怎么能改变这一点

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

使用alexandria:switch(来自alexandria库,可从quicklisp获得)

(switch (combination :test #'string=)
  ("FOO" …)
  …)
从alexandria库使用alexandria:开关,可从quicklisp获得

(switch (combination :test #'string=)
  ("FOO" …)
  …)
您的代码只计算count\p组合

通常我会将字符串转换成一个数字,然后用它进行计算。使用LOGCOUNT获取位或其他信息。即使我使用了一个类似大小写的开关,我也会将字符串转换为一个数字,而不是进行大量的字符串比较。

您的代码只计算count\p组合


通常我会将字符串转换成一个数字,然后用它进行计算。使用LOGCOUNT获取位或其他信息。即使我使用了一个类似大小写的开关,我也会将字符串转换为一个数字,而不是进行大量的字符串比较。

您可以编写一个宏:

(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个分支。在这些选项中,第二个生成的代码最多,然后是第一个,然后是第三个。

您可以编写宏:

(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个分支。在这些选项中,第二个生成的代码最多,然后是第一个,然后是第三个。

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

(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))
(case (intern combination)
  (EEEEE 0)
  (EEEEP 1)
  (EEEPE 1)
  ...)
(case (intern combination :keyword)
  (:EEEEE 0)
  (:EEEEP 1)
  (:EEEPE 1)
  ...)
但您需要记住,intern在运行时在当前包*package*的上下文中运行,这意味着如果这是在定义它的包之外调用的函数的一部分,它将无法工作。有两种方法可以解决这个问题,基本上是一种方法的两种变体:包中的intern intern组合或intern as关键字。在后一种情况下,整个表单将如下所示:

(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))
(case (intern combination)
  (EEEEE 0)
  (EEEEP 1)
  (EEEPE 1)
  ...)
(case (intern combination :keyword)
  (:EEEEE 0)
  (:EEEEP 1)
  (:EEEPE 1)
  ...)

同样值得注意的是性能方面的考虑。它们不应该是一个问题,因为虽然实习是一个相当繁重的操作,但一开始,当对已经实习的符号重复调用时,基本上只是一个字典查找。

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

(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))
(case (intern combination)
  (EEEEE 0)
  (EEEEP 1)
  (EEEPE 1)
  ...)
(case (intern combination :keyword)
  (:EEEEE 0)
  (:EEEEP 1)
  (:EEEPE 1)
  ...)
但您需要记住,intern在运行时在当前包*package*的上下文中运行,这意味着如果这是在定义它的包之外调用的函数的一部分,它将无法工作。有两种方法可以解决这个问题,基本上是一种方法的两种变体:包中的intern intern组合或intern as关键字。在后一种情况下,整个表单将如下所示:

(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))
(case (intern combination)
  (EEEEE 0)
  (EEEEP 1)
  (EEEPE 1)
  ...)
(case (intern combination :keyword)
  (:EEEEE 0)
  (:EEEEP 1)
  (:EEEPE 1)
  ...)

同样值得注意的是性能方面的考虑。它们不应该是一个问题,因为虽然插入是一个相当繁重的操作,但最初,当对已插入的符号重复调用时,基本上只是一个字典查找。

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

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

另一种解决方案是将规则定义为列表,并在列表中搜索匹配字符串

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

CASE使用EQL谓词。模式匹配?CASE使用EQL谓词。模式匹配?这就是我在括号中添加该部分的原因。这段代码只是我发明更好的积分系统之前使用的占位符,数字可能不同。我需要能够任意更改这些数字中的任何一个。这就是为什么我添加了括号中的部分。这段代码只是我发明更好的积分系统之前使用的占位符,数字可能不同。我需要能够任意更改这些数字中的任何一个。我不打算只计算数字
P's。在我发明更好的计分系统之前,这只是一个占位符。我不打算只计算P。在我发明更好的积分系统之前,这只是一个占位符。