Ruby学习之代码块
代码块在其他的语言中都或多或少接触过一些,如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学习之代码块的更多相关文章
- iOS学习之代码块(Block)
代码块(Block) (1)主要作用:将一段代码保存起来,在需要的地方调用即可. (2)全局变量在代码块中的使用: 全局变量可以在代码块中使用,同时也可以被改变,代码片段如下: ;//注意:全局变量 ...
- Java学习之代码块(静态,构造代码块,构造方法)执行顺序
静态代码块 static{ 代码 } 随着类的加载而加载,随类的消失而消失,存在于类中,方法外,最先执行,且只加载1次,可用来加载驱动及初始化对象属性. 构造代码块 { } 也存在于类中, ...
- Lua学习入门(代码块)
). if then else if a < then b = else b = end ). if elseif else then if a < then b = elseif a = ...
- Ruby 中的闭包-代码块
看了一片文章https://ruby-china.org/topics/38385讲closure的. 写下一些感想: 闭包就是 一个函数能够记住和存取它的lexical作用域,即使这个函数是在它的l ...
- [2014年学习计划之RoR系列] 第二步 – 熟悉Ruby语言 (2/n) Blocks and Iterators (代码块和迭代器)
[就算没有含金量,也请尊重原创, 转载自我的独立博客http://brucejia.net] Blocks and Iterators (代码块和迭代器) 代码块和迭代器是Ruby语言中比较有特点的东 ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- 从C#到Objective-C,循序渐进学习苹果开发(4)--代码块(block)和错误异常处理的理解
本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...
- Ruby基础类型,动态特性,代码块
#Ruby内置基础数据类型 NilClass,TureClass,FalseClass,Time,Date,String,Range,Struct,Array,Hash #Numerice 1.分为I ...
- IOS学习4——block代码块
本文转载自:iOS开发-由浅至深学习block 一.关于block 在iOS 4.0之后,block横空出世,它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调.这不免让我们想 ...
随机推荐
- UWP 五星好评
var pfn = Package.Current.Id.FamilyName; await Launcher.LaunchUriAsync(new Uri("ms-windows-stor ...
- 局域网下的html注入及DNS劫持
之前研究Dsploit的部分功能实现原理,现在了解的差不多了,简要记录下Dsploit的断网攻击,html注入,图片替换,cookie劫持的原理.本篇blog需要有一定的网络知识基础在看. 假设现有三 ...
- JWT.NET的使用
JWT是什么 JWT全称是Json Web Token,是一种用于双方之间传递安全信息的简洁的.URL安全的表述性声明规范.JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的 ...
- ElasticSearch 学习记录之集群分片内部原理
分片内部原理 分片是如何工作的 为什么ES搜索是近实时性的 为什么CRUD 操作也是实时性 ES 是怎么保证更新被持久化时断电也不丢失数据 为什么删除文档不会立即释放空间 refresh, flush ...
- OpenCV基础篇之查找表
程序及分析 /* * FileName : lookup_table.cpp * Author : xiahouzuoxin @163.com * Version : v1.0 * Date : Su ...
- UVA12493 - Stars(求1-N与N互质的个数)欧拉函数
Sample Input 3 4 5 18 36 360 2147483647 Sample Output 1 1 2 3 6 48 1073741823 题目链接:https://uva.onlin ...
- 介绍一个法国的时间戳server
特别说明: 以下介绍的法国时间戳server已经停止服务了.我曾发Email给相关站点的管理员.对方回复说他也不知道什么时候能恢复服务,有可能就是遥遥无期了.所以以下的内容仅有參考价值.没法实践了. ...
- xml概述(1)
本节要点: 标记语言的定义 XML简介 XML与HTML 比较 XML与数据库比较 XML的特点 XML结构 1 标记语言的定义 "XML.SGML.HTML"中的"ML ...
- Linux基础:xargs命令
简介 xargs可以将输入内容(通常通过命令行管道传递),转成后续命令的参数,通常用途有: 命令组合:尤其是一些命令不支持管道输入,比如ls. 避免参数过长:xargs可以通过-nx来将参数分组,避免 ...
- chrome调试,打完断点后关于JS的几个控制介绍
打完断点之后,关于JS的几个控制介绍. 快捷键:F8 "逐过程执行",继续执行代码,直到遇到下一个断点. 详细解释: 暂停和开始.当设置了断点之后,js的执行就暂停了,如果我们想要 ...