`
huangz
  • 浏览: 320549 次
  • 性别: Icon_minigender_1
  • 来自: 广东-清远
社区版块
存档分类
最新评论

《实用Common Lisp编程》第16-17章,面向对象细节补遗(1):辅助方法

阅读更多

在《实用common lisp编程》的十六和十七章里面,介绍了common lisp的面向对象支持方式,分别是广义函数和CLOS系统。

 

书本通过文字叙述得非常详细,但是没有附上相应的代码,本着“没有实验过就没有发言权”的求实态度,我决定从带修饰符的辅助方法、带继承的方法、继承和槽、以及多继承等几个主要知识点着手,在代码方面实现一遍,验证书中的内容。

 

带辅助方法的主方法

 

第十六章介绍了几个广义函数的修饰符,主要有 :around , :before 和 :after ,而被装饰符修饰的方法称之为“主方法”(primary method),它们的一般运行顺序如下:

 

1.如果被调用的主方法有 :around 辅助方法,先运行 :around ,在 :around 运行之后,有两种选择,一种是,不调用 call-next-method ,那么整个函数就宣告结束,主方法不会被运行。

 

另一方面,如果 :around 调用 call-next-method ,这时又出现两种情况:

1) 该主方法还有一个最相关的 :around 辅助方法,那么运行该 :around 函数。(一般是父类的 :around)

2) 该主方法没有最想关的 :around 辅助方法,那么函数的执行权交还给主方法,主方法按正常情况运行,也即是,先运行 :before (如果有的话) ,再运行主方法,最后运行 :after (如果有的话)。

 

2.如果被调用的主方法(及父类的同名主方法)有 :before 辅助方法,那么它们按照最相关的最先运行的顺序依次执行,其中,不必调用 call-next-method 。

 

3.如果被调用的主方法(及其父类的同名主方法)有 :after 辅助方法,那么它们按照最相关的最后运行的顺序依次执行,其中,不必调用 call-next-method 。

 

4.如果主方法没有辅助方法,或者它的辅助方法全部执行完毕,那么执行该主方法。

 

OK,基本规则就这么多,我们先从 :before 辅助方法开始。

 

 

:before 辅助方法,无继承

 

无继承的 :before 辅助方法将在主方法之前执行,而且, :before 辅助方法执行完毕之后,调用会自动转给主方法,因此,不必调用 call-next-method 。

 

 

(defgeneric greet (people))

(defclass person ()
    ())
    
(defmethod greet ((people person))
    (format t "greet ~%"))

(defmethod greet :before ((people person))
    (format t "greet :before ~%"))
 

 

以下正是我们想要的执行结果:

 

 

(greet (make-instance 'person))

greet :before 

greet 

NIL

 

 

 

:before 辅助方法,带继承

 

这次,我们用一个 animal 类作为 person 的父类,调用 greet 方法。

 

如果一切如常的话,它们的调用顺序应该是 person 类的 greet 的 :before 辅助函数,然后是 animal 类的 greet 的 :before 辅助函数,然后是 person 的 greet 方法。

 

 

(defgeneric greet (people))

(defclass animal ()
    ())
    
(defmethod greet :before ((people animal))
    (format t "animal's greet :before ~%"))

(defclass person (animal)
    ())
    
(defmethod greet ((people person))
    (format t "greet ~%"))

(defmethod greet :before ((people person))
    (format t "greet :before ~%"))

 

 

 执行结果:

 

 

(greet (make-instance 'person))

greet :before 

animal's greet :before 

greet 

NIL

 

 

OK,方法如我们所料般执行。

 

我们可以来讲讲 :after 辅助方法了。

 

 

:after 辅助方法,无继承

 

无继承的带 :after 的辅助函数,将在主方法之后执行。

 

 

(defgeneric greet (people))

(defclass person ()
    ())
    
(defmethod greet ((people person))
    (format t "greet ~%"))
    
(defmethod greet :after ((people person))
    (format t "greet :after ~%"))

 

执行:

 

(greet (make-instance 'person))

greet 

greet :after 

NIL


好极了,下一个。


:after 辅助方法,有继承

这次,我们继续继承 animal 类,并且 animal 类带有一个 greet 方法的 :after 辅助方法。

按书本所说,最不相关的 :after 最先运行,因此,它们的运行顺序应该是: person 类的 greet 方法,animal 类的 greet 方法的 :after 辅助方法, person 类的 greet 方法的 :after 辅助方法。

(defgeneric greet (people))

(defclass animal ()
    ())

(defmethod greet :after ((people animal))
    (format t "animal's greet :after ~%"))

(defclass person (animal)
    ())
    
(defmethod greet ((people person))
    (format t "greet ~%"))
    
(defmethod greet :after ((people person))
    (format t "greet :after ~%"))
 
测试:

(greet (make-instance 'person))
greet 
animal's greet :after 
greet :after 
NIL

OK,两个 :after 辅助方法, 第二相关的 animal 类的辅助方法先运行,然后才是第一相关的 person 类本身的辅助方法运行。

接着,来看看 :around 辅助方法。


:around 无继承,不调用 call-next-method

按书上所说,:arond 方法将在主方法以及其他辅助方法之前运行,它的优先级是最高的,而且,只有当 :around 调用 call-next-method 时,方法的执行权才会转回给主方法。

我们这里用一个无继承,不调用 call-next-method 的 ;around 作测试,如果一切正常的话,那么,它只会执行 :around 辅助方法,而不执行主方法或者其他辅助函数。

(defgeneric greet (people))

(defclass person ()
    ())
    
(defmethod greet ((people person))
    (format t "greet ~%"))

(defmethod greet :around ((people person))
    (format t ":around ~%"))

(defmethod greet :before ((people person))
    (format t ":before ~%"))
    
(defmethod greet :after ((people person))
    (format t ":after ~%"))
 
执行试试:

(greet (make-instance 'person))
:around 
NIL

WOW,这个 :around 拦截了所有其他方法,只有它自己运行了,真够霸道的,看来用它写一些诸如 memorize 函数的东西应该不错。


:around 无继承,调用 call-next-method

这次,我们在 :around 函数体内加上 (call-next-method) ,这样,函数的执行权就会转交回给被装饰的 greet 函数,那么函数就会以一般的执行顺序执行(:before -> 主方法 -> :after)。

(defgeneric greet (people))

(defclass person ()
    ())
    
(defmethod greet ((people person))
    (format t "greet ~%"))

(defmethod greet :around ((people person))
    (format t ":around ~%")
    (call-next-method)) ; new add

(defmethod greet :before ((people person))
    (format t ":before ~%"))
    
(defmethod greet :after ((people person))
    (format t ":after ~%"))
 
测试:

(greet (make-instance 'person))
:around 
:before 
greet 
:after 
NIL

嗯,先 :around ,后 :before ,接着 greet 主方法 ,最后 :after ,这就是我们要的。


:around 有继承,父子类都有 :around,都调用 call-next-method

嗯,现在我们有两个类 animal 和 person ,它们都有 :around ,而且两个 :around 都在函数体最后调用 call-next-method 。

按书本所说,它们的执行顺序应该是: person 的greet方法的 :around , animal 的 greet 方法的 :around ,然后是 person 的 greet 方法的 :before ,person 的 greet 主方法,最后是 person 的 greet 方法的 :after 。

(defgeneric greet (people))

(defclass animal ()
    ())

(defmethod greet :around ((people animal))
    (format t "animal's greet :around ~%")
    (call-next-method)) ; call person's greet :before

(defclass person (animal)
    ())

(defmethod greet ((people person))
    (format t "greet ~%"))

(defmethod greet :around ((people person))
    (format t ":around ~%")
    (call-next-method)) ; call animal's greet :around

(defmethod greet :before ((people person))
    (format t ":before ~%"))

(defmethod greet :after ((people person))
    (format t ":after ~%"))

测试:

(greet (make-instance 'person))
:around 
animal's greet :around 
:before 
greet 
:after 

嗯,一切正常。



:around ,带继承,父类的 :around 不调用 call-next-method 方法

(defgeneric greet (people))

(defclass animal ()
    ())

(defmethod greet :around ((people animal))
    (format t "animal's greet :around ~%"))
    ; no (call-next-method) anymore

(defclass person (animal)
    ()) 

(defmethod greet ((people person))
    (format t "greet ~%"))

(defmethod greet :around ((people person))
    (format t ":around ~%")
    (call-next-method)) ; call animal's greet :around

(defmethod greet :before ((people person))
    (format t ":before ~%"))

(defmethod greet :after ((people person))
    (format t ":after ~%"))
 
还有一点小疑问,就是,如果我们在 animal 类的 greet 方法的 :around 辅助方法里不调用 call-next-method ,会如何?

答案是, animal 类的 greet 方法的 :around 辅助函数就不会将执行权转回给 person 的 greet 主方法,而是就此结束了。

也即是,结果将是:

(greet (make-instance 'person))
:around 
animal's greet :around 
NIL


终极测试,没有主方法的辅助方法

测试进行到最后,忽然一个狂想从我的脑子里崩出来:一个没有主方法的辅助方法,可能运行吗?

这就来试试:

(defclass person ()
    ()) 

(defmethod greet :around ((people person))
    (format t ":around ~%"))
 
测试:

 (greet (make-instance 'person))

*** - NO-PRIMARY-METHOD: When calling #<STANDARD-GENERIC-FUNCTION GREET> with
      arguments (#<PERSON #x2058E72E>), no primary method is applicable.
The following restarts are available:
RETRY          :R1      try calling GREET again
RETURN         :R2      specify return values
ABORT          :R3      Abort main loop


噢噢,解释器在抱怨我们没有可应用的主方法,因此也证明,没有主方法的辅助方法,是不行滴。


小结

嗯,关于辅助函数的测试就此做完了,下一篇文章,我将测试关于方法的继承的相关问题。

















 

 

 

 


 

 

 

 

 

 

 

 

 

 

分享到:
评论
1 楼 zx371323 2012-03-06  
GOOD!

相关推荐

    实用Commonlisp编程

    这是一个Commonlisp编程的入门教程。很经典的一个教程。现在有机会,把它放在csdn上。欢迎下载。

    实用Common.Lisp编程

    这本《Practical Common Lisp》之所以号称Practical,正是因为这本书大量介绍Common Lisp在现实世界中的各种应用方式,算是第一本「入世传教」的Common Lisp著作。《Practical Common Lisp》是目前最畅销的Common ...

    实用Common Lisp编程 高清官方中文版

    这是一本不同寻常的 Common Lisp 入门书。本书首先从作者的学习经过及语言历史出发,随后用 21 个章节讲述了各种基础知识,主要包括 :REPL 及 Common Lisp 的各种实现、S- 表达式、函数与变量、标 准宏与自定义宏、...

    实用Common.Lisp编程.pdf

    实用Common.Lisp编程.pdf,2011.10出版

    实用Common Lisp编程

    本书首先从作者的学习经过及语言历史出发,随后用21个章节讲述了各种基础知识,主要包括:REPL 及Common Lisp 的各种实现、S- 表达式、函数与变量、标准宏与自定义宏、数字与字符以及字符串、集合与向量、列表处理、...

    实用Common Lisp编程 田春

    这是一本不同寻常的Common Lisp 入门书。本书首先从作者的学习经过及语言历史出发,随后用21 个章节讲述了各种基础知识,主要包括:REPL 及Common Lisp 的各种实现、S- 表达式、函数与变量、标 准宏与自定义宏、数字...

    Common.Lisp编程 中文版本

    《实用Common Lisp 编程》是一本不同寻常的Common Lisp 入门书。本书首先从作者的学习经过及语言历史出发,随后用21个章节讲述了各种基础知识,主要包括:REPL 及Common Lisp 的各种实现、S- 表达式、函数与变量、...

    Practical Common Lisp-1st-2005

    Practical Common Lisp-1st-2005,官方排版,我为大部分章节加了二级书签

    common-lisp-the-language-second-edition.PDF

    common-lisp-the-language-second-edition.PDF

    《实用Common Lisp编程》

    Common Lisp,缩写为 CL(不要和缩写同为CL的组合逻辑混淆),是Lisp的众多方言之一,标准由ANSI X3.226-1994定义。它是为了标准化此前众多的Lisp分支而开发的,它本身并不是一个具体的实现而是各个Lisp实现所遵循的...

    CommonLisp语言学习程序CommonLispKoans.zip

    Common Lisp Koans(lisp-koans)是一个语言学习练习程序,类似 ruby koans,python koans 等等。Common Lisp Koans 主要是帮助学习一些 lisp 规范特性和改进,可以学习到大量的 Common Lisp 语言特性。终端,在文件...

    Common LISP书籍中文版pdf格式带书签.rar

    内含ANSI Common Lisp+On Lisp+实用Common Lisp编程,带书签

    LISP和通用LISP编程LISP and Common LISP Programming

    这是LISP和Common LISP编程的上一页,我们正在处理中,将那里的所有书籍都转换为新页面。 请再次检查此页面!!!

    实用common lisp 编程

    lisp是古老的可编程的编程语言,与c语言是编程的两种极端。

    Common Lisp 高级编程技术

    关于LISP的翻译资料. [美] Paul Graham 著 Chun Tian (binghe),Kov Chai 译. 共有25章,翻译了1-15章、25章及附录 感谢作者和翻译者.

    Common-Lisp 教程 示例游戏_Common-Lisp_代码_下载

    cd ~/common-lisp/ (or other asdf searchable location) git clone https://github.com/rabbibotton/clos-encounters.git At your CL prompt (ql:quickload :clos-encounters) (clos-encounters:start-game) 更多...

    Successful Lisp How to Understand and Use Common Lisp - David B. Lamkins

    Provides an overview of Common Lisp for the working programmer. Introduces key concepts in an easy-to-read format. Describes format, typical use, and possible drawbacks of all important Lisp ...

    ansi common lisp 教程

    新的和更强大的方法教育学生的思考程序,该文本包含一个教程的例子,说明Lisp编程的基本概念,以及ANSI Common Lisp的一个跟上时代的总结,列出每个运营商在语言。信息和乐趣,它给学生提供了他们所需要的一切,开始...

    Lisp - 1990 - Common Lisp the Language, 2nd Edition - Steele, Guy L..pdf

    Common Lisp the Language, 2nd Edition经典,喜欢commonlisp的朋友们的少有资源

Global site tag (gtag.js) - Google Analytics