block 和 Proc

ruby中的block是方法的一个重要但非必要的组成部分,任何方法后面都可以挂载一个block,如果你定义的方法想使用block做点事情.那么你需要使用yield关键字或者&p.

def f1

puts 'f1'

end

def f2(&p)

puts 'f2'

p.call if block_given?

end

def f3

puts 'f3'

yield if block_given?

end

f1 ## 'f1', 普通方法调用不挂载块时

f1 {puts 'block'} ## 'f1' 普通方法调用挂载块时

f2 ## 'f2', 如果没有 判断 if block_given?, 将会报错

f2 {puts 'block'} ## 'f2' 'block', 挂载了块作为参数&p传入,p是一个proc,可以用关键字 call 来执行

f3 ## 'f2', 如果没有 判断 if block_given?, 将会报错

f3 {puts 'block'} ## 'f2' 'block',挂载了块,用关键字 yield触发

我们可以看到,任何方法都可以挂载一个block.

要触发block执行时,用关键字 yield会执行传入的块.

也可以通过在参数里定义&p, 传入block为一个参数,用call去触发.

block就是一段绑定了当前作用域变量代码,call的时候就是当前位置嵌入了代码

那么block作为参数传入方法后是一个什么对象呢?既然是参数,是不是可以再作为参数传递给其他方法呢?

def block_args(&p)

puts p.class

puts p

p.call

block_yield &p

end

def block_yield

puts 'yield'

yield

end

block_args {puts 'hahaha'}

## 'Proc'

## #<0x007fb2f3ae5da0>0x007fb2f3ae5da0>

## 'hahaha'

## 'yield'

## 'hahaha'

由上可见,block作为参数传入方法后是一个Proc的实例.call是proc的method,传入的块是&p, 在方法里 p是proc, 如果要作为参数再进行传递,应该使用块 即 &p.

记住这句话: "&p是block, p是proc",一般挂载在方法后面的是block,proc是个'幕后工作者',一般不会显式地创建它.

yield和&p的用法类似,&p参数传入作为c可以作为参数再传递,yield无需在方法上定义参数,使用方便却也无法再传递.

那么理解了这个之后再回过头来看一些我们常用的技巧时,例如:

['1', '2', '3', '4'].map(&:to_i)

此处的&:to_i 类比为 &p的话,符号&会触发:to_i的to_proc方法,to_proc执行后会返回一个proc实例,然后&会把这个proc实例转换成一个block.

我们需要要明白map方法后挂的是一个block,而不是接收一个proc对象做为参数.

&:to_i是一个block,block不能独立存在,同时你也没有办法直接存储或传递它,必须把block挂在某个方法后面.

## :to_i是一个Symbol实例

class Symbol

def to_proc

Proc.new {|obj| obj.send(self) }

end

end

## 所以 map(&:to_i)也应该是转化为 map{|i| i.to_i }

['1', '2', '3', '4'].map{|i| i.to_i }

## map 方法后面 传入的参数是个block.

做个小结,block和proc是两种不同的东西,block有形无体,proc可以将block实体化,可以把&p看做一种运算,其中&触发p的to_proc方法,然后&会将to_proc方法返回的proc对象转换成block.

lambda

lambda是匿名方法,lambda和proc也是两种不同的东西,但是在ruby中lambda只能依附proc而存在,这点和block不同,block并不依赖proc.

在ruby里创建一个Proc的实例对象有如下几种方法:

Proc.new {} ## #<0x007fb2f39fd0f0>0x007fb2f39fd0f0>

proc {} ## #<0x007fb2f3a02a50>0x007fb2f3a02a50>

lambda {} ## #<0x007fb2f3a00070>0x007fb2f3a00070>

-> {} ## #<0x007fb2f41c9838>0x007fb2f41c9838>

我们惊奇地发现在ruby里要创建一个Proc的方法居然有四种,不管是lambda,还是proc,亦或者是-> 居然都是Proc的实例.但是我们仔细看还是可以看出 Proc.new 和 proc 应该是一样的,区别于 lambda 和 -> (都含有 (lambda)). 所以我们说proc和lambda是不一样的

那么具体哪些地方不一样呢?先来几个经典的例子:

## 首先我们在上面说了 proc来源用& 符号来触发转换成block

def f(p)

instance_eval &p

1

end

p1 = Proc.new {}

p2 = proc {}

l1 = lambda {}

f(p1) # 1

f(p2) # 1

f(l1) # ArgumentError wrong number of arguments (1 for 0)

f(l2) # ArgumentError wrong number of arguments (1 for 0)

proc 和 lambda 第一处不同点是,proc可以用&转化成block,而lambda不行.

p = proc{return 0}

l = lambda {return 0}

p.call ## LocalJumpError: unexpected return

l.call ## 0

#############

def f0()

p = Proc.new {return 0}

p.call

1

end

def f1()

p = proc { return 0 }

p.call

1

end

def f2()

l = lambda { return 0}

l.call

1

end

def f3()

l = -> { return 0 }

l.call

1

end

f0 ## 0

f1 ## 0

f2 ## 1

f2 ## 1

proc 和 lambda 第二处不同点是对return关键字的处理不同.

proc的return只能在方法体里执行,proc单独call会报错,lambda则可以单独执行call.

方法体里的 proc 对return敏感,执行时如果有return则中断跳出方法体,不会再继续往下执行.

而 lambda 的执行更像是调用了某个方法,方法体里return返回后会继续往下执行.

p = proc {|a, b| puts 'proc args'}

l = lambda {|a, b| puts 'lambda args'}

