代码块在其他的语言中都或多或少接触过一些,如perl中sort{$a<=>$b}keys,传入代码块实现按数值排序,在swift中用到闭包,更加深入学习到training closure、capturing value等代码风格,对代码块有了深入的了解,并且意识到代码块是引用类型(Reference Type),和Value Type有所区别,意识到代码块和类、方法等的相似之处。

在学习Ruby的过程中,对代码块的理解更加加深一步,不仅仅是简化代码的功能,还涉及到作用域、可调用对象等知识。

代码块

代码块定义在大括号或者do...end关键字中,块被传递到方法中时,可以以yield关键字回调,同时能够通过Kernel#block_given?()方法检测是否传入了代码块:

class MyClass
    def my_method
        return yield if block_given?
        "There's no block"
    end
end
obj = MyClass.new
puts obj.my_method{"There's a block!"}
puts obj.my_method

块也可以有自己的参数,在||中用逗号隔开,调用时用yield(arg)就能调用:

def my_method
    return yield(2,3)
end
puts my_method {|x,y| "The two numbers are #{x} and #{y}”}

以上语句会输出 The two numbers are 2 and 3

绑定

块不仅仅可以通过调用参数的方式和其他语句交流,更加重要的是,块在定义的时候,会从当前作用域获取到绑定:

def my_method
    return yield(2,3)
end
z = 4
puts my_method {|x,y| "The there numbers are #{x},#{y} and #{z}"}

这理解起来并不难:在过程语句中,每一条语句都可以访问到当前作用域的其他变量,块也是一样,把可以运行的代码看做两部分组成:代码本身和一组绑定。

如果还是有些困惑的话,是因为表示代码块的{}并不意味着新的作用域,请注意Ruby中是没有{}表示开始结束的,而是用了类似python的风格。

作用域

那么在Ruby中,是什么真正影响了作用域呢?其实只有三个关键字会开启新的作用域,称为“作用域门”:

class、module、def

一旦碰到这三个关键字的其中一个,就意味着进入了新的作用域,并且在end关键字出现时离开,这里可以理解为作用域门就是左大括号{,end可以看成右大括号},如果你愿意这么理解的话。class、module和def有微妙的差别:class和module定义中的代码会被立即执行,而def定义中的代码只有被调用的时候才会被执行。

由于作用域仅仅由这三个关键字来决定,那么可以通过一些方法穿越作用域门,而让代码块获得上方作用域的绑定:用Class.new()、Module.new、Module#define_method()方法来代替class、module和def关键字:

var = "top level obj"
MyClass = Class.new do
    define_method :my_method do
        puts var
    end
end
obj = MyClass.new
obj.my_method

如上述代码,在my_method中任然可以访问到var,而不会受到作用域门的阻隔,这种方法成为扁平作用域,通过利用作用域门和穿越作用域门,可以灵活地共享作用域以及作用域保护。

打破封装

在作用域中,还有一种黑科技:Object#instance_eval()方法。这个方法可以传递一个块,作为上下文探针,该方法调用的块的接收者会成为self,此时,该块可以访问接收者的私有方法和实例变量,甚至可以在不碰其他的绑定情况下,修改self对象:

class MyClass
    def init
        @v = 1
    end
    attr_reader :v
end

obj = MyClass.new
obj.instance_eval {
    @v = 2
}
puts obj.v

此时,obj的v属性已经被修改了!所以为什么称它为黑科技了,因为他可以打破封装结构,同时它还有另外的作用,制作洁净室

洁净室是那些只为了执行其中的代码块的类:

class CleanRoom
    def complex_calculation
        #…
    end
   
    def do_something
        #…
    end
end
clean_room = CleanRoom.new
clean_room.instance_eval{
    if complex_calculation > 10
        do_something
    end

以上就是一个洁净室的例子

可调用对象

块的使用过程分为两步:打包和调用。将块打包后方便以后调用的方法有三种:proc、lambda、使用方法。

在Ruby中,绝大多数东西都是对象,但是块除外,如果想要将块打包存储,则需要调用一些类来获取帮助。

Proc就是其中的一种类。在新建Proc类时,会将调用的块存储,并在之后可以通过call来调用:

obj = Proc.new{"This is a block"}
puts obj.call

除了Proc.new之外,Ruby还提供两个内核方法用来将块转换成Proc:proc()、lambda(),其中proc()可以看成是Proc.new的一个别名(Ruby1.9之后),他们的区别是lambda更加像一个方法,他的return会从lambda中返回,而proc仅仅表示从当前作用域中返回,以及其他的一些不是很清楚的区别。

和在swift中类似,代码块可以看做是方法的最后一个隐含参数,也可以显示地命名它,要求使用&符号开头(这里没有任何联系,但是我记忆的时候将他理解为:因为块是引用类型,所以用&符号传入引用):

def my_method(a,b,&operation)
    #...
end
my_method(1,2){
    #...

此时,用&命名也是一种存储块的方式,通过这种方式可以让块的内容在多个方法之间传递,或者将这个块传递给另外一个Proc。

&符号的真实含义:将一个Proc对象作为块来使用,简单地去掉&,就可以再次获得Proc对象。直到了这点,也可以在其他地方,利用&Proc来将Proc转换回一个块。

 

Ruby学习之代码块的更多相关文章

  1. iOS学习之代码块(Block)

    代码块(Block) (1)主要作用:将一段代码保存起来,在需要的地方调用即可. (2)全局变量在代码块中的使用: 全局变量可以在代码块中使用,同时也可以被改变,代码片段如下: ;//注意:全局变量 ...

  2. Java学习之代码块(静态,构造代码块,构造方法)执行顺序

    静态代码块   static{ 代码 } 随着类的加载而加载,随类的消失而消失,存在于类中,方法外,最先执行,且只加载1次,可用来加载驱动及初始化对象属性. 构造代码块   {   } 也存在于类中, ...

  3. Lua学习入门(代码块)

    ). if then else if a < then b = else b = end ). if elseif else then if a < then b = elseif a = ...

  4. Ruby 中的闭包-代码块

    看了一片文章https://ruby-china.org/topics/38385讲closure的. 写下一些感想: 闭包就是 一个函数能够记住和存取它的lexical作用域,即使这个函数是在它的l ...

  5. [2014年学习计划之RoR系列] 第二步 – 熟悉Ruby语言 (2/n) Blocks and Iterators (代码块和迭代器)

    [就算没有含金量,也请尊重原创, 转载自我的独立博客http://brucejia.net] Blocks and Iterators (代码块和迭代器) 代码块和迭代器是Ruby语言中比较有特点的东 ...

  6. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  7. 从C#到Objective-C,循序渐进学习苹果开发(4)--代码块(block)和错误异常处理的理解

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...

  8. Ruby基础类型,动态特性,代码块

    #Ruby内置基础数据类型 NilClass,TureClass,FalseClass,Time,Date,String,Range,Struct,Array,Hash #Numerice 1.分为I ...

  9. IOS学习4——block代码块

    本文转载自:iOS开发-由浅至深学习block 一.关于block 在iOS 4.0之后,block横空出世,它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调.这不免让我们想 ...

随机推荐

  1. 38.Linux驱动调试-根据系统时钟定位出错位置

    当内核或驱动出现僵死bug,导致系统无法正常运行,怎么找到是哪个函数的位置导致的? 答,通过内核的系统时钟,因为它是由定时器中断产生的,每隔一定时间便会触发一次,所以当CPU一直在某个进程中时,我们便 ...

  2. OGNL简介

    OGNL 一:OGNL简介 OGNL的全称是Object  Graph  Navigation  Language即对象导航语音.它是一个开源项目,工作在视图层,用来取代页面中的java脚本.简化数据 ...

  3. 三种方法实现PCA算法(Python)

    主成分分析,即Principal Component Analysis(PCA),是多元统计中的重要内容,也广泛应用于机器学习和其它领域.它的主要作用是对高维数据进行降维.PCA把原先的n个特征用数目 ...

  4. Java数据结构和算法(八)——递归

    记得小时候经常讲的一个故事:从前有座山,山上有座庙,庙里有一个老和尚和一个小和尚,一天,老和尚给小和尚讲了一个故事,故事内容是“从前有座山,山上有座庙,庙里有一个老和尚和一个小和尚,一天,老和尚给小和 ...

  5. NOIP2014无线网络发射器选址改编1

    问题描述 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平行的129条东西向街道和129条南北向街道所形成的网格状,并且相邻的平 ...

  6. form表单提交和ajax提交优先级

    form中若定义action,那么,ajax将不能执行.form默认提交的请求优先级高于ajax

  7. linux crontab yum安装

    crontab工具来做计划任务,定时任务,执行某个脚本等等 1.检查是否已安装crontab # crontab -bash: crontab: command not found 执行 cronta ...

  8. android手机状态解释,比方android.os.Build.VERSION.SDK

    //BOARD 主板 String phoneInfo = "BOARD: " + android.os.Build.BOARD; phoneInfo += ", BOO ...

  9. 怎样获取HTML5视频的持续时间

    HTML5视频的Bigger体验是非常令人振奋的,非常easy的道理,不用载入和依赖烦人的Flash或其它第三方插件来播放视频,也是大功一件.我们能够通过自己定义控件对视频进行显示和操控,当中一个常见 ...

  10. 掌上快递 APP 项目之概述篇

    概述 学习Android开发也有一段时间了,利用业余时间独立制作的一款快递类APP软件.大概2个多星期吧,自己将其定位为"集快递信息追踪.附近快递点查询. 快递公司投诉功能为一体的便民生活类 ...