在Ruby中显式返回的好样式吗?


155

来自Python背景,在样式方面总是有“正确的方法”(“ Pythonic”的方法),我想知道Ruby是否也存在这种情况。我一直在使用自己的样式准则,但我正在考虑发布源代码,我希望它遵守可能存在的任何未编写规则。

明确输入return方法是“ Ruby之道” 吗?我见过有和没有,它都有,但是有正确的方法吗?也许有合适的时机吗?例如:

def some_func(arg1, arg2, etc)
  # Do some stuff...
  return value # <-- Is the 'return' needed here?
end

3
这是一个非常古老的问题,但是对于那些有类似问题的人,强烈建议使用Eloquent Ruby。它不是样式指南,而只是编写惯用Ruby的介绍。希望更多的人会阅读!
布莱恩·迪尔

作为继承了使用Ruby开发的大型遗留应用程序的人,我非常感谢您提出这个问题。这值得赞扬。这在我们的旧版应用程序中都是乱七八糟的。我在该团队中的工作之一就是改善代码库(在Ruby刚接触时很难)。
Stevers

Answers:


226

旧的(也是“已回答”的)问题,但是我会花两分钱作为答案。

TL; DR-不必这样做,但是在某些情况下,它可使您的代码更加清晰。

尽管不使用显式返回可能是“ Ruby方式”,但它使使用不熟悉的代码或不熟悉Ruby的功能的程序员感到困惑。

这是一个有些人为的示例,但是想象一下有一个像这样的小功能,该功能在传递的数字上加一个,然后将其分配给实例变量。

def plus_one_to_y(x)
    @y = x + 1
end

这是否意味着要返回一个值的函数?很难说出开发人员的含义,因为它既分配了实例变量,又返回了分配的值。

假设很久以后,另一个程序员(也许不太熟悉Ruby如何根据所执行的最后一行代码返回结果)就想输入一些print语句进行记录,该函数就变成了……

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
end

现在,如果有任何期望返回值的函数,该函数将被破坏。如果没有什么期望返回值,那很好。显然,如果在代码链中更远的某个地方,调用此函数期望返回值,那么它将失败,因为它没有恢复到期望的值。

现在真正的问题是:真的有什么期望返回值吗?这有什么坏处吗?将来会不会破坏某些东西?谁知道!只有对所有调用的完整代码审查才能让您知道。

因此,至少对我而言,最佳实践方法是非常明确地指出,如果有重要事项,您将返回某项内容;或者在没有必要的情况下完全不返回任何内容。

因此,在我们的小演示功能的情况下,假设我们希望它返回一个值,则将这样编写...

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
    return @y
end

对任何程序员来说,它确实会返回一个值将是非常清楚的,并且对于他们来说,在不意识到这一点的情况下很难破解它。

或者,可以这样写,而忽略return语句。

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
    @y
end

但是,为什么不理会“回报”一词呢?为什么不把它放在那里并让它100%清楚发生了什么呢?它实际上不会对您的代码执行能力产生任何影响。


6
有趣的一点。当我问这个问题时,我想我实际上也遇到了类似的情况,原始开发人员在他们的代码中使用了隐式返回值,这很难理解发生了什么。感谢您的回答!
Sasha Chedygov

6
我在对隐式收益进行谷歌搜索时发现了这个问题,因为我刚刚为此感到疲倦。我在隐式返回某些函数的末尾添加了一些逻辑。对它的大多数调用都不在乎(或检查)返回的值,但是一个调用却在执行-失败。幸运的是,至少在某些功能测试中它才出现。
蒂姆·霍尔特

16
尽管确实如此,但对于代码而言,可读性很重要,在我看来,避免使用编程语言的众所周知的功能来支持冗长是一种不好的做法。如果开发人员不熟悉Ruby,那么也许他们应该在接触代码之前先熟悉一下。我发现为将来某些非Ruby开发人员不得不做一些事情而不得不增加代码的混乱程度,这有点愚蠢。我们不应该将语言简化为最小公分母。红宝石的美的部分原因是,我们没有使用5行代码说些应该只需要3
布赖恩·迪尔

25
如果单词return为代码增加了语义,为什么不呢?这几乎不是很冗长-我们不是在说5行vs 3行,只是一个字。我更愿意return至少在函数中看到(也许不在块中),以表达代码的实际作用。有时,您必须返回中间功能-不要return仅仅因为可以就在最后一行保留最后一个关键字。我一点也不赞成冗长,但我赞成一致性。
mindplay.dk

