块的定义、调用与运行
在Ruby中,定义一个代码块的方式有2种 ,一是使用do … end, 另外一种是用大括号“{}”把代码内容括起来。代码块定义时也是可以接受参数的。但是,只有在调用一个方法的时候才可以定义一个块。
块定义好之后,会直接传递给调用的方法,在该方法中,使用“yield”关键字即可回调这个块。
def block_method(a, b)
a + yield(a, b)
end
puts block_method(1, 2) { |a, b| a*2+b } # => 5
result = block_method(1, 2) do |a, b|
a+b*2
end
puts result # => 6
如果一个方法定义的时候使用了yield关键字,但是调用的时候却没有传递代码块,方法会抛出“no block given (yield) (LocalJumpError)”异常。可以使用Kernal#block_given?方法检测当前的方法调用是否包含代码块。
def check_block
return yield if block_given?
'no block'
end
puts check_block{ 'This is a block'} # => This is block
puts check_block # => no block
代码在运行的时候,除了需要代码外,还需要运行环境,即一组各种变量的绑定。代码块就是由代码和一组绑定组成的,代码块可以获得自己定义的局部变量的绑定,和上下文可见的实例变量,在代码块执行的时候,只会根据自己定义时可见的绑定来执行。业界把块这样的特性称之为闭包(Closure)。
def closure_method
x = "Goodbye"
yield("@xianlinbox")
end
x = "Hello"
puts closure_method { |y| "#{x} World, #{y}!" } # => Hello World, @xianlinbox!
作用域
前面提到代码运行时,需要一组绑定, 这组绑定在代码的运行过程中,还会发生变化,这种变化发生的根本原因就是作用域发生改变,每个变量绑定都有自己的作用域,一但代码切换作用域,旧的绑定就会被一批新的绑定取代。在Ruby的世界中,作用域相对简单,作用域之间是截然分开的,一旦进入另外一个作用域,原先的绑定就会替换为一组新的绑定,可以通过Kernal#local_variables方法查看当前作用域下的绑定。那么,程序什么时候会从一个作用域跳转到另一个作用域呢?
Ruby程序只会在3个地方关闭前一个作用域,同时打开一个新的作用域:
- 类定义, class … end
- 模块定义, module … end
- 方法定义, def … end
这三个地方通常称之为作用域们(Scope Gate)。
v1 = 1
class MyClass
v2 = 2
puts local_variables.to_s + "call 1" # => [:v2]call 1
def my_method
v3 = 3
puts local_variables.to_s + " call 2"
end
puts local_variables.to_s + "call 3" # => [:v2]call 3
end
obj = MyClass.new
obj.my_method # =>[:v3] call 2
puts local_variables.to_s + "call 4" # =>[:v1, :obj]call 4
因为作用域之间的隔离,让人忍不住会想,怎样才能让一个绑定穿越多个作用域?在Ruby中有一个叫做扁平化作用域(Flat Scope)的概念,通俗点说就是变身拆迁队长,拆掉所有的作用域门。
- 用Class.new()方法代替class关键字来定义类
- 使用Module。new()方法代替module关键字定义模块
- 使用Module#define_method代替def关键字定义方法。
v1 = 1
MyClass = Class.new do
v2 = 2
puts local_variables.to_s + "call 1" # => [:v2, :v1]call 1
define_method :my_method do
v3 = 3
puts local_variables.to_s + " call 2"
end
puts local_variables.to_s + "call 3" # => [:v2, :v1]call 3
end
MyClass.new.my_method # => [:v3, :v2, :v1] call 2
puts local_variables.to_s + "call 4" # => [:v1]call 4
instance_eval()和instance_exec()
在Ruby中,提供了一个非常酷的特性,可以通过使用Objec#instance_eval(), Objec#instance_exec()方法插入一个代码块,做一个的对象上下文探针(Context Proble),深入到对象中的代码片段,对其进行操作。有了这个特性以后,就可以很轻松的测试对象的行为,查看对象的当前状态。
class MyClass
def initialize
@v = 1;
end
end
obj = MyClass.new
obj.instance_eval do
puts self # => #<MyClass:0x007fbb2d0299b0>
puts @v # => 1
end
obj.instance_exec(5) { |x| puts x * @v } # => 5
可调用对象
从底层来看,对代码块的使用分为2步,
* 打包代码备用
* 调用yiel执行代码
这种先打包,后执行的策略称之为延迟执行(Deferred Evaluation)。
代码块在Ruby中并不是一个对象,但是,如果你想把一个代码块保存为一个对象以供调用应该怎么办呢? Ruby提供了Proc类,其简单来说就是一个转换成对象的块。在Ruby中,把一个块转换为Proc对象的方法有以上4种:
- * proc{…}
- * Proc.new { …}
- * lambda{…}
- * &操作符
proc1 = proc { |x| x+1 }
puts proc1.class # => Proc
proc2 = Proc.new { |x| x+1 }
puts proc2.class # => Proc
proc3 = lambda { |x| x+1 }
puts proc3.class # => Proc
&操作符只有在方法调用时才有效,在方法定义时,可以给方法添加一个特殊的参数,该参数必须为参数列表中的最后一个,且以&符号开头,其含义就是,这是一个Proc对象,我想把它当做一个块来用,如果调用该函数时,没有传递代码块,那么该参数值将为nil。有了这个符号之后,就可以很容易的把一个代码块转换为Proc对象,并在多个方法之间传递。
def my_method(&block)
test(&block)
block
end
def test
puts yield(3) if block_given? # => 4
end
p2 = my_method{ |x| x+1 }
puts p2.class # => Proc
Ruby中,所有的可调用对象,最后都是一个Proc对象,为什么还需要使用lambda这个方法来创建一个可调用对象呢? 使用lambda和使用proc创建的Proc对象有一些些微的差别,主要体现在2个方面:
- return关键字的行为,lambda中,return仅表示从lambda中返回, 而proc中,则是从定义proc的作用域中返回。
- 参数校验规则:lambda中,参数个数不对,会抛ArgumentError错误,而proc中,则会尝试调整参数为自己期望的形式,参数多,则忽略多余的,参数少则,自动补nil。
def proc_method
p = proc { return 10 };
result = p.call
return result*2
end
def lambda_method
p = lambda { return 10 };
result = p.call
return result*2
end
puts proc_method # => 10
puts lambda_method # => 20
p1 = proc { |a, b| [a,b] }
p2 = lambda { |a, b| [a,b] }
puts p1.call(1, 2, 3).to_s # => [1, 2]
puts p1.call(1).to_s # => [1, nil]
puts p2.call(1, 2, 3).to_s # => ArgumentError
puts p2.call(1).to_s # => ArgumentError
分享到:
相关推荐
六标准差之迷思.pptx
《教养的迷思》读后感.pdf
CEO管理运营之道经典实用课件之二:CEO四大迷思.ppt
科技品牌十大迷思
人工智能(AI)作为一门划时代的伟大技术,在教育领域的应用和发展,往往会伴随着教育迷思的出现。这些迷思主要源于人们对AI技术的过高期望、缺乏科学认识、以及AI与教育结合的不确定性。文章中提到了几个关键点,...
为了破解这些迷思,麦肯锡等专业机构提出了“八大关键战略举措”,包括同客户经营、规模化精准营销、产品定制、整合客户旅程、数据洞见共享、一体化团队协同、生态场景共创等。通过这些举措的实施,可以全面推动银保...
在教学实践中,教师应不断积累教学经验,优化教学设计,使之更加符合在线教学的特点。教学内容的设计要注重深度与丰富性,课程活动设计要具有挑战性和延展性。在此基础上,教师还应灵活运用多种教学方法和策略,如...
“七式团队迷思”是一个由心理学家欧文·珍尼斯提出的概念。这个概念描述的是一个团队陷入迷思的情况,从而导致团队作出一些劣质的决策。这种现象在团队管理中是非常普遍的,也是团队领导者需要特别注意的问题。 ...
### 破除迷思:SOC中的AI 在当今高度数字化的世界中,网络安全已经成为企业和组织不可忽视的关键领域。随着威胁的不断演变和技术的进步,安全运营中心(SOC)面临着前所未有的挑战。本文旨在深入探讨SOC中引入人工...
麦肯锡:破解迷思,探索高质量银保发展新模式(2021).pdf
《自我解释策略对转变学生化学平衡迷思概念的研究》这篇硕士专业学位论文主要探讨了如何通过自我解释策略来改变学生在化学平衡领域的错误认知,即迷思概念。论文作者为刘瑞东,由陈康叔和黄燕宁两位导师指导,属于...
健康行业中小企业发展迷思之做好一个产品概念.doc
网络安全五大迷思.pdf
【推动六标准差的迷思】在实际推行六标准差的过程中,可能会出现一些误解或误区,比如认为六标准差只适用于大型企业,或者将其视为一种短期的项目,而非长期的战略。此外,有些人可能认为六标准差过于复杂,难以在...
解析自有品牌策略与绩效关系的迷思:层级贝氏回归模式之运用.pdf
在探讨人工智能时代的迷思时,我们不可避免地会接触到两个核心议题:技术恐惧与技术拜物教。这两个概念不仅涉及人工智能技术本身的发展,也与社会、文化和伦理等因素紧密相关。以下将详细阐述这两个概念的内涵、成因...
传统职业经理人的迷思.doc
揭开FMCG客户的顾客关系管理迷思
### 破解青春期的迷思 青春期是一个充满变化与挑战的生命阶段,它不仅对青少年自身,也对周围的成人提出了考验。本文旨在澄清并解答围绕青春期的一些常见误解,帮助青少年及其监护人更好地理解和应对这一时期的种种...
《CEO的四大迷思》是一本探讨领导艺术的著作,由美国管理顾问帕特里克·兰西奥尼撰写。兰西奥尼以其在管理咨询领域的丰富经验,揭示了CEO们常常陷入的四个主要误区,并提出了如何构建和维持一个健全组织的见解。以下...