方法声明
采用如下语法:
def output_something(value)
puts value
end
也可以省略参数列表周围的括号:
def output_something value
puts value
end
方法体中的最后一个表达式的结果会作为函数的返回值。因此,return关键字并不是必须得。不过,return关键字仍有用处,如表示满足某些条件下,提前从方法体中退出。需要注意的是,在Block内使用return,会直接从方法中退出,而不是从Block中退出,如果需要从Block中退出,需要使用break,例子:
six = (1..10).each {|i| break i if i > 5} # six值为6
方法调用
采用如下语法:
method_name(parameter1, parameter2)
同样,括号可以省略:
method_name parameter1, parameter2
默认参数
声明方法时,可以指定参数的默认值:
def foo( j, i = 7)
return i + j
end
变长参数列表
声明方法时,在参数前添加星号(*),表示该方法接受变成的参数。变长参数会以数组的形式存储,如:
def calculate_value(x,y,*otherValues)
puts otherValues
end
calculate_value(1,2,'a','b','c') # otherValues的值为['a', 'b', 'c']
在调用方法时,可以用数组作为参数传入方法,并在数组前添加星号(*)。这样,数组会被打开,并将元素逐个赋值给方法参数,如:
arr = [1, 2, 'a', 'b', 'c']
calculate_value(*arr) # x的值为1,y的值为2,otherValues的值为['a', 'b', 'c']
调用方法时,还可以传递哈希,这样,我们同时拥有了命名参数及可变长的参数列表,如:
def accepts_hash( var )
print "got: ", var.inspect
end
accepts_hash :arg1 => 'giving arg1', :argN => 'giving argN'
# 输出 {:argN=>"giving argN", :arg1=>"giving arg1"}
关键字参数
从Ruby 2.0开始,方法声明/调用支持关键字参数,如:
def test_method(a, b, c:true, d:false)
puts a,b,c,d
end
调用的时候,我们可以:
test_method(1, 2)
test_method(1, 2, c:someValue, d:someOtherValue)
test_method(1, 2, d:someValue, c:someOtherValue)
关键字参数的应用场景就是,如果有很多可选的参数,那么应用关键字参数调用方法,可读性更高。
理解Ruby中的Block,Proc和方法
Ruby借鉴了函数式编程中的闭包(Closure),高阶函数(High-Order Function)及头等函数(First-Class Function)。在Ruby中,这几个概念分别称作:Block,Proc及方法(Method)。这3个概念相近但又有区别,容易混淆。
Proc
Proc就是绑定了一组局部变量的代码块。一旦绑定完成,代码块可以在其他上下文中调用,而且仍能访问到那些绑定的局部变量。Proc充当Ruby中函数(Function)的角色,或者更准确的叫法-函数对象,社区中又称为functor。
Wikipedia中的闭包(Closure)被定义为能够访问自己语义上下文中的变量的函数。因此,Proc也可视作Ruby中的闭包。
Ruby中创建Proc有2种方法:
- 通过Proc.new方法
- 通过Kernel方法 - lambda
这2个方法在创建Proc的时候,有如下2个区别:
- 参数校验:Proc.new不会进行参数校验;lambda方式会对参数个数进行校验。
- return行为:对Proc.new创建的Proc 代码块内添加return语句,会直接作为包含的函数本身的return行为;lambda创建的Proc代码块中的return语句更符合直观的理解,返回给调用者。
针对区别1,举例子:
pnew = Proc.new {|x, y| puts x + y}
lamb = lambda {|x, y| puts x + y}
# 输出 6
pnew.call(2, 4, 11)
# 抛异常 ArgumentError
lamb.call(2, 4, 11)
针对区别2,举例子:
def try_ret_procnew
ret = Proc.new { return "Baaam" }
ret.call
"This is not reached"
end
# 打印 "Baaam"
puts try_ret_procnew
def try_ret_lambda
ret = lambda { return "Baaam" }
ret.call
"This is printed"
end
# prints "This is printed"
puts try_ret_lambda
针对Proc的调用,除了使用call方法外,还可以使用[]方法,如:
say = lambda {|something| puts something}
say.call("Hello")
# 如下调用会与call方法效果一样
say["Hello"]
方法(Method)
方法也是代码块,但是不会与同语义下的变量绑定。想法,方法会绑定到对象实例上,并可访问对象实例的属性。
class Boogy
def initialize
@id = 15
end
def arbo
puts "The id is #{@id}"
end
end
# 初始化Boogy实例
b = Boogy.new
b.arbo # 输出 "The id is 15
可以将方法调用理解为向对象发送消息。实际上,Ruby中的Object对象提供了send方法,以下3种调用arbo方法的方式,效果是一样的。
# 直接针对对象实例调用方法
b.arbo
# 通过Object#send方法向对象发送消息
b.send("arbo")
# 方法名既可以用字符串传递,也可以用Symbol传递
b.send(:arbo)
实际上,Ruby中我们可以定义top-level层级的方法,这些方法看似没有被放到任何用户定义的类里,其实Ruby会将它们放入Object类中。不过,日常使用中,我们仍然可以将这种top-level的方法理解为独立的方法,在其他语言(C或者Perl)中,这类方法称作函数。
def say (something)
puts something
end
say "Hello"
Object.send(:say, "Hello") # 该行与上一行效果一样
Block
也是代码块,和Proc很类似。区别在于,Block不能独立存在,必须在绑定并转换为Proc后,才可以独立存在,并被执行。形象的比喻:Block像虫卵,Proc像昆虫。
将Block作为参数传递
当将Block作为最后一个参数传递给Ruby中的方法的时候,Ruby会将Block转换为匿名的Proc并传递给方法。方法内部,可以通过yield关键字执行Proc。
def do_twice
yield
yield
end
do_twice {puts "Hola"}
也可以显示的将Proc作为参数传递给方法,如:
def do_twice(what)
what.call
what.call
end
do_twice lambda {puts "Hola"}
上述2种将Proc作为参数传递的方法皆可。
&符号的使用
在方法调用时,将Block追加在方法末尾,在方法体内部是没有办法索引到这个转换后的Proc的。不过,可以在参数列表的最后一个参数前加&符号,这样,末尾追加的Block在被转换成Proc后,方法体内部可以通过这个&参数访问,如:
def contrived(a, &f)
# 可以通过f索引到Block
f.call(a)
# 同样可以通过yield传递参数给Block
yield(a)
end
# this works
contrived(25) {|x| puts x}
# 下面将Proc作为参数传递,会导致抛出ArgumentError
# 因为&f并不算是一个参数,只是为了转换Block
contrived(25, lambda {|x| puts x})
Content mainly from 「Ruby Programming」