p.call(1, 2) ## proc args

l.call(1, 2) ## lambda args

p.call ## proc args

l.call ## ArgumentError: wrong number of arguments (0 for 2)

proc 和 lambda 第三处不同点是对对参数的检查.proc 不检查参数,lambda检查参数.

小结

做个小结,lambda和proc是Proc的两种不同实例.proc是在方法体的当前上下文处执行一段block代码的.而lambda更像是一个方法,将block作为一个方法,call的时候去调用这个方法.

lambda 检查参数, return时正常返回, proc 不检查参数,return 时中断方法体.lambda更像是一个方法.匿名方法.

linux ruby作用域,浅谈ruby语言中的一些概念(lambda, proc, block)相关推荐

  1. c程序语言的常量变量和标识符,浅谈C语言中的常量与变量.pdf

    课程教育研究 CourseEducationResearch 2014年4月 上旬刊 教学.信息 浅谈C语言中的常量与变量 刘 星 (青 岛工学院商学院 山东 青岛 266300) [摘要]在任何一种 ...

  2. c语言指针很危险,浅谈C语言中指针使用不当的危险性.doc

    浅谈C语言中指针使用不当的危险性.doc 第 19 卷 Vol . 19 第 2 期 No . 2 洛阳师专学报 Journal of Luoyang Teachers College 2000 年 ...

  3. c语言弱符号与函数指针,浅谈C语言中的强符号、弱符号、强引用和弱引用【转】...

    首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引用.在看到3.5.5节弱符号和强符号时,我感觉有些困惑,所以写下此篇,希望能和同样 ...

  4. c语言中很多中括号由外向里,浅谈C语言中的类型声明

    文章目录 [隐藏] 新年第一更!之前群友问了一个 C语言 问题,即int(*(*p)()).int *(*p)()和int *(*p())的区别在哪里.确实,有时C语言的类型声明是很魔性的,看着也很令 ...

  5. c语言如何求一个数学表达式的值,浅谈C语言中表达式的求值

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 C语言研究性学习的路线 现行的多数C语言教材有太多的误区,不仅不能给读者提供有效的学习线索,还常常"误导"读者,于是,"死记 ...

  6. 如何求c语言表达式的值,浅谈C语言中表达式的求值

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 C语言研究性学习的路线 现行的多数C语言教材有太多的误区,不仅不能给读者提供有效的学习线索,还常常"误导"读者,于是,"死记 ...

  7. 计算机语言中的次方,浅谈Go语言中的次方用法

    Go语言中符号 " ^ " 不再用于次方,而是表示"按位异或的运算" 具体的运算规则如下: 按位异或 ^ : 两位一个为 0, 一个为 1 ,结果为 1 ,否则 ...

  8. 浅谈c语言中的字符串

    写在前面:最近MM问了我一个问题--字符串在内存中位于哪里?我想当然是位于数据段(data segment).她又问,那怎么保证它只读呢?我答不上了.有些问题,看似简单,背后却隐藏着天机,后来查了一些 ...

  9. 浅谈C语言中数组理解

    前言:本人为c语言初学者,如果引用理解错误请各位指出!我定会虚心改正,和大家共同前行. 目录 1.初始化数组 2.给数组元素赋值 3.数组边界 4.指定数组的大小 5.多维数组 6.关于二维数组的初始 ...

最新文章

  1. 区块链人才月均薪酬1.6万元?
  2. 如何在Mac上的IntelliJ IDEA中增加IDE内存限制?
  3. 13.3Runtime 类中的主要方法
  4. 操作系统中的进程与线程和java中的线程
  5. 光动能表怎么维护_男士手表什么牌子好,男士手表品牌推荐, 天梭、阿玛尼、西铁城、天王表、罗西尼、卡西欧男手表推荐...
  6. gethours_日期getHours()方法以及JavaScript中的示例
  7. 拓扑检查中的一些问题(为啥没自相交)
  8. Python之进程+线程+协程(同步对象、信号量、队列)
  9. pppoe路由桥混合模式启用_无线路由器怎么设置
  10. 关于matlab的图像显示方法
  11. SSM框架整合及详解
  12. P2P平台公司的9种职位
  13. 如何在桌面上显示我的计算机,Win10如何将我的电脑(此电脑)显示到桌面上?
  14. “ji32k7au4a83”是一个弱密码?
  15. sublime license key
  16. Cesium|xt3d视频融合
  17. 极路由s1有wds_极路由1、1s等机型刷OpenWrt--成为真正的极客
  18. eset找不到服务器更新失败,ESET NOD32连接到服务器以更新常见的错误检测方法
  19. 什么是业务流程管理BPM
  20. 计算机win10搜不到wifi,笔记本win10系统搜不到wifi网络怎么回事|笔记本搜不到wifi网络的解决方法...

热门文章

  1. java 获取apk的包名_java从apk文件里获取包名、版本号
  2. eval() python_如何使用 Python 编写 vim 插件
  3. android porting usb audio,android - 在android中启动时找不到audio-hal-2-0 - 堆栈内存溢出...
  4. 用计算机画 信息技术课标要求,[引用]小学信息技术课程标准
  5. java正式测试数据隔离,开发环境要不要和测试环境隔离?
  6. matlab seed函数_如何用matlab生成随机数函数_matlab随机数生成函数
  7. 基于Python+Django+Mysql的蔬菜水果在线购物商城
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的学生课堂考勤管理系统
  9. 基于JAVA+SpringMVC+Mybatis+MYSQL的企业客户管理系统
  10. oracle 等待sql,oracle sql 锁,锁等待相关sql