分享 透彻理解 Ruby 中的 return

mingyuan0715 · 发布于 2015年3月22日 · 最后由 arth 回复于 2015年9月05日 · 6699 次阅读
2329
本帖已被设为精华帖!

在ruby中,有三个关键字可以从一段代码中返回,分别是returnnextbreak,今天主要研究一下return。

方法中的return

在ruby中调用一个方法,默认的情况下,会一行一行依次执行方法中的代码,方法的值是最后一行的值。

return可以改变这个方法一行一行执行的行为,当遇到return,方法会直接返回(rescue、ensure例外)。

def test
  puts 'first puts'
  return
  puts 'next puts'
end

在上面这个例子中,puts 'next puts'这行代码永远不会执行。同时我们可以显示指定返回值,默认为nil。例如上面这个例子没有指定返回值,其返回值则为nil。

proc中的return

代码胜千言,先看几个例子:

def test1
  proc = Proc.new { return 10 }
  puts 'puts here'
end

def test2
  proc = Proc.new { return 10 }
  proc.call
  puts 'puts here'
end

def test3
  return 10
  puts 'puts here'
end

test4 = Proc.new { return 10 }
test4.call

在上面的例子中:

  • test1方法,puts 'puts here'能够执行,返回值为最后一行代码的返回值。

  • test2方法,返回值为10。

  • test3方法的行为和test2是等价的。

  • test4在call调用的时候会报错,错误信息是:LocalJumpError: unexpected return;

为什么会这样,其实test2test3已经告诉我们答案了,在proc中使用 return 和在方法本身中使用return的效果是一样的。

test4的例子就相当于你直接执行return 10,报的错误也同样是:LocalJumpError: unexpected return

LocalJumpError这个错误其实很有意思,如果拦截这个错误,是依然可以拿到return的返回值的。 下面是我在pry中做的实验。

return 10
#=> LocalJumpError: unexpected return

_ex_
#=> <LocalJumpError: unexpected return>

_ex_.class
#=> LocalJumpError < StandardError

_ex_.exit_value
#=> 10

_ex_.reason
#=> :return

lambda 中的return

还是继续看例子:

test1 = -> { puts 'first puts'; return 10; puts 'next puts' }
test1.call

# first puts
#=> 10

def test2
  la = -> { puts 'first puts'; return 10; puts 'next puts' }
  la.call
  puts 'puts here'
end

# first puts
# puts here
#=> nil

lambda对象,跟proc对象不一样,在lambda中,return是从lambda代码块中返回。在proc中,return是从proc代码块所在的上下文环境中返回。

共收到 30 条回复
12128
48hour · #2 · 2015年3月23日

支持, Proc和Lambda这个细节以前没太注意

12224
Guest · #3 · 2015年3月23日

good job :plus1:

8596
huopo125 · #4 · 2015年3月23日

👍 学习了,pry中_ex_

332
vincent · #5 · 2015年3月23日

:plus1: proc 和 lambda 中 return 的不同是很容易让人迷惑的。

2564
kikyous · #6 · 2015年3月23日

ruby元编程 里面有详细的说明

15307
angelfan · #7 · 2015年3月23日

我之前看proc和lambda的区别没有特别明白,这样来解释感觉好容易明白

10316
chiangdi · #8 · 2015年3月23日

简单来说,基本上不用 proc,都是用 lambda 的。

96
jex · #9 · 2015年3月23日

。。。。你只要说Ruby中block不是function/lambda就行了,哪来这么多混乱。在其它语言中return都是退出function/lambda,只是Ruby多出了block,它用的是花括号,像lambda但又不是,才产生了混淆

96
cicholgricenchos · #10 · 2015年3月23日

#8楼 @chiangdi 然而每次用each的时候都在用proc : -)

10316
chiangdi · #11 · 2015年3月23日

