方法是一个有参数,有名字并且和对象关联的代码块,对象(更确切叫法是消息的接收者)调用方法,返回方法中最后一个表达式(ruby中基本上没有语句的概念,全是表达式)的值。很多语言都区分方法和函数,方法和对象相关。在ruby中,一切都是对象,即使你定义一个全局的函数,看上去不和任何对象关联,而实际上它被严格的定义为Object类的私有方法
irb(main):012:0> def hello irb(main):013:1> end => nil irb(main):014:0> Object.private_methods.grep :hello => [:hello]
为了方便,方法的名字以小写字母开头,当然也可以用大写字母开头,但是这样会和一般的常量混,当方法名超过一个单词时,可以用下划线将它们分开。ruby的方法名中可以有各式各样的符号,+,-,×等等,你可以为任何你想到的操作符定义成方法,唯一例外的是负号和正号,得叫'+@,-@'。
方法的参数:
加不加括号?加空隔不加括号,加括号不加空隔,强调是方法调用的时候加上括号,其它时候只要不出现语法问题,在明确的情况下,我想这是自由滴。
方法的参数在方法的名字之后,ruby的方法参数有很多种形式,ruby1.8与1.9又有一些微小的差异。ruby方法参数形式主要有:一般的,有默认值的,带*号的,hash,和带&的。一般的不用说了,首当其冲最好是放在参数列表的最前面。
当你定义一个方法的时候,你可以为一些参数指定默值。
def prefix(s, len=1) s[0,len] end prefix("Ruby", 3) # => "Rub" prefix("Ruby") # => "R"
#很明显参数不仅仅只是常量,可以是实例变量,也可以是有关前一参数的表达式
def suffix(s, index=s.size-1) s[index, s.size-index] end
在ruby1.8里面,有默认值的参数只能出现在普通参数的后面,1.9放宽了这个限制,但是有默认值的参数还是必须要连在一起的,这很好理解。
irb(main):001:0> def hello(a,b,c='world',d) irb(main):002:1> puts a,b,c,d irb(main):003:1> end => nil irb(main):004:0> hello 1,2,3 1 2 world 3 => nil irb(main):005:0> hello 1,2,3,4 1 2 3 4 => nil #不允许的情况 irb(main):006:0> def hello(a,b,c='world',d,e='unpass') irb(main):007:1> puts a,b,c,d,e irb(main):008:1> end SyntaxError: (irb):6: syntax error, unexpected '=', expecting ')' def hello(a,b,c='world',d,e='unpass') ^ (irb):6: syntax error, unexpected ')', expecting $end from /usr/local/bin/irb:12:in `<main>'
带*号的参数
有时候我们可能想要写一个可以传递任意个参数的方法,可以使用这种参数
def max(first, *rest) max = first rest.each {|x| max = x if x > max } max end max(1) # first=1, rest=[] max(1,2) # first=1, rest=[2] max(1,2,3) # first=1, rest=[2,3]
在一个方法的参数里,不管是形参(parameter)还是实参(argument)这种参数只能有一个,在ruby1.8里,这种参数必须出现在所有的一般参数和有默认值的参数之后,但是在1.9里面,它的后面可能出现一般参数。
irb(main):001:0> def max(first,*rest,addtion) irb(main):002:1> max=first irb(main):003:1> puts addtion irb(main):004:1> rest.each{|x|max=x if x>max} irb(main):005:1> max irb(main):006:1> end => nil irb(main):007:0> max(1,2,3) 3 => 2 irb(main):008:0> max(1,2,3,4) 4 => 3 irb(main):009:0> max(1,2,3,4,5) 5 => 4
#后面的一般参数分别是倒数的第一个,第二个,以此类推。
上面看到的是带*的参数为形参时*的作用,当参数是实参时,这种作用是逆向的,有点像并行赋值时*号的作用(但是他们是有区别的)
data = [3, 2, 1] m = max(*data) # first = 3, rest=[2,1] => 3 #Consider what happens without the *: m = max(data) # first = [3,2,1], rest=[] => [3,2,1] #In Ruby 1.9, enumerators are splattable objects. To find the largest #letter in a string, for example, we could write: max(*"hello world".each_char) # => 'w'
hash形式的参数的优点是不用记住参数的顺序,当然你可以传递一个对象,相杂整就杂整
def sequence(args) n = args[:n] || 0 m = args[:m] || 1 c = args[:c] || 0 a = [] # Start with an empty array n.times {|i| a << m*i+c } # Calculate the value of each array element a # Return the array end #You might invoke this method with a hash literal argument like this: sequence({:n=>3, :m=>5}) # => [0, 5, 10]
#用裸hash sequence(:m=>3, :n=>5) # => [0, 3, 6, 9, 12] # Ruby 1.9 的新语法,json形式,ruby语法越来越像命令了~ sequence c:1, m:3, n:5 # => [1, 4, 7, 10, 13] #在省去参数的小括号,hash的大括号也要省去,要不然程序会把它看成一个#block sequence {:m=>3, :n=>5} # Syntax error!
参数列表中带有&:
这种参数该是放在参数列表最后面的一种参数,&的作用有点像*,在形参里把一个block=>proc,方法调用时,逆向功能。
值得一提的是在方法内部,使用 yeild调用代码块是不需要显示的在参数列表中显示的
irb(main):020:0> def test irb(main):021:1> yield irb(main):022:1> end => nil irb(main):023:0> test {puts "hello"} hello => nil
在你显示的用&block声明后,你还是可以通过yield来调用代码块
irb(main):029:0> def test(&block) irb(main):030:1> block.call() irb(main):031:1> yield irb(main):032:1> end => nil irb(main):033:0> test{puts 'hello'} hello hello => nil
调用一个方法时,&的作用
a, b = [1,2,3], [4,5] # Start with some data. summation = Proc.new {|total,x| total+x } # A Proc object for summations. sum = a.inject(0, &summation) # => 6 sum = b.inject(sum, &summation) # => 15
#这样就把方法的实现抽象出来了~
在ruby1.9里面,Symbol类定义了一个to_proc方法,所以我们也可以用以下的这种形式来调用代码
words = ['and', 'but', 'car'] # An array of words uppercase = words.map &:upcase # Convert to uppercase with String.upcase upper = words.map {|w| w.upcase } # This is the equivalent code with a block
Symbol#to_pro
def to_proc Proc.new { |*args| args.shift.__send__(self, *args) } end
小结:总的来说,对于形参,参数的顺序是:一般的=》缺省的=》hash=》带*号的=》&block 不过不太会出现一起用到这些参数的情况。对于方法的近亲,block,lambda,proc的参数赋值和方法参数赋值是不太一样的,留到下次吧。