6
这是一个很好的答案。但这仍然冒着您编写打算作为过程(即函数返回单元)的Ruby方法的风险,例如side_effect(),另一个开发人员决定(ab)使用过程的隐式返回值。要解决此问题,您可以使过程明确return nil
亚历克斯·迪恩

66

不会。好的Ruby样式通常只使用显式的return作为早期return。Ruby在代码最小化/隐式魔术方面大有作为。

就是说,如果明确的回报将使事情更清晰或更容易阅读,则不会有任何损害。


116
如果代码极简主义导致混乱的极简主义,那有什么意义呢?
jononomo 2014年

@JonCrowell它应该导致文档最大化。
jazzpi 2015年

29
@jazzpi如果您需要大量编写代码以使其被很好地理解,那么您不是在练习极简主义,而是在练习代码golf
Schwern

2
排除最终的返回结果并不令人困惑,将其包含在内也令人困惑-作为Ruby开发人员,return作为函数的早期出口,它已经具有重要的语义含义。
德文·帕森斯

14
@DevonParsons无论如何,人们到底如何return将最后一行的a混淆为早期退出?
DarthPaghius

31

我个人使用return关键字来区分所谓的功能方法(即主要针对其返回值执行的方法)和主要针对其副作用执行的过程方法。因此,对于返回值很重要的方法,可以使用一个额外的return关键字来引起人们对返回值的注意。

调用方法时,我使用相同的区别:函数方法带有括号,而过程方法则没有。

最后但并非最不重要的一点是,我还对块使用了这种区别:功能块获取花括号,过程块(即“执行”操作的块)获取do/ end

但是,我尽量不要对此抱有虔诚的态度:使用块,花括号和do/ end具有不同的优先级,而不是添加显式括号来消除表达式的歧义,我只是改用其他样式。方法调用也是如此:如果在参数列表周围添加括号使代码更具可读性,即使有问题的方法本质上是程序性的,我也会这样做。


1
现在,我忽略了任何“单线”收益,这与您在做的事情相似,而我做的其他所有事情都一样。很高兴知道我并不完全符合我的风格。:)
Sasha Chedygov

@Jorg:您是否在代码中使用早期收益?这会与您的程序/功能方案造成混淆吗?
安德鲁·格林

1
@安德鲁·格林(Andrew Grimm):是的,是的。我正在考虑将其反转。我的绝大多数方法都是引用透明的/纯函数的/功能性的/无论您想如何调用它,因此在此处使用较短的形式是有意义的。以函数形式编写的方法往往没有尽早的回报,因为这暗示了“阶跃”的概念,它不是很实用。虽然,我不喜欢中间的回报。我要么一开始就使用它们作为警卫人员,要么一开始就使用它们以简化控制流程。
约尔格W¯¯米塔格

很好的答案,但实际上最好的做法是使用()和函数方法来调用过程方法而无需-参见我在下面的答案以了解为什么
Alex Dean

13

实际上,重要的是要区分:

  1. 函数-为返回值执行的方法
  2. 程序-因副作用而执行的方法

Ruby没有区分这些的本机方式-这使您容易编写过程,side_effect()而另一个开发人员决定滥用过程的隐式返回值(基本上将其视为不纯函数)。

要解决此问题,请从Scala和Haskell的书中抽出叶子,并让您的过程明确返回nil(aka Unit()使用其他语言)。

如果遵循此规则,那么使用显式return语法还是不只是个人风格问题。

要进一步区分功能和过程:

  1. 复制JörgW Mittag的好主意,即用大括号编写功能块,并使用 do/end
  2. 调用过程时,请使用(),而调用函数时,请不要

需要注意的是约尔格W¯¯米塔格实际上主张的其他方式-避免()S表示程序-而是因为你想一边实现方法调用从变量清晰可辨,尤其是当元数为0见的,这不是最好的方法调用Scala的风格指南为细节。


如果我需要从返回的值function_a,并且function_a已将其写入调用(并且只能通过调用进行操作)procedure_b,那么这function_a真的是一个函数吗?它返回一个值,满足您对功能的要求,但有副作用,满足过程的要求。
德文·帕森斯

2
您说得对,德文郡(Devon)- function_a可能包含procedure_...调用树,而实际上procedure_a可能包含function_...调用树。像Scala一样,但与Haskell不同,在Ruby中不能保证函数不会产生副作用。
Alex Dean

8

样式指南指出,您不应return在最后一条语句上使用。如果不是最后一个,您仍然可以使用它。这是社区严格遵守的约定之一,如果您打算与使用Ruby的任何人进行协作,那么也应该这样做。


