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()的方式进行回调.这不免让我们想 ...
随机推荐
- 在php代码中执行liunx命令
- jquery写的树状列表插件-alvintree
在做项目的时候遇到选择部门下人员的功能,可多选可单选,所以就想着使用树状列表来进行选择,但在网上找了很多,发现要么就是挺复杂,要么就是需要各种前端框架的支持,试了一个感觉难用,所以就想着自己写一个简便 ...
- Dagger2进阶必备技能
之前写过一篇文章介绍Dagger2的初步知识, 本篇文章主要介绍Dagger2的进阶知识点. 主要包含的内有有 @Binds与@Provides的使用 Provider与Lazy的使用 依赖与包含 D ...
- 框架原理第一讲,熟悉常用的设计方式.(以MFC框架讲解)
框架原理第一讲,熟悉常用的设计方式.(以MFC框架讲解) 一丶什么是框架,以及框架的作用 什么是框架? 框架,简而言之就是把东西封装好了,使用框架开发可以快速开发程序,例如MFC程序的双击写代码. 为 ...
- Linux多进程编程实例
前言:编写多进程程序时,我们应该了解一下,创建一个子进程时,操作系统内核是怎样做的.当通过fork函数创建新的子进程时,内核将父进程的用户地址空间的内容复制给子进程,这样父子进程拥有各自独立的用户空间 ...
- day5、文件乱码怎么解决
1.1 Linux下,如何将一个乱码的文件进行重命名 方法一: 命令格式:mv $(ls |egrep "[^a-zA-Z0-9.-]") tandao.tx [root@nb ...
- windows 怎样查看port占用情况
開始--执行--cmd 进入命令提示符 输入netstat -ano 就可以看到全部连接的PID 之后在任务管理器中找到这个PID所相应的程序假设任务管理器中没有PID这一项,能够在任务管理器中选&q ...
- C++、Objective-C 混合编程
在XCODE中想使用C++代码,你须要把文件的扩展名从.m改成.mm.这样才会启动g++编译器. 我们来看个測试代码: [java] view plaincopy class TestC { priv ...
- 初识ajax
ajax优势:不刷新整个页面,只刷新局部(无刷新) 无刷新的好处: 只更新部分页面,有效利用宽带 提供连续的用户体验 提供类似C/S的交互效果,操作更方面 什么是ajax AJAX :代表 Async ...
- MVC Anti-XSS方案
1:Form提交模式 在使用Form提交时,MVC框架提供了一个默认的机制.如果数据中含有恶意字,则会自动转向出错页面. 2:Ajax+JSON提交模式. MVC框架未提供对于Json数据的Ant ...