Rubyで明示的に返すのは良いスタイルですか?


155

スタイルに関しては「正しい方法」(「Python」の方法)が常に存在するPythonの背景から来ているので、Rubyにも同じことがあるのではないかと思っています。私は独自のスタイルガイドラインを使用してきましたが、ソースコードをリリースすることを考えており、存在する可能性のある未作成のルールに準拠したいと考えています。

returnメソッドに明示的に入力するのは「Rubyウェイ」ですか?私はそれがある場合とない場合でそれを見たことがありますが、それを行う正しい方法はありますか?たぶんそれを行う適切な時期はありますか?例えば:

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

3
これは非常に古い質問ですが、同様の質問がある人には、Eloquent Rubyという本を強くお勧めします。これは、慣用的なRubyを作成するための入門と同じくらい、スタイルガイドではありません。もっと多くの人に読んでもらいたい!
Brian Dear

Rubyで開発された大規模なレガシーアプリを継承した人物として、この質問をしていただきありがとうございます。これは賛成に値します。これは、従来のアプリ全体に散らばっています。このチームでの私の仕事の1つは、コードベースを改善することです(Rubyが初めての場合は難しい)。
Stevers 2018年

回答:


226

古い(そして「答えられた」)質問ですが、答えとして2セントで投げます。

TL; DR-必須ではありませんが、場合によってはコードをより明確にすることができます。

明示的な戻り値を使用しないことは「Rubyの方法」かもしれませんが、見慣れないコードで作業しているプログラマーや、Rubyのこの機能に慣れていないプログラマーを混乱させます。

これはやや工夫された例ですが、このような小さな関数があり、渡された数値に1を加えてインスタンス変数に割り当てる関数を想像してください。

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

これは、値を返す関数か、そうでないか?どちらもインスタンス変数を割り当て、かつ割り当てられた値も返すため、開発者が何を意味しているかを言うのは本当に難しいです。

ずっと後に、別のプログラマー(おそらく、実行されたコードの最後の行に基づいてRubyがどのように戻るかになじみがないかもしれません)がやって来て、ロギングのためにいくつかの印刷ステートメントを入れたいと思います、そして関数はこれになります...

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
興味深い点。この質問をしたとき、私は実際に同じような状況に遭遇したと思います。元の開発者がコードで暗黙の戻り値を使用し、何が起こっているのかを理解するのが非常に困難でした。ご回答有難うございます!
サーシャチェディゴフ2012年

6
私はこれに焦げたばかりだったので、暗黙のリターンでグーグルしながら質問を見つけました。暗黙的に何かを返す関数の最後にロジックを追加しました。ほとんどの呼び出しは戻り値を気にしません(またはチェックしません)が、1つはそうしました-そしてそれは失敗しました。幸いなことに、少なくともいくつかの機能テストではそれが現れました。
Tim Holt、

16
真実ではありますが、コードが可読であることが重要であり、冗長性を優先してプログラミング言語のよく知られている機能を避けることは、私の意見では悪い習慣です。開発者がRubyに慣れていない場合は、コードに触れる前に慣れる必要があります。Ruby以外の開発者が後で何かをしなければならない将来のイベントのためだけにコードの乱雑さを増やさなければならないのは少しばかげています。言語を最低限の共通点に減らすべきではありません。ルビーの美しさの一部は、我々は唯一の3を取るべきである何かを言うために、コードの5行を使用する必要がないことです
ブライアン・ディア

25
単語returnがコードに意味的な意味を追加するのであれば、なぜですか?それはほとんど冗長ではありません-5行と3行ではなく、あと1語です。returnコードが実際に何をしているのかを表現するために、少なくとも関数内(おそらくブロック内ではない)を見たいと思います。場合によっては、関数の途中で戻る必要があります- returnできるからといって、最後の行の最後のキーワードを省略しないでください。私は冗長性をまったく好みませんが、一貫性を優先します。
mindplay.dk 2013年

6
これは素晴らしい答えです。しかし、プロシージャ(別名関数を返すユニット)を意図するRubyメソッドを作成するリスクは依然として残ります。たとえばside_effect()、別の開発者がプロ​​シージャの暗黙的な戻り値を(ab)使用することを決定します。これを修正するには、プロシージャを明示的に作成しますreturn nil
Alex Dean

66

いいえ。Rubyスタイルが適切な場合、通常、早期のリターンには明示的なリターンのみを使用します。Rubyはコードのミニマリズム/暗黙の魔法に大きな力を持っています。

とはいえ、明示的な復帰によって物事がより明確になり、読みやすくなっても、何も害はありません。


116
それが混乱の最大化をもたらすなら、コードの最小化のポイントは何ですか?
jononomo 14

@JonCrowell代わりに、ドキュメントの最大化をもたらすはずです。
jazzpi

29
@jazzpiミニマリズムを実践していないことを理解するためにコードを詳細に文書化する必要がある場合は、コードゴルフを実践しています
シュヴェルン2015

