Each循环
For循环在Ruby中不常见,因为当我们需要循环时,我们会根据惯用法这样写:
1.upto(5).each { |e|
puts e
}
以上代码等价于:
print = lambda { |e| puts e }
1.upto(5).each(&print)
1.upto(5) 的返回值为 [1,2,3,4,5], 是一个数组实例(Array Instance)
each 是数组的实例方法(Instance Method),我们可以像调用其它普通方法(Method)一样来调用each
我们定义了 print 这个 Lambda,并把它用为调用 each 时的最后一个特殊“块(Block)”参数
我们使用 & (Ampersand)将print的类型从Lambda转为块(Block)
块(Block)
在Ruby中每一次调用方法都可以传入一个块参数。
块参数可以没有但最多只能有1个。
传入块参数的方法有两种:
- 直接以 do..end 或 {...} 的方式直接写在方法后面
- 写为最后一个参数并且类型为Block
在第二种方法里,使用 & 将Lambda转换为块是关键
1.upto(5).each(print) 是错误的写法,Ruby解释器会以为你传给了each一个“真正”的参数,而each方法不接受“真正”的参数。
如果忘了写 &,那么你会得到
"Error: #<ArgumentError: wrong number of arguments (1 for 0)>"
Proc 和 Lambda 的定义与调用
Proc 和 Lambda 非常类似, 并且在Ruby中都为Proc类型(Lambda不是一个类名)。你可以把它们理解成可以赋值给变量的“函数”。
定义Proc需使用new关键词后边接一个block
is_one = Proc.new { |x| x == 1 }
当然你也可以写成 Proc.new(&another_proc) ,但是这样写只是绕远路并没有好处
定义Lambda有两种写法:
is_two = lambda { |x| x == 2 }
is_three = -> (x) { x == 3 } # Rails中常见
你可以把一个普通的方法转换为Lambda。我们首先需要获得方法对象然后使用to_proc。
def method_is_four(n)
n == 4
end
is_four = self.method(:method_is_four).to_proc
在Ruby中调用Proc和Lambda的方法有三种:
- call
- []
- .()
is_one.call(1) # true
is_two[1] # false
is_three.(1) # false
使用case时,接在when后面的Proc或Lambda会被自动调用
case 2
when is_one
puts "one"
when is_two
puts "two"
else
puts "unknown"
end
# 运行结果:two
Proc 与 Lambda 的区别
Proc 与 Lambda 最大的区别有两个
- 调用Lambda时传入的参数数量需与定义时一致。调用Proc时缺少的参数将被默认为nil
- 在Lambda中return退出Lambda本身。在Proc中return退出包裹Proc的外层方法
is_one.call() # proc: false
is_two.call() # lambda: Error: #<ArgumentError: wrong number of arguments (0 for 1)>
def method1
lambda {
return 'lambda'
}.call
return 'method'
end
# 运行结果:method
def method2
Proc.new {
return 'proc'
}.call
return 'method' #这句不会被执行因为上面的proc抢先return了
end
# 运行结果:proc
Proc 与 Lambda 的好处
使用Proc 与 Lambda 的好处在于可以利用它们被定义时的作用域,也就是闭包。闭包这个主题会在以后的文章中与大家分享。