【问题标题】:Common Lisp: Passing an object to a methodCommon Lisp:将对象传递给方法
【发布时间】:2018-01-21 13:51:25
【问题描述】:

我对对象(类实例)的行为有疑问。

代码示例:

(defclass game-cards ()
  ((card-symbol :initarg :card-symbol :accessor card-symbol)
   (colour      :initarg :colour      :accessor colour)))

(defvar *king-hearts* (make-instance 'game-cards
                                     :card-symbol 'King
                                     :colour 'hearts))
(defvar *ace-spades*  (make-instance 'game-cards
                                     :card-symbol 'Ace
                                     :colour 'spades))

(defclass game-states ()  
  ((my-cards    :initarg :my-cards    :accessor my-cards)                                
   (other-cards :initarg :other-cards :accessor other-cards)))

(defparameter *state-1*
    (make-instance 'game-states
                   :my-cards    '(*king-hearts* *ace-spades*)
                   :other-cards ()))


(defmethod play-game ((state game-states))
  (some-job (first (my-cards state))))

(defmethod some-job ((card game-cards))
  (colour card))

当 some-job 与参数列表中的游戏卡对象一起使用时,它会像我预期的那样工作。

CL-USER>  (some-job  *king-hearts*)
HEARTS
CL-USER> 

这也有效:

CL-USER> (first (my-cards *state-1*))
*KING-HEARTS*
CL-USER> 

当我尝试这个时:

(some-job (first (my-cards *state-1*)))

我收到以下错误消息:

There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SOME-JOB (1)>
when called with arguments
  (*KING-HEARTS*).
   [Condition of type SIMPLE-ERROR]

当我将 some-job 定义为函数时:

(defun some-job-1 (card)
  (colour card)) 

同样的行为发生。

现在的错误信息是:

There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::COLOUR (1)>
when called with arguments
  (*KING-HEARTS*).
   [Condition of type SIMPLE-ERROR]

现在*king-hearts* 似乎没有通过某些工作和颜色区分为游戏卡的实例。

是什么原因?为你的答案加油。

【问题讨论】:

  • 您不是在传递一个对象,而是在传递一个符号(即变量本身)。您的问题可以简化为(some-job '*king-hearts*)。
  • 试试:my-cards (list *king-hearts* *ace-spades*)。
  • @melpomene:避免这种情况的正确方法是什么?
  • @melpomene:好的,谢谢你。

标签: common-lisp


【解决方案1】:

不评估引用的数据。这是基本的 Lisp 评估规则:

CL-USER 1 > pi
3.141592653589793D0

CL-USER 2 > 'pi
PI

CL-USER 3 > '(pi pi)
(PI PI)

CL-USER 4 > (list pi pi)
(3.141592653589793D0 3.141592653589793D0)

CL-USER 5 > (list 'pi 'pi)
(PI PI)

这里PI是一个符号而不是一个数字:

CL-USER 6 > (type-of 'pi)
SYMBOL

CL-USER 7 > (type-of pi)
DOUBLE-FLOAT

因此我们可以为数字定义一个方法:

CL-USER 8 > (defmethod square ((n number)) (* n n))
#<STANDARD-METHOD SQUARE NIL (NUMBER) 402005F60B>

CL-USER 9 > (square pi)
9.869604401089358D0

但是对符号的调用不起作用,因为只有一个数字的方法:

CL-USER 10 > (square 'pi)

Error: No applicable methods for #<STANDARD-GENERIC-FUNCTION SQUARE 4060010C1C> with args (PI)
  1 (continue) Call #<STANDARD-GENERIC-FUNCTION SQUARE 4060010C1C> again
  2 (abort) Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

我们可以在调试器中为符号定义一个方法:

CL-USER 11 : 1 > (defmethod square ((n symbol))
                   (let ((n (symbol-value n)))
                     (* n n)))
#<STANDARD-METHOD SQUARE NIL (SYMBOL) 4020285ED3>

然后我们重新调用:

CL-USER 12 : 1 > :c 1
9.869604401089358D0

如何解决您的问题:

  • 使用LIST 创建一个 CLOS 对象列表
  • 或使用SYMBOL-VALUE从全局变量中检索CLOS对象。

后者通常意义不大。

【讨论】:

  • @Rainer Joswig 谢谢。我已经阅读了引号的含义,但没有正确掌握符号和变量名之间的关系。据我所知,'(*king-hearts* *ace-spades*) 是未评估变量的列表,这意味着符号列表。因此,传递未在列表定义中评估的变量意味着传递符号。
  • @ Rainer Joswig 您提出的第二种解决方案(使用symbol-value)具有优势,即我可以在repl 中以有意义的名称玩纸牌游戏,并且可以检查某些功能是否有效使用符号列表正确(例如浅滩卡)。
  • @N.Böck:这是 Lisp 中的双重用途:符号和列表本身就是数据。在程序中,列表是Lisp 形式(-> 函数调用、特殊形式、宏形式),而符号可以提供变量。一旦你在 Lisp 程序中引用它们,它们就是数据。这与许多其他编程语言不同,其中代码和数据不重叠。
  • @N.Böck:通常人们会直接使用 CLOS 对象而不为它们传递符号。
  • @Rainer Joswig:好的,我对常见的 lisp 和面向对象编程都是新手。我将为game-cards 类定义一个额外的插槽,其中包含卡名称作为字符串。因此,如果我需要卡片名称,我可以使用字符串而不是符号名称。
猜你喜欢
  • 2013-07-26
  • 2020-09-25
  • 2019-11-03
  • 2011-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 2011-09-27
相关资源
最近更新 更多