`
ruby_windy
  • 浏览: 62731 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

理解Ruby中block的本质

阅读更多
Ruby非常特色的特性有两点:
  • Module 优雅地解决多继承问题
  • Block 块调用

虽然这两个特性均不是Ruby原创,但显然是它将这两个特性发挥到很恰到好处,害的最近的C#也在改进支持它们.

然而,正是这两个特性,使得RubyBeginner经常迷惑不解.这也是我在学习过程中经常会遇到的问题,我想就将Block解牛的过程列出来,也算是对自己的过程作一个记录吧.

前言
在Ruby,所谓的"Block"有多种,而Block在计算机科学理论中被称为"过程",(哇,就是当年研究Pascal中的关键字closures)" , Block在Ruby中有几种称谓,Blocks, Procs 和 lambdas. 其中表现行为上具有些许不同,也是造成Rubyers误解的直接原因.
没办法,ruby设计哲学之一就是,与现实世界类似,一个问题可以有几种不同的方法解决.
那么,为了更少的编码,为了更"RubyLike"的编码,我们开始吧.

开始
1. 它应该是什么?
(如果你还没弄清楚Block是怎么回事,建议回去啃RubyProgramming第二版那本镐书先)
我们先从一个需求开始,
我们看一个例子,数组支持排序,供我们使用.
那么看一下用法:
#按长短排序
["p","bc","de"].sort do |i,j|
  i.size <=> j.size
end
#按字母顺序
["p","bc","de"].sort do |i,j|
  i <=> j
end


看得出,使用block使得排序这个sort方法很强大.也很易理解.

如果对C很熟悉,也许有一种感觉,很像C的函数指针呀,确实,我开始的时候也有此感觉,不过细想,C的函数指针使用真的风险很大,即复杂又要各种类型转换.

如果对函数式编程有所了解的话,可以感觉到这个block又像所谓的"高阶函数"参数,嗯,确实,其实大部分时候就把它当作方法的参数即可,只可以这个参数不是一个表达式,而是一份完整的代码.


2. 有哪些形式?
(引子,听说过block,Proc,lamba,Method了吧?)
接下来看看它们的形式:

block:
block作为ruby最经常使用的语法之一,也是最"RubyLike"的,其用法以上简单介绍了.
我们再看到如何实现自己方法的block.

def method_with_block
  yield
end
method_with_block do puts 'call me' end


so easy, 使用yield这个关键字即可支持所谓的方法参数,如果想支持带block参数,也可以:

def method_with_block_parameter
  n = 'windy'
  yield(n)
end
method_with_block do |i| puts 'call me' + i end


Proc:
作为block的同胞,它是在ruby发展过程中出现的,它出现的目的,很简单,能复用block.
看一个需求:
帮我把两个数组arr1,arr2都按长短排序,我们利用Proc可以这么写:
size_code = Proc.new do |i,j|
  i.size <=> j.size
end
arr1.sort(&size_code)
arr2.sort(&size_code)

很简单嘛,可以注意到它的参数带了&这个符号,&的目的也很简单,告诉ruby解释器,'哟,这个不是一般的参数,是"高阶函数"'
另外,细心的人可以发现,我写Proc总是P大写的,而block总是小写,这是为什么呢?
1. Proc实际是Ruby内置的一个类,而block只是我们称谓代码块用的简写.
2. 一个Proc实例可以当作方法参数传递,而block更像是匿名的Proc

说到这里,block跟Proc基本上区别已经很清晰了,我们再补充一些关键的特性:
1. block与Proc跳出代码都使用next(而不是return,接下来要说的lambda正好与此相反)
2. block与Proc都只能在方法中接受一次,也即方法不能获得多个block或Proc(不用担心,松本行弘也说过,你用不着使用两个block) (另外,其实,你可以传递一个hash或数组,得到两个Proc实例)
3. 它们都有返回值,即最后一句代码的返回值.(我一直对ruby所有表达式的返回值没有弄的十分清楚,有弄清楚的同学欢迎讨论下)

lambda:
使用lambda让ruby更加的有趣起来,用老外的话叫funny了,也使我们更加困惑了,弄这么多形式干嘛?
先说下lambda本身,我们知道ruby是揉合了众多语言的特点,lambda不例外的是从lisp本身得到的命名.lambda念出来大家都好像听过,嗯,就是希腊的字母λ嘛.(第十一个)
我想说的就是它只是一个关键字,为了表达某种意思,产生的原因就是ruby语言之神设计的.(没啥特别的...)

来历我们清楚了,看下使用:
以上面的Proc的例子为准,我们继续写一个lambda的例子:

size_code_lambda = lambda do |i,j|
  i.size <=> j.size
end

arr1.sort(&size_code_lambda)
arr2.sort(&size_code_lambda)

嗯,没区别?

嗯,这里真的没区别,靠,那要它做甚.
其实你可以使用 size_code_lambda.class 看一下,发现它也是Proc对象?
嗯,怎么回事,其实你使用&传递的时候,ruby帮你全部转换成Proc了,不过此Proc对象非彼Proc对象,是有区别的.

我们再看一个例子:
一不小心我没弄清楚sort能传几个参数进去.
我这么写了(如果,多了一个k):
size_code_lambda = lambda do |i,j,k|
  i.size <=> j.size
end
arr1.sort(&size_code_lambda)

嗯,报异常了.
你把用Proc的代码跑一下,嗯? 没问题.
好,我们总结第一个区别:
1. lambda开始帮我检查代码传递参数了,而Proc不帮忙.
那既然Proc不检查,它传什么给我? 我们可以很快验证下:
size_code = Proc.new do |i,j,k|
  puts k,k.class
  i.size <=> j.size
end
arr1.sort(&size_code)
# nil, NilClass

嗯,空值.没错.(跟你预期一样吧?)

再继续看,我们有时候想从代码块中返回出来继续执行怎么办?
试着return吧:
一个例子
def methods(&code)
  code.call
  puts 'methods end'
end

a_proc = Proc.new { puts 'call proc' ; return ; }

a_lambda = lambda { puts 'call lambda' ; return ; }
methods(&a_proc) # => unexpected return (LocalJumpError)
methods(&a_lambda) # => methods end



输出截然不同啊,那么我们有第2个区别了:
2. lambda中返回使用return,而Proc/block中不能.
那么使用什么返回,看过那本镐书后应该知道,使用next即可.

那么加深一点印象,提个问题,如何Proc使用return为什么会返回LocalJumpError?
其实很简单,Proc保留了当时定义代码块的上下文环境,即闭包,这里返回LocalJumpError的原因是我们定义的上下文无法再次return了
换句话说,Proc中return其实是直接return到proc的上一层环境了.
那其实这里有个技巧:

使用Proc可以实现高阶跳转

如:
def go
  a = Proc.new do 
    puts 'proc'
    return
  end
  
  methods(&a)
  
  puts 'end go'
end

def methods(&a)
  puts 'methods'
  a.call
  puts 'end methods'
end

go
# =>methods
# proc

看懂了你算完全明白了Proc与lambda的这点区别了.

好了,区别总算讲完了,那我们应该怎么去使用呢?
1. lambda更像是一个完整的方法,只不过是匿名的,它的参数会被"神"检查下,它return的时候就是从自己的"方法"返回.
2. Proc只是一个过程,无依无靠,参数不对的时候只是被遗弃或随机捡一个nil送给你,return的时候就从原始父环境返回.

根据ruby发展过程,lambda比block/Proc更晚一点出来,lambda语法当然会比proc更好一些(从不要让我惊讶的角度),但proc没有消失的原因,一方面是兼容性,另一方面也与它的"魔法"有一点关系.
所以,使用的话,block应该优先被使用,它更简洁.
如果需要进行对象传递的话,建议更多使用lambda,它更少让你惊讶,还可以帮你检查参数,何乐而不为呢?
如果遇到Proc的高阶跳转用法了,能明白就好.

看到这里,基本上这篇总结的营养就没啥的了,你也会感觉都明白于心了.但是,我还是想把实际情况彰明一下:
lambda其实也不过是Proc的一种实例...(ruby学习深度过高的原因就在这里了,有兴趣的可以研究或跟我探讨一下)

最后最后,lambda既然如此像方法(Method),就简单说一下Method:
Method也是ruby中的一个类,它的名字已经告诉我们一切了,见一个例子:
def a
  puts 'a'
end

puts method(:a).class

method(:a).call
# => Method
# a


Method类与lambda非常非常像,除了它有个名字外...

好了,我可以去歇菜了,你们也好自为止...
分享到:
评论
2 楼 jonson_hhb 2012-05-26  
Good, 3ks for your job !
1 楼 夜鸣猪 2012-03-07  
确实比我那个要清楚些

相关推荐

    深入理解Ruby中的代码块block特性

    它可以用来执行Block中的代码,并且还能向Block传递参数。这使得Block可以作为回调函数来使用: ```ruby def test(&block) block.call("world") end test { |msg| puts "hello #{msg}" } # 输出: hello world ```...

    深入理解Ruby中的block概念

    深入理解Ruby中的block概念,是理解Ruby编程的关键之一。Block在Ruby中扮演着代码块的角色,它可以被看作是高阶函数的一种特殊形式的语法。虽然Ruby不支持First-class functions,即函数不是一等公民,但block使得...

    深入讲解Ruby中Block代码快的用法

    #### Block中的变量 Block内的变量可以分为两种:局部变量和外部变量。 1. **局部变量**:这些变量仅在Block内部可见,不能在Block外部访问。例如: ```ruby [1, 2, 3, 4].each do |value| square = value * ...

    ruby中文教程,从基础到深入的让你学习ruby

    在学习Ruby的过程中,你还需要了解一些工具,如Ruby的解释器irb(交互式Ruby环境)和ri/rdoc(Ruby文档工具),它们有助于调试和理解代码。版本管理工具如RVM(Ruby Version Manager)和rbenv可以帮助你管理多个Ruby...

    Ruby中Block和迭代器的使用讲解

    Block是Ruby语言中的一个非常重要的特性,可以将其理解为一段可执行的代码片段。它能够像参数一样被传递,并且在方法调用中被激活。Block的强大之处在于它不仅提高了代码的可读性和简洁性,还极大地增强了程序的灵活...

    ruby中英文api

    书中可能包含了大量的示例代码和实践案例,帮助读者理解并掌握Ruby的精髓。 在学习这些文档时,重要的是理解和掌握Ruby的核心理念,即“代码应该是可读的”,这意味着应尽量保持代码简洁,避免冗余,并利用Ruby的...

    ruby 中文文档 必备资料

    总之,这份"ruby中文文档"是学习Ruby的宝贵资源,它将帮助你理解语言的核心概念,掌握面向对象编程,了解元编程的威力,并熟悉Ruby on Rails框架的使用。通过深入阅读和实践,你将在Ruby的世界里游刃有余。

    ruby中文文档.chm

    Ruby中的块(Block)、 Proc 和 Lambda 是其元编程能力的重要体现。它们允许你在运行时创建和修改代码,这在编写高度定制和自适应的程序时非常有用。文档会详细介绍这些概念及其在实际编程中的应用。 此外,Ruby...

    Ruby 小白入门指南理解 Ruby 及其特点.txt

    安装完成后,你可以在命令行中输入 ruby -v 来检查 Ruby 是否成功安装以及安装的版本。 三、学习基础语法 变量和数据类型:Ruby 支持多种基本数据类型,包括整数、浮点数、字符串、数组、哈希等。你需要了解如何...

    Ruby中的block、proc、lambda区别总结

    主要介绍了Ruby中的block、proc、lambda区别总结,本文讲解了yield 和 block call 的区别、block 和 proc、lambda 的区别、proc 和 lambda 的区别,需要的朋友可以参考下

    ruby中文文档中心资料

    这份"ruby中文文档中心资料"包含了丰富的资源,帮助中文用户深入理解并掌握Ruby编程。 首先,"nb60_trans_old_zh_CN"可能指的是Ruby的一个版本,如NetBeans IDE 6.0的中文翻译版,NetBeans是一个流行的开源集成开发...

    ruby中文手册 chm

    中文翻译版可以帮助中文读者更好地理解书中的概念,从而提高学习效率。 通过这个中文手册,开发者不仅可以学习到Ruby的基本语法,还可以了解到更高级的主题,如元编程、闭包、 Blocks、Proc对象和Symbol,以及如何...

    ruby中文教程(源代码)

    - 函数与方法:Ruby中的函数被称为方法,可以定义在类或模块中,支持块(block)和 Proc 对象。 2. **面向对象编程(OOP)** - 类与对象:Ruby是纯面向对象的语言,一切皆对象。你可以创建自定义类来封装数据和...

    ruby中文 文档

    Ruby是一种强大的、动态的、面向对象的编程语言,它的设计理念在于强调代码的可读性和简洁性,这使得它成为开发者们的热门选择。...阅读并实践文档中的例子,将有助于加深对Ruby的理解,提升编程能力。

    Ruby中文教程及相关源代码

    3. **函数与方法**:Ruby中的函数定义和调用,块和 Proc 对象,以及方法的定义、重载和作用域。 4. **面向对象编程**:Ruby是纯面向对象的语言,讲解类的定义、继承、模块(Module)的使用,以及对象创建和消息传递...

    Ruby入门教程中文PDF 附实例

    本教程针对初学者,旨在帮助读者快速掌握Ruby的基础知识,并通过实例深入理解其用法。 首先,Ruby的基本语法是它的一大亮点。Ruby允许开发者以更自然的方式编写代码,这得益于它的语法接近于英语。例如,变量的声明...

    ruby语言帮助文档(简体中文)

    Ruby语言的帮助文档是学习和理解其语法、类库、方法和框架的重要资源。 这个"ruby语言帮助文档(简体中文)"包含了Ruby的所有基础知识,从语言特性到高级概念,为初学者和经验丰富的开发者提供了全面的指导。以下是...

Global site tag (gtag.js) - Google Analytics