Ruby Blocks & Closures

Topics
Blocks
What is a block?
How does a block look like?
How does a block get passed and executed?
Proc object
& (Ampersand) operator
lambda
Where do blocks get used?
Closure
What is a closure?


What is a block?
什么是Block
Blocks are basically nameless functions.
Blocks是基本的匿名函数。
You can think of it as a nameless chunk of code.
你可以认为它是一个匿名代码块。
You can pass a nameless function to another function (I will it target function in this presentation), and then that target function can invoke the passed-in nameless function
你可以用匿名函数和另一个函数相联系,然后目标函数就能调用这个匿名函数。
For example, a target function could perform iteration by passing one item at a time to the nameless function.
例如,目标函数可以执行迭代通过匿名函数。

How to Represent a Block?
怎样书写Block
A block can be represented in two different formats - these two formats are functionally equivalent
一个block有两种描述方式 - 这两种方式作用一样
  1. puts "----First format of code block containing code fragment between { and }"
  2. [1, 2, 3].each { |n| puts "Number #{n}" }
  3. puts "----Second format of code block containing code fragment between do and end"
  4. [1, 2, 3].each do |n|
  5.   puts "Number #{n}"
  6. end
How a block is passed & executed
block怎样编译,执行
When a method is invoked, a block can be passed (attached)
当一个方法被调用时,block被编译
The yield() method in the invoked method (target function) executes the passed block
  1. puts "----Define MyClass which invokes yield"
  2. class MyClass
  3.   def command()
  4.     # yield will execute the attached block to the method
  5.     yield() 
  6.   end        
  7. end
  8. puts "----Create object instance of MyClass"
  9. m = MyClass.new
  10. puts "----Call command method of the MyClass passing a block"
  11. m.command {puts "Hello World!"}
How a block receive arguments
block如何接收参数
A block can receive arguments - they are represented as comma-separated list at the beginning of the block, enclosed in pipe characters:
block能接收参数 - 参数用逗号隔开,并加上特殊字符:
  1. puts "----Define MyClass which invokes yield"
  2. class MyClass
  3.   def command1()
  4.     # yield will execute the supplied block
  5.     yield(Time.now) 
  6.   end        
  7. end
  8. puts "----Create an object instance of MyClass"
  9. m = MyClass.new
  10. puts "----Call command1 method of the MyClass"
  11. m.command1() {|x| puts "Current time is #{x}"}
A block can receive multiple arguments:
block能够接收复杂参数:
  1. puts "---Define a method called testyield"
  2. def testyield
  3.   yield(1000, "Sang Shin")
  4.   yield("Current time is"Time.now)
  5. end
  6. puts "----Call testyield method"
  7. testyield { |arg1, arg2| puts "#{arg1} #{arg2}" }


Proc Objects
What is a Proc object?
Proc objects (Procs) are blocks of code that have been converted to “callable” objects.
Proc对象是可调用的block代码块。
Proc objects are considered as first-class objects in Ruby language because they can be:
Proc对象被认识是Ruby中的第一类对象 :
Created during runtime
运行期创建
Stored in data structures (saved in a variable)
在数据结构里储存
Passed as arguments to other functions
能传递参数到其他函数
Returned as the value of other functions.
能给其他函数返回值。

How To Create a Proc Object?
怎么创建一个Proc对象?
Use new keyword of Proc class passing a block
  1. puts "----Create a Proc object and call it"
  2. say_hi = Proc.new { puts "Hello Sydney" }
  3. say_hi.call
  4. puts "----Create another Proc object and call it"
  5. Proc.new { puts "Hello Boston"}.call
How to execute a block of a Proc Object?
如何运行一个Proc对象?
Use call method of the Proc object
调用call方法

How to pass a Proc object as an argument?
如何传递一个Proc对象作为一个参数?
Pass it just like any other Ruby object - hence the reason why Proc is a first-class object
可以像传递其它Ruby对象一样传递它,因为Proc是第一类对象
  1. puts "----Create a Proc object"
  2. my_proc = Proc.new {|x| puts x}
  3. puts "----Define a method that receives Proc object as an argument"
  4. def foo (proc_param, b)
  5.   proc_param.call(b)
  6. end
  7.  
  8. puts "----Call a method that passes a Proc object as an arugment"
  9. foo(my_proc, 'Sang Shin')
How to pass Arguments to the block(represented by the Proc object)?
如何传递参数给一个block
Pass arguments in a call method of the proc object
用call方法传递
  1. puts "----Create a Proc object"
  2. my_proc = Proc.new {|x| puts x}
  3. puts "----Define a method that receives Proc object as an argument"
  4. def foo (proc_param, b)
  5.   proc_param.call(b)
  6. end
  7.  
  8. puts "----Call a method that passes a Proc object as an arugment"
  9. foo(my_proc, 'Sang Shin')
How to Use a Proc object as a Return Value?
怎样用一个Proc对象当作返回值?
Just like any other Ruby object
  1. puts "----Define a method that returns Proc object as a return value"
  2. def gen_times(factor)     
  3.     Proc.new {|n| n*factor }   
  4. end
  5. puts "----Assign Proc object to local variables"
  6. times3 = gen_times(3)   
  7. puts "----Execute the code block passing a parameter"
  8. puts times3.call(3)           

