在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
【转载】原文http://rubyer.me/blog/917/#viewSource blocks, Procs, Methods, lambdas(也称闭包)是Ruby中最强大的一部分,用过你就会知道,同时也是最容易迷惑的。 首先:blocks代码块最常见、最简单、最富争议、最有Ruby风格的方式是blocks。写法如下: array = [1, 2, 3, 4] array.collect! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16] do…end构成一个block。然后把这个block通过collect!传给一个数组。就可以使用block中的n来迭代数组中每个元素。 class Array def iterate! self.each_with_index do |n, i| self[i] = yield(n) end end end array = [1, 2, 3, 4] array.iterate! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16] 首先,我们打开Array,并添加进iterate!方法。方法名以!结尾表示危险方法,引起注意。现在我们就可能像使用collect!一样使用iterate! 与属性不同,在方法中不需要指定block的名字,而是使用yield来调用。yield会执行block中的代码。同时,注意我们是怎么把 n(each_with_index当前处理的数字)传给yield的。传给yield的参数即对应了block中的参数(||中的部分)。现在n就能被 block调用并在yield调用中返回n**2。 以上仅仅是个开始,yield只是调用block的一种方式,还有一种叫Proc,看看。 class Array def iterate!(&code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array = [1, 2, 3, 4] array.iterate! do |n| n ** 2 end puts array.inspect # => [1, 4, 9, 16] 和上一段代码只有两个不同 def what_am_i(&block) block.class end puts what_am_i {} # => Proc block竟然是Proc!那Proc是什么? Procs 过程blocks很简单,但当我们需要处理很多blocks,多次使用一个blocks时,我们不得不重复代码。既然Ruby是完全面向对象的,我们就能把这些可复用的代码保存成object。这段可复用的代码就是Proc(procedure的简称) class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array_1 = [1, 2, 3, 4] array_2 = [2, 3, 4, 5] square = Proc.new do |n| n ** 2 end array_1.iterate!(square) array_2.iterate!(square) puts array_1.inspect puts array_2.inspect # => [1, 4, 9, 16] # => [4, 9, 16, 25] 注意:并没有在 iterate!的参数头部添加&,因为Proc只是一个普通类,不需要特殊处理。 上面的方式也是大多数语言处理闭包的方式。 def callbacks(procs) procs[:starting].call puts "Still going" procs[:finishing].call end callbacks(:starting => Proc.new { puts "Starting" }, :finishing => Proc.new { puts "Finishing" }) # => Starting # => Still going # => Finishing 所以,什么时候用blocks而不用Procs呢?我一般这样判断:
Lambda 拉姆达表达式上面的Procs与blocks用法很像其它语言中的匿名函数(即lambdas)。Ruby也支持lambdas. class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end array = [1, 2, 3, 4] array.iterate!(lambda { |n| n ** 2 }) puts array.inspect # => [1, 4, 9, 16] lambdas看起来很像Procs,但它们有2个细微的区别。 def args(code) one, two = 1, 2 code.call(one, two) end args(Proc.new{|a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}"}) args(lambda{|a, b, c| puts "Give me a #{a} and a #{b} and a #{c.class}"}) # => Give me a 1 and a 2 and a NilClass # *.rb:8: ArgumentError: wrong number of arguments (2 for 3) (ArgumentError) 可以看到,在Proc中,多余的参数被设为nil。但lambdas中,Ruby抛出了一个错误。 def proc_return Proc.new { return "Proc.new"}.call return "proc_return method finished" end def lambda_return lambda { return "lambda" }.call return "lambda_return method finished" end puts proc_return puts lambda_return proc_return中,执行到Proc.new中的return时,直接返回”Proc.new”,不继续执行。 为什么会有这样的不同? 所以,什么时候用lambda而不是Proc呢?可以参考下面代码: def generic_return(code) code.call return "generic_return method finished" end puts generic_return(Proc.new { return "Proc.new" }) puts generic_return(lambda { return "lambda" }) # => *.rb:6: unexpected return (LocalJumpError) # => generic_return method finished Ruby语法中一般参数(例子中为Proc)不能含有return。但使用了lambda后可以用return。 def generic_return(code) one, two = 1, 2 three, four = code.call(one, two) return "Give me a #{three} and a #{four}" end puts generic_return(lambda { |x, y| return x + 2, y + 2 }) puts generic_return(Proc.new { |x, y| return x + 2, y + 2 }) puts generic_return(Proc.new { |x, y| x + 2; y + 2 }) puts generic_return(Proc.new { |x, y| [x + 2, y + 2] }) # => Give me a 3 and a 4 # => *.rb:9: unexpected return (LocalJumpError) # => Give me a 4 and a # => Give me a 3 and a 4 使用lambda,代码很自然。但如果用Proc,我们需要对Arrays进行赋值。 Method 对象当你想把一个方法以闭包的形式传递给另一个方法,并且保持代码DRY。你可以使用Ruby的method方法。 class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end def square(n) n ** 2 end array = [1, 2, 3, 4] array.iterate!(method(:square)) puts array.inspect # => [1, 4, 9, 16] 例子中,我们先有了一个square方法。我们可以把它转换成一个Method对象并以参数形式传递给iterate!方法。但,这个新对象属于哪个类呢? def square(n) n ** 2 end puts method(:square).class # => Method 如你所料,square不是Proc,而是Method。Method与lambda用法相同,因为它们的概念是一样的。不同的是Method是有名字的method,而lambda是匿名method. 总结到此为止,我们已经了解了Ruby的4种闭包类型:blocks, Procs, lambdas 和 Methods。 译至:http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论