李哲 — APRIL 28, 2015

returnbreaknext 这几个关键字的使用都涉及到跳出作用域的问题,而他们的不同 则在于不同的关键字跳出去的目的作用域的不同,因为有代码块则导致有一些地方需要格外注意。

return

常用方式

通常情况下的return语句和大家理解的意思是相同的。

def m1 param
if param == 1
return 'returned 1'
end
'returned default value'#根据Ruby语言规范,最后一条执行语句的结果将作为返回值返回,retu rn是可选的
end m1(1) # => returned 1
m1(2) # => returned default value

在有异常捕获的ensure时,情况会稍有不同:

def m1
'return default'
ensure
puts 'I am sure that it will be here!'
end m1 # => return default

像这种情况,在ensure语句之前,无论是否显示用return来返回,m1方法都会返回ensure之前的值, ensure语句只是确保之后的代码块puts 'I am sure that it will be here!'执行,但是不会从这里返回。 如果在ensure语句中显示的用return来返回值时,情况就不一样了。示例如下:

def m1
return 'return default'
ensure
return 'I am sure that it will be here!'
end m1 # => I am sure that it will be here!

无论在ensure之前是否显示返回,都只会返回ensure之后的值。

在有代码块干预的情况下,又会有所不同:

def m1
p 'start ... '
proc do
p 'block start'
return
p 'block end'
end.call
p 'end ... '
end m1 # 输出结果:
#
# "start ... "
# "block start"

这个应该是在预料之中的,再看下一个:

def m1
p 'start ... '
-> do
p 'block start'
return
p 'block end'
end.call
p 'end ... '
end m1 # 输出结果:
#
# "start ... "
# "block start"
# "end ... "

这里多了一行"end ... ",原因何在?这就是Proc和Lambda最大的区别,在他们之中的return 语句跳出去的目的作用域不同,Proc会直接跳出整个方法的调用,而Lambda只会跳出自身的作用域, 返回到方法中继续执行,这一点需要格外注意。(在break中,Proc和Lambda的跳出方式和return是一样的,后面就不再赘述了。)

break

先来看一个简单的小例子:

result = [1, 2, 3, 4, 5].map do |i|
i * 2
end p result # => [2, 4, 6, 8, 10]

这个没什么奇怪的,那么看看下面这个,来猜猜它的输出结果是什么?

result = [1, 2, 3, 4, 5].map do |i|
break if i > 3
i * 2
end
# FLAG
p result

[1, 2, 3, nil, nil]?还是[1, 2, 3]?还是什么?答案是nil,因为执行break后,直接跳到了FLAG ,也就是跳出了map方法,而map方法中的语句并没有执行完,导致没有任何返回值,为了验证这个想法是正确的,我们 可以利用Ruby语言的break可以带返回值的特性来验证一下:

result = [1, 2, 3, 4, 5].map do |i|
break 'returned break' if i > 3
i * 2
end p result # => "returned break"

这里可以证明我们的猜测是正确的。虽然上面说明了这个问题,但是应该还不是非常容易理解,我们自己定义 一个代码块,再来说明一下:

def m1
p 'start in m1 ... '
m2 do # 代码块
p 'start in block in m1 ... '
p 'end in block in m1 ... '
end
p 'end in m1 ... '
end def m2 &block
p 'start in m2 ... '
block.call
p 'end in m2 ... '
end m1 # 输出结果:
#
# "start in m1 ... "
# "start in m2 ... "
# "start in block in m1 ... "
# "end in block in m1 ... "
# "end in m2 ... "
# "end in m1 ... "

然后我们在m1中的block中添加break,来看看执行结果:

def m1
p 'start in m1 ... '
m2 do # 代码块
p 'start in block in m1 ... '
break
p 'end in block in m1 ... '
end
p 'end in m1 ... '
end def m2 &block
p 'start in m2 ... '
block.call
p 'end in m2 ... '
end m1 # 输出结果:
#
# "start in m1 ... "
# "start in m2 ... "
# "start in block in m1 ... "
# "end in m1 ... "

可以看到代码块的最后一行代码没有执行,m2的最后一行也没有执行,就是因为这一行没有执行,导致 break的第二个例子中的map没有返回任何值。总结一下,代码块中的break会直接跳出调用的方法(m2), 而在声明代码块的方法(m1)中继续执行此方法(m1)中剩下的语句。

next

next关键字类似其他语言中的continue,它的工作方式基本和continue类似。

def m1
p 'start in m1 ... '
m2 do # 代码块
p 'start in block in m1 ... '
next
p 'end in block in m1 ... '
end
p 'end in m1 ... '
end def m2 &block
p 'start in m2 ... '
block.call
p 'end in m2 ... '
end m1 # 输出结果:
#
# "start in m1 ... "
# "start in m2 ... "
# "start in block in m1 ... "
# "end in m2 ... "
# "end in m1 ... "

只是略过了代码块的最后一行代码,这就是next的工作方式了。我们再来看看break的那个例子如果 用next来写,看看结果是什么?如果你完全理解了上面所写的,相信你已经能在大脑中计算出结果了:

result = [1, 2, 3, 4, 5].map do |i|
next if i > 3
i * 2
end p result # => [2, 4, 6, nil, nil]

