ruby
如果您是C++
程序员并且需要在Ruby中工作,则需要学习一些知识。 本文讨论了Ruby新手可能会误解的六种Ruby功能,尤其是当他或她来自类似但并非相当的环境(例如C++
,尤其如此:
- Ruby类层次结构
- Ruby中的单例方法
-
self
关键字 -
method_missing
方法 - 异常处理
- 穿线
注意:本文中的所有代码均经过测试,并基于Ruby 1.8.7版。
Ruby中的类层次结构
Ruby中的类层次结构可能很棘手。 创建一个Cat
类型的类,并开始使用其层次结构(请参见清单1 )。
清单1. Ruby中的隐式类层次结构
irb(main):092:0> class Cat
irb(main):093:1> end
=> nil
irb(main):087:0> c = Cat.new
=> #<Cat:0x2bacb68>
irb(main):088:0> c.class
=> Cat
irb(main):089:0> c.class.superclass
=> Object
irb(main):090:0> c.class.superclass.superclass
=> nil
irb(main):091:0> c.class.superclass.superclass.superclass
NoMethodError: undefined method `superclass' for nil:NilClass
from (irb):91
from :0
如清单1所示,Ruby中的所有对象(甚至用户定义的对象)都是Object
类的后代。这与C++
形成鲜明对比。 没有什么比普通的数据类型更好,例如C/C++ int
或double
。 清单2显示了整数1的类层次结构。
清单2. 1的类层次结构
irb(main):100:0> 1.class
=> Fixnum
irb(main):101:0> 1.class.superclass
=> Integer
irb(main):102:0> 1.class.superclass.superclass
=> Numeric
irb(main):103:0> 1.class.superclass.superclass.superclass
=> Object
到目前为止,一切都很好。 现在您知道类本身是Class
类型的对象。 最终, Class
最终从Object
派生,如清单3所示 ,它具有Ruby内置的String
类。
清单3.类的类层次结构
irb(main):100:0> String.class
=> Class
irb(main):101:0> String.class.superclass
=> Module
irb(main):102:0> String.class.superclass.superclass
=> Object
Module
是Class
的基类,它带有一个警告,即您不能直接实例化用户定义的Module
对象。 如果您不想使用Ruby内部结构,可以考虑将Module
具有与C++
名称空间相似的特性:您可以定义自己的方法,常量等。 您将一个Module
包含在Class
,然后,该Module
所有元素现在都神奇地成为Class
的元素。 清单4提供了一个示例。
清单4.模块不能直接实例化,只能与类一起使用
irb(main):020:0> module MyModule
irb(main):021:1> def hello
irb(main):022:2> puts "Hello World"
irb(main):023:2> end
irb(main):024:1> end
irb(main):025:0> test = MyModule.new
NoMethodError: undefined method `new' for MyModule:Module
from (irb):25
irb(main):026:0> class MyClass
irb(main):027:1> include MyModule
irb(main):028:1> end
=> MyClass
irb(main):029:0> test = MyClass.new
=> #<MyClass:0x2c18bc8>
irb(main):030:0> test.hello
Hello World
=> nil
那么,这里是回顾:当您在Ruby中编写c = Cat.new
时, c
是Cat
类型的对象,该对象派生自Object
。 Cat
类是Class
类型的对象,该对象从Module
派生,而Module
又从Object
派生。 因此,对象及其类型都是有效的Ruby对象。
单例方法和可编辑类
现在,看一下单例方法。 假设您想用C++
建模类似于人类社会的模型。 你会怎么做? 有一个名为Human
的类,然后有数百万的Human
对象? 这更像是为僵尸社会建模。 每个人都必须具有一些独特的特征。 Ruby的singleton方法在这里派上用场,如清单5所示 。
清单5. Ruby中的单例方法
irb(main):113:0> y = Human.new
=> #<Human:0x319b6f0>
irb(main):114:0> def y.paint
irb(main):115:1> puts "Can paint"
irb(main):116:1> end
=> nil
irb(main):117:0> y.paint
Can paint
=> nil
irb(main):118:0> z = Human.new
=> #<Human:0x3153fc0>
irb(main):119:0> z.paint
NoMethodError: undefined method `paint' for #<Human:0x3153fc0>
from (irb):119
Ruby中的Singleton方法是仅与特定对象相关联的方法,不适用于常规类。 它们以对象名称为前缀。 在清单5中 , paint
方法专门针对对象y
和y
; z.paint
导致未定义的方法错误。 您可以通过调用singleton_methods
找出对象中的单例方法列表:
irb(main):120:0> y.singleton_methods
=> ["paint"]
还有另一种在Ruby中定义单例方法的方法。 考虑清单6中的代码。
清单6.创建单例方法的另一种方法
irb(main):113:0> y = Human.new
=> #<Human:0x319b6f0>
irb(main):114:0> class << y
irb(main):115:1> def sing
irb(main):116:1> puts "Can sing"
irb(main):117:1> end
irb(main):118:1>end
=> nil
irb(main):117:0> y.sing
Can sing
=> nil
清单5还为将新方法添加到用户定义的类以及内置的Ruby现有类(如String
有趣的可能性。 除非您可以访问所用类的源代码,否则在C++
这是不可能的。 再次查看String
类( 清单7 )。
清单7. Ruby允许您修改现有的类
irb(main):035:0> y = String.new("racecar")
=> "racecar"
irb(main):036:0> y.methods.grep(/palindrome/)
=> [ ]
irb(main):037:0> class String
irb(main):038:1> def palindrome?
irb(main):039:2> self == self.reverse
irb(main):040:2> end
irb(main):041:1> end
irb(main):050:0> y.palindrome?
=> true
清单7清楚地说明了如何编辑现有的Ruby类以添加您选择的方法。 在这里,我添加了palindrome?
String
类的方法。 因此,Ruby类在运行时是可编辑的-一个强大的属性。
现在,您已经对Ruby的类层次结构和单例有了一些了解,让我们继续学习self
。 注意我在定义palindrome?
使用了self
palindrome?
方法。
发现自我
self
关键字最常见的用法可能是在Ruby类中声明一个静态方法,如清单8所示 。
清单8.使用self声明类的静态方法
class SelfTest
def self.test
puts "Hello World with self!"
end
end
class SelfTest2
def test
puts "This is not a class static method"
end
end
SelfTest.test
SelfTest2.test
正如清单8的输出所示 ( 清单9所示),没有对象就不能调用非静态方法。 行为类似于C++
。
清单9.在没有对象的情况下调用非静态方法时的错误
irb(main):087:0> SelfTest.test
Hello World with self!
=> nil
irb(main):088:0> SelfTest2.test
NoMethodError: undefined method 'test' for SelfTest2:Class
from (irb):88
在继续介绍self
更多深奥用法和含义之前,请注意,您还可以在Ruby中定义一个静态方法,方法是在类名之前添加类名:
class TestMe
def TestMe.test
puts "Yet another static member function"
end
end
TestMe.test # works fine
清单10提供了一个更有趣但看起来很困难的self
用法。
清单10.使用元类声明静态方法
class MyTest
class << self
def test
puts "This is a class static method"
end
end
end
MyTest.test # works fine
此代码以稍微不同的方式将test
定义为类静态方法。 要了解正在发生的事情,您需要详细了解class << self
语法。 class << self … end
创建一个元类。 在方法查找链中,在访问对象的基类之前先搜索对象的元类。 如果在元类中定义方法,则可以在类上调用该方法。 这类似于C++
的静态方法的概念。
是否可以访问元类? 是的:只需从class << self … end
内部返回self
。 请注意,在Ruby类声明中,您没有义务仅放置方法定义。 清单11显示了元类。
清单11.掌握元类
irb(main):198:0> class MyTest
irb(main):199:1> end
=> nil
irb(main):200:0> y = MyTest.new
=> #< MyTest:0x2d43fe0>
irb(main):201:0> z = class MyTest
irb(main):202:1> class << self
irb(main):203:2> self
irb(main):204:2> end
irb(main):205:1> end
=> #<Class: MyTest >
irb(main):206:0> z.class
=> Class
irb(main):207:0> y.class
=> MyTest
回到清单7中的代码,您看到palindrome
被定义为self == self.reverse
。 在这种情况下, self
与C++
没有什么不同。 C++
的方法以及Ruby中的方法都需要一个对象来操作,以修改或提取状态信息。 self
在这里指的是那个物体。 请注意,可以选择通过以下方式调用公共方法:在self
关键字前添加前缀,以指示该方法在其上作用的对象,如清单12所示 。
清单12.使用self调用方法
irb(main):094:0> class SelfTest3
irb(main):095:1> def foo
irb(main):096:2> self.bar()
irb(main):097:2> end
irb(main):098:1> def bar
irb(main):099:2> puts "Testing Self"
irb(main):100:2> end
irb(main):101:1> end
=> nil
irb(main):102:0> test = SelfTest3.new
=> #<SelfTest3:0x2d15750>
irb(main):103:0> test.foo
Testing Self
=> nil
您不能在Ruby中使用带self
关键字前缀的私有方法。 对于C++
开发人员而言,这可能会造成混乱。 清单13中的代码清楚地表明, self
不能与私有方法一起使用:对私有方法的调用只能由隐式对象进行。
清单13. self不能与私有方法调用一起使用
irb(main):110:0> class SelfTest4
irb(main):111:1> def method1
irb(main):112:2> self.method2
irb(main):113:2> end
irb(main):114:1> def method3
irb(main):115:2> method2
irb(main):116:2> end
irb(main):117:1> private
irb(main):118:1> def method2
irb(main):119:2> puts "Inside private method"
irb(main):120:2> end
irb(main):121:1> end
=> nil
irb(main):122:0> y = SelfTest4.new
=> #<SelfTest4:0x2c13d80>
irb(main):123:0> y.method1
NoMethodError: private method `method2' called for #<SelfTest4:0x2c13d80>
from (irb):112:in `method1'
irb(main):124:0> y.method3
Inside private method
=> nil
由于Ruby中的所有内容都是一个对象,因此在irb
提示符下调用self
时,会得到以下结果:
irb(main):104:0> self
=> main
irb(main):105:0> self.class
=> Object
启动irb
,Ruby解释器将为您创建主对象。 这个主要对象在Ruby相关文献中也被称为顶级上下文 。
足够的self
。 让我们继续进行动态方法和method_missing
方法。
method_missing背后的奥秘
考虑清单14中的Ruby代码。
清单14. method_missing的作用
irb(main):135:0> class Test
irb(main):136:1> def method_missing(method, *args)
irb(main):137:2> puts "Method: #{method} Args: (#{args.join(', ')})"
irb(main):138:2> end
irb(main):139:1> end
=> nil
irb(main):140:0> t = Test.new
=> #<Test:0x2c7b850>
irb(main):141:0> t.f(23)
Method: f Args: (23)
=> nil
显然,如果您需要伏都教,清单14应该让您露出微笑。 这里发生了什么? 您创建了一个Test
类型的对象,然后使用23
作为参数调用tf
。 但是Test
没有使用f
作为方法,因此您应该得到NoMethodError
或类似的错误消息。 Ruby在这里做了一些有趣的事情: method_missing
拦截并处理了您的方法调用。 method_missing
的第一个参数是缺少的方法名称,在这种情况下为f
。 第二个也是最后一个参数是*args
,它捕获传递给f
的参数。 您在哪里可以使用这样的东西? 在其他选项中,您可以轻松地将方法调用转发到包含的Module
或组件对象,而无需为顶级类中的每个调用显式提供包装应用程序编程接口。
再看一下清单15中的一些伏都教。
清单15.使用send方法将参数传递给例程
irb(main):142:0> class Test
irb(main):143:1> def method1(s, y)
irb(main):144:2> puts "S: #{s} Y: #{y}"
irb(main):145:2> end
irb(main):146:1> end
=> nil
irb(main):147:0>t = Test.new
irb(main):148:0> t.send(:method1, 23, 12)
S: 23 Y: 12
=> nil
在清单15中 , class Test
定义了一个称为method1
的方法。 但是,您可以直接调用send
方法,而不是直接调用该方法。 send
是Object
类的公共方法,因此可用于Test
(记住,所有类都派生自Object
)。 send
方法的第一个参数是表示方法名称的符号或字符串。 通常情况下, send
方法不能执行哪些操作? 您可以使用send
方法访问类的私有方法。 当然,这是否是一个好功能仍有待商.。 查看清单16中的代码。
清单16.访问类私有方法
irb(main):258:0> class SendTest
irb(main):259:1> private
irb(main):260:1> def hello
irb(main):261:2> puts "Saying Hello privately"
irb(main):262:2> end
irb(main):263:1> end
=> nil
irb(main):264:0> y = SendTest.new
=> #< SendTest:0x2cc52c0>
irb(main):265:0> y.hello
NoMethodError: private method `hello' called for #< SendTest:0x2cc52c0>
from (irb):265
irb(main):266:0> y.send(:hello)
Saying Hello privately
=> nil
投掷和捕捉不是他们看起来的样子
如果您像我一样具有C++
背景并且倾向于尝试编写异常安全代码,那么当您看到Ruby具有throw
和catch
关键字时,您可能会开始感到宾至如归。 不幸的是,在Ruby中, throw
and catch
具有完全不同的含义。
Ruby通常使用begin…rescue
块处理异常。 清单17提供了一个示例。
清单17. Ruby中的异常处理
begin
f = File.open("ruby.txt")
# .. continue file processing
rescue ex => Exception
# .. handle errors, if any
ensure
f.close unless f.nil?
# always execute the code in ensure block
end
在清单17中 ,如果在尝试打开文件时发生了不好的事情(可能是丢失的文件或文件权限有问题),则运行rescue
块中的代码。 在代码ensure
块始终运行,不论任何异常是否提高。 请注意,在rescue
块之后可以ensure
块是可选的。 另外,如果必须显式抛出异常,则语法为raise <MyException>
。 如果选择拥有自己的异常类,则可能希望从Ruby的内置Exception
类派生相同的类,以利用现有方法。
Ruby中的catch-throw工具并不是真正的异常处理:您可以使用throw
更改程序流。 清单18显示了一个使用throw
和catch
的示例。
清单18.投掷并捕获Ruby
irb(main):185:0> catch :label do
irb(main):186:1* puts "This will print"
irb(main):187:1> throw :label
irb(main):188:1> puts "This will not print"
irb(main):189:1> end
This will print
=> nil
在清单18中 ,当代码流到达throw
语句时,执行被中断,并且解释器开始寻找处理相应符号的catch
块。 从catch
块结束的地方重新开始执行。 看一下清单19中的throw
and catch
的示例:注意,您可以轻松地在函数之间散布catch
和throw
语句。
清单19. Ruby中的异常处理:嵌套catch块
irb(main):190:0> catch :label do
irb(main):191:1* catch :label1 do
irb(main):192:2* puts "This will print"
irb(main):193:2> throw :label
irb(main):194:2> puts "This won't print"
irb(main):195:2> end
irb(main):196:1> puts "Neither will this print"
irb(main):197:1> end
This will print
=> nil
有人说Ruby使C goto
疯狂到了它的catch
和throw
支持的新高度。 鉴于在每个级别上可能有多层嵌套功能以及可能有catch
块的情况, goto
疯狂类比在这里似乎确实有些蒸腾。
Ruby中的线程可以是绿色的
Ruby 1.8.7版不支持真正的并发。 真的,真的不是。 但您说,您在Ruby中拥有Thread构造。 你是对的。 但是,每次您调用Thread.new
它都不会产生真正的操作系统线程。 Ruby支持的是绿色线程 :Ruby解释器使用单个操作系统线程来处理来自多个应用程序级线程的工作负载。
当某些线程正在等待某些I / O发生时,此“绿色线程”概念很有用,并且您可以轻松地调度其他Ruby线程来充分利用CPU。 但是这种构造不能使用现代的多核CPU。 (维基百科提供了一个极好的一块解释了什么是绿色线程。参见相关信息中的链接。)
最后一个示例(请参见清单20 )证明了这一点。
清单20. Ruby中的多个线程
#!/usr/bin/env ruby
def func(id, count)
i = 0;
while (i < count)
puts "Thread #{i} Time: #{Time.now}"
sleep(1)
i = i + 1
end
end
puts "Started at #{Time.now}"
thread1 = Thread.new{func(1, 100)}
thread2 = Thread.new{func(2, 100)}
thread3 = Thread.new{func(3, 100)}
thread4 = Thread.new{func(4, 100)}
thread1.join
thread2.join
thread3.join
thread4.join
puts "Ending at #{Time.now}"
假设您在Linux®或UNIX®机器上拥有top
实用程序,请在终端中运行代码,获取进程ID,然后运行top –p <process id>
。 当top
开始时,按Shift-H列出正在运行的线程数。 您应该只看到一个线程来确认您无论如何都知道:Ruby 1.8.7中的并发性是一个神话。
综上所述,绿色线程还不错。 它们在重型I / O绑定程序中仍然有用,更不用说这种方法可能是跨操作系统最可移植的。
结论
本文涵盖了很多领域:
- Rub中的类层次结构概念
- nSingleton方法
- 解读
self
关键字和method_missing
方法 - 例外情况
- 穿线
尽管有怪癖,但Ruby很有趣,并且以最少的代码完成大量工作的能力非常强大。 那么,难怪Twitter之类的大型应用程序正在使用Ruby来发挥其真正的潜力。 Ruby中的编码愉快!
翻译自: https://www.ibm.com/developerworks/opensource/library/os-sixrubyfeatures/index.html
ruby