2
最終的なリターンを除外することは紛らわしくありません、それを含めることは紛らわしいです-Ruby開発者returnとして、関数へのEARLY EXITとしてすでに重要な意味論的意味を持っています。
デボンパーソンズ

14
@DevonParsons returnとにかく、誰かが最後の行のを初期の出口として混乱させることができるでしょうか?
DarthPaghius 2017

31

私は個人的にreturnキーワードを使用して、関数メソッドと呼ばれるもの、つまり主に戻り値に対して実行されるメソッドと、主に副作用のために実行される手続き型メソッドを区別しています。したがって、戻り値が重要なメソッドは、戻り値にreturn注意を引くために追加のキーワードを取得します。

私はメソッドを呼び出すときに同じ区別を使用します。関数メソッドは括弧を取得しますが、手続き型メソッドは括弧を取得しません。

そして最後に重要なことですが、ブロックでもこの区別を使用します。機能ブロックは中括弧を取得し、手続き型ブロック(つまり、「何かを行う」ブロック)はdo/を取得しendます。

ただし、私はそれについて信心を持たないようにしています。ブロック、中括弧、およびdo/ endは優先順位が異なり、式を明確にするために明示的な括弧を追加するのではなく、他のスタイルに切り替えます。同じことがメソッドの呼び出しにも当てはまります。パラメーターリストの周りにかっこを追加すると、コードが読みやすくなります。問題のメソッドが本質的に手続き型であっても、コードを読みやすくします。


1
現在、私は「ワンライナー」でのリターンを省略しています。これは、あなたがやっていることに似ています。他のことはすべて同じように行います。私は自分のスタイルに完全に満足しているわけではありません。:)
サーシャチェディゴフ2009年

@Jorg:コードで早期リターンを使用していますか?それはあなたの手続き/機能スキームに混乱を引き起こしますか?
Andrew Grimm、

1
@Andrew Grimm:はい、そうです。私はそれを反転させることを考えています。私のメソッドの大部分は参照透過的/純粋/機能的/とにかく呼び出したいものなので、そこではより短い形式を使用するのが理にかなっています。また、関数形式で記述されたメソッドは、「ステップ」の概念を意味するため、早期には返らない傾向がありますが、これはあまり機能的ではありません。でも途中での返品は嫌いです。制御フローを簡略化するために、最初はガードとして、または最後にそれらを使用します。
イェルクWミッターク

すばらしい答えですが、実際には()付きの手続き型メソッドとなしの関数型メソッドを呼び出す方が適切です。理由については、以下の私の答えを参照してください
Alex Dean

13

実際に重要なことは、以下を区別することです。

  1. 関数-戻り値に対して実行されるメソッド
  2. プロシージャ-副作用のために実行されるメソッド

Rubyにはこれらを区別するネイティブの方法がないため、プロシージャの記述に対して脆弱でside_effect()あり、別の開発者がプロシージャの暗黙的な戻り値を悪用することを決定します(基本的には純粋でない関数として扱います)。

これを解決するには、ScalaとHaskellの本から1枚の葉を取り、プロシージャを明示的にnil(別名Unitまたは()他の言語で)返すようにします。

これに従えば、明示的なreturn構文を使用するかどうかは、個人的なスタイルの問題になります。

関数とプロシージャをさらに区別するには:

  1. JörgW Mittagの、中括弧で機能ブロックを書き、手続きブロックを do/end
  2. プロシージャを呼び出すときはを使用し()、関数を呼び出すときは使用しないでください

回避-イェルクWミッタークは、実際に他の方法で回避提唱ことを注意()の手続きのために-しかし、あなたはサイドもたらすメソッド呼び出しはアリティは0を参照してくださいある場合は特に、変数とは明確に区別できるようにしたいので、それは賢明ではありませんメソッド呼び出しのScalaのスタイルガイドについて詳細。


私はからの戻り値が必要な場合はfunction_a、およびfunction_a呼び出すために書かれていた(と呼び出すことによってのみ動作することができます)procedure_b、その後でfunction_a本当に機能?関数の要件を満たす値を返しますが、プロシージャの要件を満たす副作用があります。
デボンパーソンズ

2
-あなたはデボン正しいfunction_aの木含めることができますprocedure_...実際として、通話をprocedure_aする木含まれている可能性がfunction_...呼び出しを。Scalaと同様ですが、Haskellとは異なり、Rubyでは関数が副作用を起こさないという保証はありません。
Alex Dean

8

スタイルガイドには、return最後のステートメントでは使用しないように記載されています。それが最後のものでない場合でも、それを使用できます。これはコミュニティが厳守している規約の1つであり、Rubyを使用している人と共同作業を計画している場合はそうするべきです。