话虽这么说,使用explicit returns 的主要论据是它使来自其他语言的人感到困惑。

  • 首先,这并不完全是Ruby独有的。例如,Perl也具有隐式返回。
  • 第二,适用于此的大多数人来自Algol语言。其中大多数都是比Ruby “低级”的方式,因此您必须编写更多代码才能完成某些工作。

在Java中,方法长度(不包括getters / setters)的一种常见启发式方法是一个屏幕。在这种情况下,您可能看不到方法定义和/或已经忘记了从哪里返回。

另一方面,在Ruby中,最好坚持使用少于10行的方法。鉴于此,人们会想知道为什么在明显暗示的情况下他必须多写10%的陈述。


由于Ruby没有void方法,并且所有内容都更加简洁,因此,如果您使用显式returns ,则只会增加开销,而没有任何好处。


1
“这是社区严格遵守的惯例之一。” 我的经验是,不,除非他们是非常有经验的Ruby开发人员,否则他们不会严格遵守此惯例
蒂姆·霍尔特

1
@TimHolt那时我一定很幸运。(:
ndnenkov '16

1
@ndn完全同意。重要的是,当一个方法(或类)开始超过5/100行时,最好重新考虑您要完成的工作。确实,Sandi的规则是用于思考代码而不是任意教条的认知框架。我遇到的大多数代码都没有问题,因为它是人为约束的-通常是相反的-具有多个职责的类,方法比许多类长。从本质上讲,太多的人“跳高了鲨鱼”-混合责任。
布莱恩·迪尔

1
有时约定仍然不是一个好主意。它依赖魔术的风格惯例,使返回类型难以猜测。除了保存7次击键外,它没有其他优势,并且几乎与所有其他语言都不一致。如果Ruby曾经进行过Python 3样式的合并和简化,我希望隐式的东西是斩波块上的第一个。
Shayne

2
除非明确指出,它实际上并没有使事情更容易阅读!魔术和可理解性不是目标。
Shayne

4

我同意Ben Hughes的观点,不同意Tim Holt的观点,因为这个问题提到了Python的确定方式,并询问Ruby是否具有类似的标准。

是的

这是该语言的一个众所周知的功能,以至于任何希望调试红宝石问题的人都应该合理地知道这一点。


3
我在理论上同意您的观点,但是在实践中,程序员会犯错误并搞砸了。举个例子,我们都知道您在条件语句中使用了“ ==”而不是“ =”,但是我们所有人都曾经犯过该错误,直到后来才意识到。
蒂姆·霍尔特

1
@TimHolt我没有解决任何特定的用例,我只是在回答问题。正如社区所选择的那样,return在不必要的时候使用关键字显然是不好的风格。
德文·帕森斯

3
很公平。我说的是,如果您不小心,您提到的样式可能会导致错误。根据我的经验,我个人提倡另一种风格。
蒂姆·霍尔特

1
@TimHolt顺便说一句,在编写样式指南之前就已经问过这个问题,因此不同意的答案不一定是错误的,只是过时了。我还是偶然发现了这里,却没有看到我提供的链接,所以我发现其他人也可能会在这里结束。
德文·帕森斯

1
它不是官方的“标准”。rubocop使用的ruby样式指南与Devon Parsons的声明在上面链接,是由非核心的ruby黑客创建的。因此,德文郡的评论实际上是错误的。当然,您仍然可以使用它,但这不是标准。
shevy

1

大多数情况下,这取决于您喜欢哪种样式。如果要从方法中间的某处返回,则必须使用关键字return。


0

就像本说的。“ ruby​​方法的返回值是函数主体中最后一条语句的返回值”这一事实导致return关键字在大多数ruby方法中很少使用。

def some_func_which_returns_a_list( x, y, z)
  return nil if failed_some_early_check


  # function code 

  @list     # returns the list
end

4
如果您的意图仍然清楚,您还可以写“如果失败则返回零”
Mike Woodhouse,2009年

@Gishu我实际上在抱怨if语句,而不是方法名称。
安德鲁·格林

@安德鲁..哦,失踪的结局:)。得到它了。修正了平息Mike的其他评论的问题。
Gishu 2011年

为什么不if failed_check then nil else @list end呢?那是真正的功能
cdunn2001

@ cdunn2001尚不清楚为什么还有其他功能。.这种样式的原因是它通过早期退出减少了嵌套。恕我直言,也更具可读性。
Gishu 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.