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.
。但是实例方法名称的前面不可以。
- class Robot
- def ping
- @@count += 1
- end
- def self.count
- @@count
- end
- end
但是,只修改两处的的方法定义的话显然不够。试着运行的话便会出现下面的错误信息。
robot.rb:3:in `ping': uninitialized class variable @@count in Robot (NameError)
类变量的 @@count
未被定义。
应该在哪里执行类变量的初始化呢。
下面的代码是笔名为 y@su 的先生写的对 Robot
类的定义。
- class Robot
- def initialize
- @@count ||= 0
- end
- def ping
- @@count += 1
- end
- def self.count
- @@count
- end
- end
这也能运行,但是构造函数 initialize
方法的作用是,将 Robot
实例初始化。在将类变量初始化的情况下不是那么适合。
类变量在类定义中初始化。
下面是示范解答。
- class Robot
- @@count = 0
- def ping
- @@count += 1
- end
- def self.count
- @@count
- end
- end
- r1 = Robot.new
- r1.ping
- r2 = Robot.new
- r2.ping
- r1.ping
- 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君在说着“很难得到正确答案”的同时,发来了下面的代码。
- require 'mod'
- class Klass
- include Mod
- end
- puts Klass.new.foo
- #puts Klass.foo # => undefined method `foo' for Klass:Class (NoMethodError)
一般的使用 include
方法取出模块便是这样。
下面的代码是 river125 的答案。
- require 'mod'
- class Klass
- include Mod
- def self.foo
- k = Klass.new
- k.foo
- end
- end
因为问题中写了“利用Mod
模块”,这也没错,但是要避免每次调用方法foo
都生成实例。
大詰君提供了了正确解答。
- require 'mod'
- class Klass
- extend Mod
- end
Ruby参考手册的Object,就 extend
方法 “把用参数指定的模块的实例方法作为self 特殊方法追加”进行了说明。
特殊方法是什么呢。
这是在与实例方法进行对比时使用的语言。别名为单例方法。
具有某个对象 obj
的方法,被分为实例方法和特殊方法。
实例方法被其对象的类定义,并被类的实例全体所共有。
与此相对,特殊方法在对象中被定义。
更准确的说法是,对象在所属的特殊类中被定义。
总之,被一个对象专有的方法就是特殊方法。
那么,像怎样写的时候,应该怎么做呢。
- class Klass
- extend Mod
- end
虽然方法 extend
的运行主体 self
未在表面出现,但是直接在类定义之下,所以类 Klass
就是 self
。
请回想一下在Ruby 语言里,类即是对象。
一句话,就是将模块 Mod
作为叫做类 Klass
的“对象”的特殊方法进行追加。
实际上,“类方法”就是类的特殊方法!
最后来看示范解答。
- require 'mod'
- class Klass
- class << self
- include Mod
- end
- end
class << self ... end
是定义特殊类时的写法。在其中定义方法的话,就成为类 Klass
的类方法。
虽然比使用 extend
时的代码要长,但是好处是在希望添加 Klass
独自的类方法 baz
时可以这样写。
- require 'mod'
- class Klass
- class << self
- include Mod
- def baz
- "Baz"
- end
- end
- end
因为明确了从模块引入的类方法与独自的类方法的关系,我喜欢下面这样的写法。
此外,还可以重写私有方法 bar
。
- require 'mod'
- class Klass
- class << self
- include Mod
- private
- def bar
- "BAR"
- end
- end
- end
顺便说一下,下面这样大体上也能重写,但是 bar
便成了公共方法。
- require 'mod'
- class Klass
- extend Mod
- private
- def self.bar
- "BAR"
- end
- end
虽然 private
是一个将以后的方法定义的作用域私有的方法,但对于特殊类的方法定义却没有效果。
(2009/08/06)