next语句也能带返回值:

result = [1, 2, 3, 4, 5].map do |i|
next 'next' if i > 3
i * 2
end p result # => [2, 4, 6, "next", "next"]

其他

对于return,在方法中,代码块中都可以使用,而breaknext只能在代码块中使用(循环结构中 也可以使用,但是一般它也是用代码块的形式来表示),如果在方法中调用两者会提示语法错误,也就是:

def m1
return # OK
break # Invalid break, compile error (SyntaxError)
next # Invalid next, compile error (SyntaxError)
end

结论

return 大部分情况下和其他语言无异,需要注意在ensure以及ProcLambda两种不同的 代码块中的细节问题。

break 在有方法嵌套调用中的代码块中需要注意,它总是返回到调用代码块方法的方法中(有点绕)。

next 最老实,基本不需要注意什么。

最后就是,不只是return能返回值,breaknext都能返回值。


本文作者系OneAPM工程师李哲 ,想阅读更多好的技术文章,请访问OneAPM官方技术博客。

Ruby中的语句中断和返回的更多相关文章

  1. ruby中printf "%x"%-4为何会打印开头..

    先看一下ruby中printf "%x" % -4的返回结果: irb(main):134:0> printf "%x\n" % -4 ..fc 前面的. ...

  2. MyBatis-执行插入语句的时候返回主键ID到传入的参数对象中

    <!-- 保存项目信息 --> <insert id="saveItem" parameterType="pd" useGeneratedKe ...

  3. [翻译]理解Ruby中的blocks,Procs和lambda

    原文出处:Understanding Ruby Blocks, Procs and Lambdas blocks,Procs和lambda(在编程领域被称为闭包)是Ruby中很强大的特性,也是最容易引 ...

  4. 理解Ruby中的作用域

    作用域对于Ruby以及其它编程语言都是一个需要理解的至关重要的基础知识.在我刚开始学习ruby的时候遇到很多诸如变量未定义.变量没有正确赋值之类的问题,归根结底是因为自己对于ruby作用域的了解不够, ...

  5. Ruby中Block, Proc, 和Lambda

    Block Blocks就是存放一些可以被执行的代码的块,通常用do...end 或者 {}表示 例如: [1, 2, 3].each do |num| puts num end [1, 2, 3]. ...

  6. 向linux内核中添加外部中断驱动模块

    本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内 ...

  7. ruby中symbol

    Symbol 是什么 Ruby 是一个强大的面向对象脚本语言(本文所用 Ruby 版本为1.8.6),在 Ruby 中 Symbol 表示“名字”,比如字符串的名字,标识符的名字. 创建一个 Symb ...

  8. ruby中的可调用对象--proc和lamdba

    ruby中将块转变成对象的三种方法 ruby中的大部分东西都是对象,但是块不是.那么,如果你想存下来一个块,方便以后使用,你就需要一个对象.ruby中有三种方法,把块转换成可以利用的对象. Proc. ...

  9. ruby中的链式访问和方法嵌套

    先看一道题,这道题是codewars上的一道题,我很早就看到了,但是不会写.等到又看到这道题的时候,我刚看完元编程那本书,觉得是可以搞定它的时候了.废话不多说,先看这道题,题目最开始是为JavaScr ...

随机推荐

  1. wpf 在引用外部的资源字典

    启动的APP.xaml

  2. ORACLE-用户常用数据字典的查询使用方法

    一.用户 查看当前用户的缺省表空间 SQL> select username,default_tablespace from user_users; USERNAME DEFAULT_TABLE ...

  3. hdu 1212 Big Number

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1212 Big Number Description As we know, Big Number is ...

  4. linux新增一块硬盘加入原有分区

    原有硬盘空间已经不足,添加一块新硬盘,并且加入到原根目录下 查看新硬盘 1 2 fdisk -l Disk /dev/sdb: 240.1 GB, 240057409536 bytes 在新硬盘上创建 ...

  5. Libevent windows/linux下编译

    1.windows下: 编译环境: windows xp sp3 + vs2010 (1)    解压libevent-2.0.21-stable.tar.gz到D:\libevent-2.0.21- ...

  6. Linux下Nginx的安装与配置

    安装前需要安装pcre:ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/1.解压缩:     tar xjpf pcre-7.8.tar.b ...

  7. ASP.NET MVC中使用事务写法

    一些项目中,会涉及到事务的写法,比如订单相关,订单成功,会涉及到产品的库存和账户金额的一些信息变动,当然,如果整个流程成功,那是没什么问题,关键是如果中间某一步骤出现bug了,那之前已执行的一些变动就 ...

  8. [转]gdb结合coredump定位崩溃进程

    [转]gdb结合coredump定位崩溃进程 http://blog.sina.com.cn/s/blog_54f82cc201013tk4.html Linux环境下经常遇到某个进程挂掉而找不到原因 ...

  9. [笔记]一个测试浏览器对html5支持的网站

    用需要测试的浏览器打开这个地址:http://html5test.com/

  10. SVN基于一个branch创建新branch

    在本地现有Branch(Checkout出来的目录)上,右键SVN,选择Branch/Tags,选择目录.比如https://sadcsc01.pmmr.com/web/Jaguar/branches ...