#10楼 @cicholgricenchos 刚去试了下好像 each 表现的和 proc 一样,直接从代码块的所在的上下文环境中返回了,还真是有点小惊讶。。但是把 代码块变成 Proc 对象时大家确实应该是习惯用 lambda 的。

1638
xiaoronglv · #12 · 2015年3月23日

:plus1:

8043
teddy_1004 · #14 · 2015年3月24日 1 个赞

赞,写的简单易懂,又复习了一遍

15楼 已删除
14358
est · #16 · 2015年3月24日 1 个赞

有三个关键字可以从一段代码中返回,分别是return、next、break

还有一个 yield,甚至直接抛异常,如果你喜欢的话。

14837
mobile · #17 · 2015年3月25日
def double(callable_object)
  callable_object.call * 2
end
puts double( lambda { return 10 } )           
def another_double
  p = Proc.new { return 10 }
  result = p.call
  return result * 2 
end
puts another_double

大家考虑一下这个返回的是什么?分享一下思路。

12232
kenyonduan · #18 · 2015年3月25日 1 个赞

Ruby 元编程这本书里有讲这个哦,还有一个就是 proc 和 lambda 对于传入的参数的处理不太一样。 proc 会帮我们处理一下参数,lambda 不会

proc:
proc = Proc.new {|a,  b| a * b}
proc.call(1, 2, 3)
# => 2

lambda:
ld = lambda {|a,  b| a * b}
ld.call(1, 2, 3)
# => ArgumentError: wrong number of arguments (3 for 2)
2575
darkbaby123 · #19 · 2015年3月25日 1 个赞

虽然自己写代码时大多数会使用 lambda,但 Ruby 内建的方法(比如迭代器)都是用的 proc 。因为:

  1. proc 中 return 直接作用于外层的 scope 。这点在迭代器中很常用。
  2. proc 参数验证没 lambda 那么严格,这点也在迭代器中很常用。

目前貌似没发现 Ruby 内建方法使用 lambda 的情况。

一个有意思的地方是,因为上面的原因, proc 被隐蔽地用在很多地方。如果有时候需要像 lambda 那样 return 的时候,可以用 next 代替。比如:

user.save.tap do |success|
  next unless success

  send_notification
  write_log
end
14358
est · #20 · 2015年3月27日

#19楼 @darkbaby123 呃。。。这个 next 是 Enumerator特有的吧。。。。

554
zcq100 · #21 · 2015年3月28日

巩固基础知识

11314
Zoker · #22 · 2015年3月30日

还是要再看一遍元编程呐。

2575
darkbaby123 · #23 · 2015年3月31日

@est 实际证明对非 enumerator 的 block 也有效果。

14358
est · #24 · 2015年3月31日

#23楼 @darkbaby123 求事实学习下。。。

96
yct21 · #25 · 2015年3月31日 1 个赞

#24楼 @est

def some_function()
  yield 2
end

some_function() {|y| next y*y} # => 4

其实就是退出当前 block.

2575
darkbaby123 · #26 · 2015年3月31日 1 个赞

@est 上面的帖子就是个例子,我是用在 tap 里面。目的嘛:

proc 被隐蔽地用在很多地方。如果有时候需要像 lambda 那样 return 的时候,可以用 next 代替。

96
cholerae · #27 · 2015年4月05日

正在学习ruby,mark一下

3583
zeayes · #28 · 2015年4月22日 1 个赞

proc是代码片段(snippet),它里面的return就相当于结束调用它的context的执行。 lambda是匿名方法(method),它里面的return就是结束该method(自己)的执行。

96
sweetycode · #29 · 2015年6月19日

#28楼 @zeayes 一语中的

19891
jackxu · #30 · 2015年7月21日 1 个赞

说白了也是block与lambda的区别,lambda本质上是函数,所以遇到return时会从lambda函数内返回,而block是一段能执行的代码,相当于你在函数中插入一段断码,当block中包含return语句时,导致整个函数就返回了

16207
arth · #31 · 2015年9月05日

直白明了!谢谢作者

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册