Proc对象
来源:互联网 发布:工作日志管理系统php 编辑:程序博客网 时间:2024/06/09 23:32
对象的原生行为
查看原生行为
irb> puts Object.new.methods.sortirb> [:!, :!=, :!~, :<=>, :==, :===, :=~, :__id__, :__send__, :class, :clone, :define_singleton_method, :display, :dup, :enum_for, :eql?, :equal?, :extend,:freeze, :frozen?, :hash, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_o, :method, :methods, :nil?, :object_id, :private_methods, :protected_methods,:public_method, :public_methods, :public_send, :remove_instance_variable, :respond_to?,:send, :singleton_class, :singleton_method, :singleton_methods, :taint, :tainted?,:tap, :to_enum, :to_s, :trust, :untaint, :untrust, :untrusted?]
用 object_id 方法唯一标识对象
irb>obj = Object.newirb>obj.object_idirb>70206757165840irb>str = "abc"irb>70206756020620
用 respond_to?方法查询对象的能力
irb>obj = Object.newirb>if obj.respond_to?("talk")irb> obj.talkirb>elseirb> puts "sorry,the object does's understand the 'talk' message"irb>endirb>sorry,the object does's understand the 'talk' message
- 用send方法发送信息给对象
Proc对象
使用 Proc.new 创建 Proc 的实例。通过实例化 Proc 类并包含代码块,创建 Proc 对象:
irb> pr = Proc.new{puts "Insid a Proc's block"}irb> pr.call
proc 方法:
proc 方法携带一个代码块然后返回一个 Proc 对象。因此可以说 proc { puts “Hi!” }代替 了 Proc.new { puts “Hi!” },它们有相同的结果。Proc.new 和 proc 通常稍有不同,proc 作为匿名的方式用于 lambda(详见 14.2 节),而 proc/lambda 方法则会产生特有的 Proc 对象, 它与 Proc.new 产生的对象不完全相同。这里是有些让人困惑,所以现在尽管 Proc 对象有这两种变 化形式,但暂且把 Proc.new 和 proc 看作同一事物,相反 lambda 则会产生其他的变化。至少,从 命名上它们更为相似。
proc 和代码块以及区别
创建 Proc 对象时,总是要提供一个代码块。但不是每个代码块都可以作为 proc 的主要成 分。这个片段:
irb> [1,2,3].each{|x| puts x * 10}
涉及一个代码块但是并没有创建 proc。亊情要比之前的复杂一些。使用第 9 章中短暂所见的特殊 参数语法,方法会捕获一个代码块,将其对象化成为 proc。
irb> def call_a_proc(&block) block.call endirb> call_a_proc{puts "I'm the block ... or Proc ...or something"}irb> I'm the block ... or Proc ...or something
但是通过使用相似的特殊语法,proc 能够代替在方法调用时的代码块:
ibr> p = Proc.new{|x| puts x.upcase} %w{Divid Black}.each(&p)irb> DIVID BLACK
代码块与proc相互转换:
代码块和 proc 相互转换非常容易——不用惊讶,因为代码块存在的目的就是被执行,而 proc 是对象,它的任务就是执行之前定义好的代码块。首先将看到代码块转换为 proc,稍后会看到 proc 替代代码块的使用。
获取代码块作为proc
下面将以另一个简单的示例方法作为开端,该方法会捕获自己的代码块作为Proc 对象然后调用这个对象:
irb> def capture_block(&block) block.call endirb> capture_block{puts "Inside the block"}
注:&标记也会出现在执行另一个转换操作时:使用 Proc 对象代替代码块时
&block = Proc.new{puts "Inside the block"}
capture_block{puts "Inside the block"}
对代码块使用proc
下面是如何使用 proc 代替代码块时,调用 capture_block 的做法:
irb>p = Proc.new{puts "This proc argument will serve as a code block"}irb>capture_block(&p)irb>This proc argument will serve as a code block
to_proc方法概述
**理论上,可以在任何类中或任何对象中定义 to_proc 方法,然后这些受影响的对象就可以 运用&标记的技术了。可能不需要为这个做太多的工作,对于 to_proc 最有用的两个类是 Proc (乊前谈论到)和 Symbol(下一节中讨论),且 to_proc 行为已经被内建在这些类中。不过看 一下 to_proc 是如何与自定义的类合并使用的,可以对编程语言表面乊下的强大动态特性有一
些感觉。**下面是一个相当奇怪但具有指导性的代码:
irb> class Person attr_accessor :name def self.to_proc Porc.new{|preson| person.name} end end irb>d = Person.new irb>d.name = "mafeihu" irb>m = Preson.new irb>m.name = "Matz" irb>puts [d,m].map(&Person) irb>mafiehu Majosn
简洁的Symbol#to_proc
内置方法Symbol#to_proc在如下情况中使用
ibr>%{ david black}.map{&:capitalize}irb>["David","Blocak"]
注:
符号:capitalize 被解释为依次収送到数组中每个元素的消息。因此,前面的代码与下面 的对等:
irb>%w{david black}.map.{|str| str.captitalize}
但是,正如代码展示的,它更为简洁。
如果看到了&:capitalize 或者在代码中看到过相似的构造,可能会认为这很神秘。但是 了解它的解析原理(即了解:capitalize 是一个符号,还有&是一个 to_proc 触収器)则会正 确地理解它,并欣赏它的表现力。Symbol#to_proc 还可以很好地用于不使用圆括号的情况:
irb>%w{david black}.map &:capitalize
将圆括号去掉后,可以让这个被 proc 加工过的符号看起来很像是位于代码块所在的位置。 当然,这里没有必要这样做。应该记住的是:每当使用 to_proc &标识符时,就是在通过&把 proc 标记为参数迚行传递,而不用再提供代码块。
与 Ruby 的其他亊物相比,Ruby 所提供的 Symbol#to_proc 是一个很好的例子,可以让用 户能够在必要时简化所编写的代码。实现Symbol#to_proc
irb>%{david black}.map(&:capitalize) 等同于irb>%w{david black}.map{|str| str.capitalize} 等同于irb>%w{david black}.map{|str| str.seed(:capitalize)
通常来说,不用为它编写什么,因为如果能够使用常觃的点运算符语法调用方法,就不必费力 去使用 send。但是基于 send 编写的版本可以指明 Symbol#to_proc 的实现方式。在本例中代码 块的任务是为了収送符号:capitalize 给数组的每个元素。这就意味着通过:capitalize #to_proc 创建的 Proc 一定会将:capitalize 作为参数収送给它自己。将这些概念总结一下,可调用和可运行对象以提出这个对 Symbol#to_proc 的简单(人们会说这几乎有点虎头蛇尾)实现:(理解有问题)
class Symbol def to_proc Proc.new{|obj| obj.send(self)} endend
proc作为闭包使用
irb>def call_some_proc(pr) a = "irrelevant 'a' in menthod scope" puts a pr.callirb>endirb>a = "'a' to be used in Proc block"irb>pr = Proc.new{puts a}irb>pr = callirb>call_some_proc(pr)irb>irrelevant 'a' in menthod scopeirb>'a' to be used in Proc block
经典的闭包示例是计数器。下面的方法返回了一个闭包(绑定了局部变量的一个 proc)。proc被用作计数器,它会在每次调用的时候增加其变量的值:
irb>def make_counter n = 0 return Proc.new{ n +=1 }irb>endirb>c = make_counterirb>puts c.call irb>puts c.callirb>d = make_counterirb>puts d.callirb>puts c.call输出结果如下:1213
proc 中的逻辑是 n 加 1��,因此 proc 第一个被调用时,它得到的值为 1,第事次是 2,以此类推。 调用 make_counter 和稍后调用 proc 的返回值确认了这一点:第一次 1 被打��出来,第事次是 2��。 但是从 1 开始,新计数器再次创建,第事次调用 make_counter 创建了一个��,而局部变量 n 被 保存在不同的 proc 中。前两个计数器的不同点,通过第三次对第一个计数器的调用可以解释清楚, 这一次打��输出 3��。在 proc 创建的时候,其内部会使用被保存的变量 n,这个过程会不断地延续。
如同所有的代码块,创建 Proc 对象时提供的代码块可以带有一个参数。下面会深入讲解在 Proc 创建过程中,代码块的实际参数和形式参数是如何工作的。Proc的形式参数和实际参数
下面是proc的实际化过程,并携带有一个参数的代码块:
irb>pr =Proc.new{|x|puts "Called with arguemnt#{x}"}ibr>pr.call(100)结果输出为:called with arguement 1000
proc 与方法的不同在于参数的处理方式,因为它们并不关心参数的数量是否正确。只有一个参数的 proc 是如下形式:
>>pr = Proc.new{|x| p x}=>#<Proc:0x007fa07481e9a0@(irb):28>
**它被调用时可以携带任意数量的参数,甚至没有参数。如果调用的时候没有参数,它的单一
参数将被设置为 nil:**>>pr.callnil
如果调用时多于一个参数,那么单一的参数将会绑定到第一个参数,其他参数将被丢弃:
>>pr.call1
使用lambda和->创建函数
如同 Proc.new,使用提供的代码块作为函数主体,lambda 方法也会返回一个 Proc 对象:
>>lam = lambda{puts "A lambda!"}=> #<Proc:0x007fa07482d040@(irb):31 (lambda)>>>lam.callA lambda!
从对字符串的检查中可以看出,从 lambda 中返回的是 Proc 类的对象。但是要注意 (lambda)表示法,虽然没有 lambda 类存在,但是 Proc 类有一个独特的 lambda 风栺。 lambda 风味的 proc 与��通 proc 有 3 个地方稍有不同:
首先,lambda 需要明确的创建过程。不论 Ruby 在何处隐式地创建 Proc 对象,它们都是常 觃的 proc 而不是 lambda。这首先就意味着当抓取一个在方法中的代码块时,如:
def m(&block)
抓取到的Proc对象是一个常规proc而不是lambda
**其次,lambda 与 proc 的不同是它们对待 return 关键字的方式。在 lambda 中的 return
会触収整个 lambda 主体立即退出 lambda 所在的代码上下文。proc 中的 return 只会从 proc 被 执行所在的方法中返回。下面有对这个不同点的说明:**def return_test l = lambda{ return } l.call puts "still here" p = Proc.new{return} p.call puts "You won't see this message"endreturn_test
这个代码片段的输出为”Still here!”。读者不会看到打��第事条消息��,因为对 Proc 对象��的调用触収了从 return_test 方法的返回。但是对 lambda 的调用��则会触収整个 lambda 主体的返回(退出),然后方法会从上一次中断的地方继续执行。
警告:
因为在proc(非lambda风味)内部的return触发了闭合方法的返回,
当没有位于方法内部调用包含 return 的 proc 时,会产生一个严重错误。
如果要看到这个错误的演示过程,可试着在命 令行执行如下命令:ruby -e 'Proc.new
{ return }.call'。**最后,最为重要的是,lambda 风栺的 proc 不能在调用的时候使用错误的参数数目。它们是
非常挑剔的:**>>lam = lambda {|x| p x}=> #<Proc:0x007fa07309da60@(irb):33 (lambda)>>>lam.call(1)1=>1>>lam.callArgumentError: wrong number of arguments (given 0, expected 1)>>ca,.call(1,2,3)ArgumentError: wrong number of arguments (given 3, expected 1)除了lambda方法,还有一个lambd的字面构造器。
lambda构造器->
lambda构造器(昵称为 “sabby lambda”)是这样运用的:
>>lam = ->{puts "hi"}=>#<Proc:0x007fa073a06250@(irb):38 (lambda)>>>lam.callhi
如果想要让 lambda 带有参数,就需要将参数放在->乊前的圆括号中,与代码块中把参数放 置在两条竖线中间不同:
>>mult = ->(x,y){x * y}=> #<Proc:0x007fa07295d7c8@(irb):39 (lambda)>>>mult.call(3,4)=>12
- Proc对象
- Proc对象相关
- proc下使用OTT生成对象结构
- 代码块续 (proc对象)
- proc
- proc
- /proc
- proc
- proc
- proc
- /proc
- proc
- /proc/
- proc
- /proc
- /proc/*
- PROC
- proc
- python进阶11:创建类和实例
- ubuntu 12.04 hadoop2.7.3 环境配置
- 第十七章 SpringMVC拦截器配置
- js 识别web和手机浏览器
- mysql 的sql优化
- Proc对象
- 小米4手机选择图库发送图片时崩溃
- 二叉树算法趣题
- Python的方法解析顺序(MRO)
- php7.1微信公众平台消息安全模式的加密及解密
- 亚马逊前首席科学家:大数据价值体现在AI、BI、CI、DI
- java多线程注意点
- Android4种网络连接方式HttpClient、HttpURLConnection、OKHttp和Volley优缺点和性能对比
- 第十八章 SpringMVC @ControllerAdvice