Proc Object works as a Closure
Proc objects (Procs) are blocks of code that have been bound to a set of local variables.  Once bound, the code may be called in different contexts and still access those variables.
Proc对象是有一组本地变量约束的代码块。
  1. def gen_times(factor)
  2.    return Proc.new {|n| n*factor }  # factor is local variable in a block
  3.  end
  4.  times3 = gen_times(3)               # Block has factor variable set to 3
  5.  times5 = gen_times(5)               # Block has factor variable set to 5
  6.  times3.call(12)                           #=> 36 because 12 * 3 (factor) = 36
  7.  times5.call(5)                             #=> 25 because 5 * 5 (factor) = 25
  8.  times3.call(times5.call(4))          #=> 60 because (4 * 5  )  * 3 (factor) = 60
lambda and Proc
lambda is equivalent to Proc.new - the following three statements are considered equivalent
lambda和Proc.new等价
  1. say_hi = Proc.new { puts "Hello Sydney" }
  2. say_hi = lambda { puts "Hello Sydney" }
  3. say_hi = proc { puts "Hello Sydney" }
There are a couple of differences, however
Difference #1 between lambda and Proc
第一点不同
Argument checking: A Proc object created from using lambda checks the number of arguments passed and if they do not match, throws ArgumentError exception
参数检查:lambda会进行参数检查,不匹配就抛出ArgumentError异常
  1. puts "----Create Proc object using lambda"
  2. lamb = lambda {|x, y| puts x + y}
  3. puts "----Create Proc object using Proc.new"
  4. pnew = Proc.new {|x, y| puts x + y}
  5. puts "----Send 3 arguments, should work"
  6. pnew.call(2, 4, 11)
  7. puts "----Send 3 arguments, throws an ArgumentError"
  8. lamb.call(2, 4, 11)
lambda and Proc Difference 2
第二点不同
How return is handled from the Proc
如何处理返回值
A return from Proc.new returns from the enclosing method (acting just like a return from a block).
Proc.new的返回值来自封闭的方,返回后跳出方法
A return from lambda acts more conventionally, returning to its caller.
lambda的返回值返回给调用者
  1. def foo
  2.   f = Proc.new { return "return from foo from inside proc" }
  3.   f.call # control leaves foo here
  4.   return "return from foo" 
  5. end
  6. def bar
  7.   f = lambda { return "return from lambda" }
  8.   f.call # control does not leave bar here
  9.   return "return from bar" 
  10. end
  11. puts foo # prints "return from foo from inside proc" 
  12. puts bar # prints "return from bar" 
& (Ampersand) Operator
How & (Ampersand) is used?
&符号如何使用?
The ampersand operator can be used to explicitly convert between blocks and Procs
&操作符用于blocks和Procs的转换
Conversion from a block to a Proc
block到Procs的转换
If an ampersand (&) is prepended to the last argument in the argument list of a method, the block attached to this method is converted to a Proc object and gets assigned to that last argument.

Conversion from a Proc to a block
Another use of the ampersand is the other-way conversion - converting a Proc into a block. This is very useful because many of Ruby’s great built-ins, and especially the iterators, expect to receive a block as an argument, and sometimes it’s much more convenient to pass them a Proc.
The method receives a block as a Proc object
  1. puts "----The attached block is passed as the last argument in the form of Proc object"
  2. def my_method_ampersand(a, &f)
  3.   # the block can be accessed through f
  4.   f.call(a)
  5.     
  6.   # but yield also works !
  7.   yield(a)
  8. end
  9.  
  10. puts "----Call a method with a block"
  11. my_method_ampersand("Korea") {|x| puts x}
Pass a Proc with & preceded
  1. puts "----Create a Proc object"
  2. say_hi = Proc.new { |x| puts "#{x} Hello Korea" }
  3. puts "----Define a method which expects a block NOT Proc object"
  4. def do_it_with_block
  5.   if block_given?
  6.     yield(1)
  7.   end
  8. end
  9. puts "----Call do_it_with_block method which expects a block, convert Proc object to a block"
  10. do_it_with_block(&say_hi)
Where Do Blocks Get Used?
Blocks Usage Examples
Iteration
[1, 2, 3].each {|item| puts item}
Resource management
file_contents = open(file_name) { |f| f.read }
Callbacks
widget.on_button_press do
puts “Button is pressed”
end


What is a Ruby Closure?
In Ruby, a Proc object behaves as a Closure
A Proc object maintains all the context in which the block was defined: the value of self, and the methods, variables, and constants in scope. This context is called scope information
A block of the Proc object can still use all original scope information such as the variables even if the environment in which it was defined would otherwise have disappeared. 
Ruby Closure Example
  1. # Define a method that returns a Proc object
  2. def ntimes(a_thing)
  3.   return proc { |n| a_thing * n }
  4. end
  5. # The a_thing is set to value 23 in a block.  
  6. p1 = ntimes(23)
  7. # Note that ntimes() method has returned.  The block still
  8. # has access to a_thing variable.
  9. # Now execute the block.  Note that the a_thing is still set to
  10. # 23 and the code in the block can access it, so the results is set 69 and 92
  11. puts p1.call(3)     #   69
  12. puts p1.call(4)     #   92

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值