在阅读Ruby代码时,经常会看到类似下面这样的代码:
1 2 3 4 5 def some_method (*a, &b ) ... end call_some_method(:name=>"test" , :type=>"some" ) a.do_something if a.respond_to?(:do_something )
这些变量前面的”*”, “&”, “:”是什么意思?我这两天花了点时间学习,归纳为以下代码:
代码一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 $ irb1.9.2p290 :001 > def t (a={}, *b, &c )1.9.2p290 :002?> puts a.inspect if a1.9.2p290 :003?> puts b.inspect if b1.9.2p290 :004?> puts c.inspect if c1.9.2p290 :005?> c.call(a) if c1.9.2p290 :006?> end => nil 1.9.2p290 :007 > t(5 , :name=>"test" , :type=>"method" )5 [{:name=>"test" , :type=>"method" }] => nil 1.9.2p290 :008 > t(5 )5 [] => nil 1.9.2p290 :009 > t(:name=>"test" , :type=>"method" ){:name=>"test" , :type=>"method" } [] => nil 1.9.2p290 :010 > t(:name=>"test" , :type=>"method" ){|hash | hash.each{|k,v | puts "#{k} =#{v} " } if hash && hash.respond_to?(:each )}{:name=>"test" , :type=>"method" } [] #<Proc:0x9e30518@(irb):10> name=test type=method => {:name=>"test" , :type=>"method" } 1.9.2p290 :011 > t(5 , :name=>"test" , :type=>"method" ){|hash | hash.each{|k,v | puts "#{k} =#{v} " } if hash && hash.respond_to?(:each )}5 [{:name=>"test" , :type=>"method" }] #<Proc:0x9e14c8c@(irb):11> => nil 1.9.2p290 :012 > t(5 ){|hash | hash.each{|k,v | puts "#{k} =#{v} " } if hash && hash.respond_to?(:each )}5 [] #<Proc:0x9df321c@(irb):12> => nil 1.9.2p290 :013 > t(:name=>"test" , :type=>"method" , 5 ){|hash | hash.each{|k,v | puts "#{k} =#{v} " } if hash && hash.respond_to?(:each )}SyntaxError : (irb): 13 : syntax error, unexpected ')' , expecting tASSOC...me=>"test" , :type=>"method" , 5 ){|hash | hash.each{|k,v | puts ... ... ^ (irb): 13 : syntax error, unexpected '}' , expecting $end from /home/user/.rvm/rubies/ruby-1.9 .2 -p290/bin/irb: 16 :in `<main>' 1.9.2p290 :014 >
代码二
1 2 3 4 5 6 7 8 9 10 11 12 $ irb1.9.2p290 :001 > def x (*a, b, &c )1.9.2p290 :002?> c.call(a,b) if c1.9.2p290 :003?> end => nil 1.9.2p290 :004 > x(5 , 6 , :name=>"t" , :type=>"x" ) {|a,b | puts a.inspect + "," + b.inspect}[5 , 6 ],{:name=>"t" , :type=>"x" } => nil 1.9.2p290 :005 > x(5 , 6 ) {|a,b | puts a.inspect + "," + b.inspect}[5 ],6 => nil 1.9.2p290 :006 >
从以上代码可以了解到以下概念:
Argument Prefix “*”
以”*“开头的方法参数,表示将调用方所有传递给该方法的参数(注:其实是排除了其它显式被要求的参数的所有参数,在以上示例代码中可以看出这一点)传递到这个以”*abc”命名的Array对象中。
–翻译自 Programming Ruby 2nd Edition “Blocks for Transactions”一节。
Argument Prefix “&”
如果一个Ruby方法的最后一个参数以”&”开头,Ruby会在该方法被调用时试图找到一个block。如果找到,该block会被转换成一个Proc类的对象。… 更有意思的是,你得到的这个Proc对象,不仅包含block中定义的代码,还包含定义该block处的所有上下文信息,比如self引用、方法、变量以及常量。Ruby的这一神奇之处在于:在该block被实际调用的时候,即使定义该block的原始范围信息看上去已经消失了,在该block的范围之内,那些原始的上下文信息还仍然可用。在其它语言中,这种功能叫做闭包(closure )。
–翻译自 Programming Ruby 2nd Edition “Blocks Can Be Closures”一节。
Symbol “:” Symbol是Ruby语言内置的一个class。我的理解是,它主要被用来作为Ruby编程中的“命名对象”(naming object)。Symbol对象被用于表示Ruby的符号表。运行以下代码会让你感到有点惊讶。
1 Symbol .all_symbols.each {|s | puts s}
那么 Symbol 对我们有什么用呢?当然也是内存。使用 String 的开销太大了,因为每一个String 都是一个对象。想想前边的例子,一个字符串每出现一次 Ruby 就会创建一个 String 对象。
通常来讲,当你面临 String 还是 Symbol 的选择时,可以参考以下标准:
如果使用字符串的内容,这个内容可能会变化,使用String;
如果使用固定的名字或者说是标识符,使用 Symbol。
那么什么时候我们会用到名字呢?很多时候都会,比如枚举值、hash keys、方法的参数)等等。
哈希表是Symbol应用最为广泛的地方。ROR 中就大量地运用这种方式,也许你已经看到了,到处都是Symbol和hash。使用hash参数的方法可以如下定义,前半部分为固定参数,后面为可变参数,或者干脆全采用哈希参数:
1 2 3 4 5 6 7 def my_method (para1, para2, options={} ) end def my_method (options={} ) end
如果你希望设定一些默认参数,并允许调用者更改这些参数,可以使用哈希对象的 merge! 方法。比如”hsh.merge!(other_hash)”。该方法将other_hash里内容加到hsh中,如果other_hash与hsh有重复的key,则key在other_hash中的value覆盖hsh中对应key的value。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Test def my_method (opts={} ) default_opts={:arg1 => 10 , :arg2 => "abc" } default_opts.merge!(opts) default_opts.each{|key,value | puts "#{key} is #{value} " } end end t = Test .new t.my_method :arg1=> 5 , :arg3=>"def" arg1 is 5 arg2 is abc arg3 is def
–摘自这里 。
我怀疑Ruby的这种”argument prefix”的变量定义风格来自于Perl,第一眼看上去感到很怪异,但不得不承认,这种风格使用起来很方便且功能很强大。
除了argument prefix,让我感到惊奇的是Ruby以下的访问控制实现:
在Ruby中直接定义的方法(不被包含在任何class/module中),看起来不属于任何类,但实际上Ruby将其分给Object类,也就是所有其它类的父类。因此,所有对象现在都可以使用这一方法。这本应是正确的,但有个小陷阱:它是所有类的私有(private)方法.我们将在下面讨论这是什么意思,但一个结果是它只能以函数的风格调用,像这样:
1 2 3 4 5 6 7 8 ruby> class Foo def fourth_power_of (x ) square(x) * square(x) end end nil ruby> Foo .new.fourth_power_of 10 10000
我们不允许向一个对象明确地运用这一方法:
1 2 ruby> "fish" .square(5 ) ERR : (eval): 1 : private method `square' called for "fish":String
这一聪明的做法使得Ruby可以像在传统语言中那样运用函数的同时,保持了Ruby 的纯OO性质(函数仍是对象方法,但接受者隐式的为self)。
以上引用文字来自这里 。
参考了镐头书以及这里 和这里 ,还有专门介绍Ruby的symbol的两篇文章[1] [2] 。
Added@20120614:
今天在我看到”通天塔导游:各种编程语言优缺点 “这篇文章时,才发现在Perl语言中,变量名称前面的”$”这种符号有一个专门的术语:sigil 。所以很自然地,本文讨论的内容就是”ruby sigils”。