ruby_满足六种误解的Ruby功能

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++ intdouble清单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

ModuleClass的基类,它带有一个警告,即您不能直接实例化用户定义的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时, cCat类型的对象,该对象派生自ObjectCat类是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方法专门针对对象yyz.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 。 在这种情况下, selfC++没有什么不同。 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方法,而不是直接调用该方法。 sendObject类的公共方法,因此可用于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具有throwcatch关键字时,您可能会开始感到宾至如归。 不幸的是,在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显示了一个使用throwcatch的示例。

清单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的示例:注意,您可以轻松地在函数之间散布catchthrow语句。

清单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疯狂到了它的catchthrow支持的新高度。 鉴于在每个级别上可能有多层嵌套功能以及可能有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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值