そうは言っても、明示的なreturns を使用することの主な議論は、他の言語から来た人々を混乱させるということです。

  • まず、これは完全にRubyに限定されるわけではありません。たとえば、Perlには暗黙の戻り値もあります。
  • 第二に、これが当てはまる人々の大部分はアルゴル語から来ています。それらのほとんどはRubyよりも「低レベル」であるため、何かを実行するには、より多くのコードを記述する必要があります。

Javaのメソッド長(ゲッター/セッターを除く)の一般的なヒューリスティックは、1つの画面です。その場合、メソッド定義が表示されていないか、元の場所を忘れている可能性があります。

一方、Rubyでは10行未満のメソッドを使用するのが最善です。それを考えると、明らかに暗示されているのに、なぜ彼が〜10%多いステートメントを書かなければならないのか不思議に思うでしょう。


Rubyにはvoidメソッドがなく、すべてがはるかに簡潔であるため、明示的なreturnsを使用する場合は、利点がないためオーバーヘッドが追加されるだけです。


1
「これはコミュニティーが厳密に守っている慣習の1つです」 私の経験では、非常に経験豊富なRuby開発者でない限り、人々はこれを厳密に守っていません。
Tim Holt

1
@TimHolt私はその時とても幸運だったに違いない。(:
ndnenkov

1
@ndnは完全に同意します。重要なことは、メソッド(またはクラス)が5/100行を超え始めたら、達成しようとしていることを再検討することをお勧めします。本当にサンディのルールは、恣意的な教義ではなく、コードについて考えるための認知的フレームワークです。私が遭遇するコードの大部分は、それが人工的に制約されているため、問題ではありません-それは一般的に反対です-複数の責任を持つクラス、多くのクラスより長いメソッド。基本的に、あまりに多くの人々が「サメに飛び込む」-責任の混合。
Brian Dear

1
慣習はまだ悪い考えです。マジックに依存するそのスタイルの規約により、戻り値の型を推測することが困難になります。7つのキーストロークを保存する以外に利点はなく、他のほとんどすべての言語に適合しません。RubyがPython 3スタイルの統合と簡略化のパスを行う場合、暗黙の何かがチョッピングブロックの最初であることを願っています。
Shayne

2
明らかにそれが実際に物事を読みやすくするわけではありません!魔法とわかりやすさは同じものではありません。
Shayne

4

私はベン・ヒューズに同意し、ティム・ホルトには同意しません。なぜなら、この質問はPythonが決定的な方法を述べ、Rubyに同様の標準があるかどうかを尋ねるからです。

します。

これは言語の非常によく知られた機能であり、ルビーの問題をデバッグすることを期待されている人はだれでもそれについて知っていることが期待されるはずです。


3
私は理論的にはあなたに同意しますが、実際には、プログラマーは間違いを犯し、失敗します。例として、「==」ではなく「==」を条件付きで使用することは誰もが知っていますが、私たち全員が以前にその間違いを犯し、後でないと気づきませんでした。
Tim Holt、

1
@TimHolt特定のユースケースに対応するのではなく、質問に答えるだけです。コミュニティが選択したように、return不要なときにキーワードを使用するのは明らかに悪いスタイルです。
デボンパーソンズ

3
けっこうだ。あなたが言及するスタイルは、あなたが注意しなければミスを引き起こす可能性があると言っています。私の経験に基づいて、私は個人的に別のスタイルを提唱しています。
Tim Holt

1
@TimHoltちなみに、この質問はスタイルガイドが書かれる前に尋ねられたので、同意しない答えは必ずしも間違っているわけではなく、時代遅れです。私はどういうわけかここにつまずいた、そして私が提供したリンクを見なかったので、私は他の誰かがまたここに行くかもしれないと思った。
デボンパーソンズ

1
公式の「規格」ではありません。rubocopによって使用され、デボンパーソンズによるクレームにリンクされたルビスタイルガイドは、非コアのルビーハッカーによって作成されました。したがって、デボンによるコメントは事実上単に間違っています。もちろん、まだ使用できますが、標準ではありません。
シェビー

1

ほとんどの場合、どちらのスタイルを選ぶかが問題になります。メソッドの途中から戻る場合は、キーワードreturnを使用する必要があります。


0

ベンが言ったように。「ルビメソッドの戻り値が関数本体の最後のステートメントの戻り値である」という事実により、ほとんどのルビメソッドでreturnキーワードが使用されることはほとんどありません。

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
また、意図が明確な場合は、「failed_some_early_checkの場合はnilを返す」と書くこともできます
Mike Woodhouse

@Gishuメソッド名ではなく、ifステートメントについて実際に不満を言っていました。
Andrew Grimm

@Andrew ..ああ、行方不明の終わり:) とった。マイクからの他のコメントも鎮静化するように修正しました。
紀州

なんでif failed_check then nil else @list end?それは本当に機能的です。
cdunn2001 2013年

@ cdunn2001なぜelse ifが機能するのか明確ではありません。このスタイルの理由は、早期終了によるネストを減らすことです。私見もより読みやすいです。
岐阜2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.