ruby问题(转载)

Ruby 练习问题集

类变量与类方法

执行下面的程序的话便发生了错误。请将程序进行修改以能正确运行。

class Robot def self.ping @@count += 1 end def count @@count end end r1 = Robot.new r1.ping r2 = Robot.new r2.ping r1.ping puts Robot.count

Robot类本来的形式是像下面这样:

  • 调用实例方法ping的话类的共有计数器便增加1个。
  • 调用类方法count的话便返回现在计数器的值。

在终端执行此程序的话,画面上显示3。

--
黒田努


解答与说明的显示・隐藏

解答与说明

这次完全不存在让人感到棘手的地方。实际上只要运行Ruby解释器进行修改的话,就可在比较短的时间内解决问题。

但是如果让你只用铅笔和纸来解决问题的话又怎么样呢。

我想正确率一定很低。

从寄来解答的诸君那里,也听到了“这次算是又学习了”的感慨。

***

先简单的温习一下类变量与类方法吧。

类变量在名称的前面添加 @@ 符号表示。定义了此项的类,便成为被子变量、实例所共有的变量。不能从除此之外的对象查看。

类方法是类提供的“方便的的函数(功能)”。与实例方法查看、改变类的状态相对照。类方法也可以从类的外部进行利用。

***

于是很快能发现的是,类方法与实例方法的定义是相反的。类方法名称的前面可以添加self. 。但是实例方法名称的前面不可以。

  1. class Robot  
  2.   def ping  
  3.     @@count += 1  
  4.   end  
  5.   
  6.   def self.count  
  7.     @@count  
  8.   end  
  9. end  

但是,只修改两处的的方法定义的话显然不够。试着运行的话便会出现下面的错误信息。

 

robot.rb:3:in `ping': uninitialized class variable @@count in Robot (NameError)

类变量的 @@count 未被定义。

应该在哪里执行类变量的初始化呢。

下面的代码是笔名为 y@su 的先生写的对 Robot 类的定义。

  1. class Robot  
  2.   def initialize  
  3.     @@count ||= 0  
  4.   end  
  5.   
  6.   def ping  
  7.     @@count += 1  
  8.   end  
  9.   
  10.   def self.count  
  11.     @@count  
  12.   end  
  13. end  

这也能运行,但是构造函数 initialize 方法的作用是,将 Robot 实例初始化。在将类变量初始化的情况下不是那么适合。

类变量在类定义中初始化。

下面是示范解答。

  1. class Robot  
  2.   @@count = 0  
  3.   
  4.   def ping  
  5.     @@count += 1  
  6.   end  
  7.   
  8.   def self.count  
  9.     @@count  
  10.   end  
  11. end  
  12.   
  13. r1 = Robot.new  
  14. r1.ping  
  15.   
  16. r2 = Robot.new  
  17.   
  18. r2.ping  
  19. r1.ping  
  20.   
  21. puts Robot.count  

大詰君、MTG君、river125君的代码,与示范解答完全一样,在此感谢。

(2009/07/30)

Ruby 练习问题集

模块与类(1)

在当前目录中存在一个下列内容的文件 mod.rb 。

module Mod def foo "Foo" + bar end private def bar "Bar" end end

请利用Mod 模块,定义具有类方法 foo 的类 Klass 。

类 Klass 的利用例如下。

puts Klass.foo

最后在终端输出 FooBar 。

--
黒田努


解答与说明的显示・隐藏

解答与说明

这是一个怎样将模块作为类方法引用的问题。

好像有一点难的样子。

MTG君在说着“很难得到正确答案”的同时,发来了下面的代码。

  1. require 'mod'  
  2.   
  3. class Klass  
  4.   include Mod  
  5. end  
  6.   
  7. puts Klass.new.foo  
  8.   
  9. #puts Klass.foo # => undefined method `foo' for Klass:Class (NoMethodError)  

一般的使用 include 方法取出模块便是这样。

下面的代码是 river125 的答案。

  1. require 'mod'  
  2.   
  3. class Klass  
  4.   include Mod  
  5.   
  6.   def self.foo  
  7.     k = Klass.new  
  8.     k.foo  
  9.   end  
  10. end  

因为问题中写了“利用Mod 模块”,这也没错,但是要避免每次调用方法foo 都生成实例。

大詰君提供了了正确解答。

  1. require 'mod'  
  2.   
  3. class Klass  
  4.   extend Mod  
  5. end  

Ruby参考手册的Object,就 extend 方法 “把用参数指定的模块的实例方法作为self 特殊方法追加”进行了说明。

特殊方法是什么呢。

这是在与实例方法进行对比时使用的语言。别名为单例方法。

具有某个对象 obj 的方法,被分为实例方法和特殊方法。

实例方法被其对象的类定义,并被类的实例全体所共有。

与此相对,特殊方法在对象中被定义。

更准确的说法是,对象在所属的特殊类中被定义。

总之,被一个对象专有的方法就是特殊方法。

那么,像怎样写的时候,应该怎么做呢。

  1. class Klass  
  2.   extend Mod  
  3. end  

虽然方法 extend 的运行主体 self 未在表面出现,但是直接在类定义之下,所以类 Klass 就是 self 。

请回想一下在Ruby 语言里,类即是对象。

一句话,就是将模块 Mod 作为叫做类 Klass 的“对象”的特殊方法进行追加。

实际上,“类方法”就是类的特殊方法!

* * *

最后来看示范解答。

  1. require 'mod'  
  2.   
  3. class Klass  
  4.   class << self  
  5.     include Mod  
  6.   end  
  7. end  

class << self ... end 是定义特殊类时的写法。在其中定义方法的话,就成为类 Klass 的类方法。

虽然比使用 extend 时的代码要长,但是好处是在希望添加 Klass 独自的类方法 baz 时可以这样写。

  1. require 'mod'  
  2.   
  3. class Klass  
  4.   class << self  
  5.     include Mod  
  6.       
  7.     def baz  
  8.       "Baz"  
  9.     end  
  10.   end  
  11. end  

因为明确了从模块引入的类方法与独自的类方法的关系,我喜欢下面这样的写法。

此外,还可以重写私有方法 bar 。

 

  1. require 'mod'  
  2.   
  3. class Klass  
  4.   class << self  
  5.     include Mod  
  6.   
  7.     private      
  8.     def bar  
  9.       "BAR"  
  10.     end  
  11.   end  
  12. end  

顺便说一下,下面这样大体上也能重写,但是 bar 便成了公共方法。

  1. require 'mod'  
  2.   
  3. class Klass  
  4.   extend Mod  
  5.     
  6.   private  
  7.   def self.bar  
  8.     "BAR"  
  9.   end  
  10. end  

虽然 private 是一个将以后的方法定义的作用域私有的方法,但对于特殊类的方法定义却没有效果。

(2009/08/06)

posted @ 2012-08-02 14:31  江南轻舞飞扬  阅读(137)  评论(0编辑  收藏  举报