Scalaに戻る


84

私は初心者のscalaプログラマーであり、奇妙な振る舞いに出くわしました。

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true;
      else false;

    if (elem.head == '(')
      balanceMain(elem.tail, open, count + 1);....

上記の場合elem.isEmpty、基本的にはtrueを返したいと思いcount == 0ます。それ以外の場合は、falseを返します。

上記で、scalaにreturnステートメントを追加する必要がないことを読みました。だから私はreturn上記を省略しました。ただし、ブール値は返されません。returnステートメントをとして追加した場合return true。それは完全に機能します。なぜそうなのですか?

また、scalaでreturnステートメントを使用することが悪い習慣と見なされるのはなぜですか


2
ありません、通常はreturnキーワードの必要は限り、あなたは十分に小さいメソッドにコードを破るように、。
mauhiz 2012

@mauhizありがとう。説明してもらえますか?どのようにそれをしますか。
Jatin 2012

4
Courserascalaコースを受講しているようです。すべてのベスト:)
weima 2012

回答:


139

returnキーワードを省略するほど簡単ではありません。Scalaでは、存在しないreturn場合、最後の式が戻り値と見なされます。したがって、最後の式が返したいものである場合は、returnキーワードを省略できます。何を返したいがある場合でもない最後の式は、Scalaはあなたがそれを返すように望んでいたことを知ることができません

例:

def f() = {
  if (something)
    "A"
  else
    "B"
}

ここで、関数の最後の式は、f文字列に評価されるif / else式です。明示的にreturnマークされていないため、Scalaは、このif / else式の結果を返したいと推測します:文字列。

ここで、if / else式のに何かを追加すると、次のようになります。

def f() = {
  if (something)
    "A"
  else
    "B"

  if (somethingElse)
    1
  else
    2
}

ここで、最後の式は、Intに評価されるif / else式です。したがって、の戻り値の型はfIntになります。本当に文字列を返したいのなら、Scalaはそれが私たちの意図したものだとは知らないので、私たちは困っています。したがって、文字列を変数に格納して2番目のif / else式の後に返すか、文字列部分が最後に発生するように順序を変更することによって、これを修正する必要があります。

最後に、次のreturnようなネストされたif-else式を使用しても、キーワードを回避できます。

def f() = {
  if(somethingFirst) {
    if (something)      // Last expression of `if` returns a String
     "A"
    else
     "B"
  }
  else {
    if (somethingElse)
      1
    else
      2

    "C"                // Last expression of `else` returns a String
  }

}


4
すべての神々、ありがとう!私は同じ問題で何時間も戦っていましたが(Courseraでもコースを作成していました)、なぜ復帰が必要なのか理解できませんでした。
イゴールロドリゲス

最初の例では、リターンを追加するとどうなりますか。すなわちreturn "A"return "B"
SamAko 2015

@ T.Rexは、値「A」または「B」でf()の呼び出し元に戻ります。しかし、私が私の答えで説明したように。Scalaではreturnを使用しないでください。Scalaのifステートメントは機能的に機能します。彼らはタイプのあるものに評価します。Javaの三項演算子のように(?:)。例:val foo = if(mybool) "A" else "B" -fooは、 "A"または "B"のいずれかを含む文字列になります。同様に、何かを返すのではなく、その中の最後の式の値を評価する関数について考えてみてください。
grmpfhmbl 2016

23

これまでの回答で説明したように、このトピックは実際にはもう少し複雑です。Rob Norrisによるこのブログ投稿では、それをより詳細に説明し、returnを使用すると実際にコードが破損する(または少なくとも非自明な効果がある)場合の例を示しています。

この時点で、投稿の本質を引用させてください。最も重要なステートメントは最初にあります。これをポスターとして印刷して壁に貼ってください:-)

returnキーワードは、「オプション」または「推論」ではありません。それはあなたのプログラムの意味を変えるので、決してそれを使うべきではありません。

関数をインライン化すると、実際に何かが壊れてしまう一例があります。

// Inline add and addR
def sum(ns: Int*): Int = ns.foldLeft(0)((n, m) => n + m) // inlined add

scala> sum(33, 42, 99)
res2: Int = 174 // alright

def sumR(ns: Int*): Int = ns.foldLeft(0)((n, m) => return n + m) // inlined addR

scala> sumR(33, 42, 99)
res3: Int = 33 // um.

なぜなら

return評価された式は、その方法の呼び出し元に、現在の計算および戻り廃棄return表示します。

これは、リンクされた投稿に示されている例の1つにすぎず、最も理解しやすいものです。まだまだありますので、ぜひ行って読んで理解してください。

Javaのような命令型言語から来た場合、これは最初は奇妙に思えるかもしれませんが、このスタイルに慣れれば、それは理にかなっています。別の引用で締めくくります:

早く戻りたいと思う状況に陥った場合は、計算を定義した方法を再考する必要があります。


12
私はRobに同意しません、IMOreturnはラムダ式だけで使用してはいけませんが、これは言語でのくだらない設計上の選択であり、コードはコンパイルすらすべきではありません(Pythonlambdaでは、returnステートメントのないキーワードを使用することでこれを回避できます)。 ..他の場合ではreturn、値を返したい場合に使用しても実際の問題は見られません(そして、すべての言語でreturnが使用されるため、メソッドの実行を終了します!)
daveoncode 2017年

2
あなたは自由に意見を述べることができますが、多くの人がScalaでreturnを使用することは少なくとも悪いスタイルであることに同意しています。returnを使用する公式のScalaドキュメントの例を見つけてください。これは、公式ドキュメントでは説明されておらず、仕様ドキュメント(6.20章)でのみ説明されているAFAIKです。Martin Odersky自身(Scalaでのプログラミング)を引用すると、「メソッドの推奨スタイルは、実際には、明示的な、特に複数のreturnステートメントを避けることです。代わりに、各メソッドを、返される1つの値を生成する式と考えてください。」この本の最初の版は無料でオンラインで入手できますartima.com/pins1ed– Grmpfhmbl 2018
1

4

私はScalaをプログラムしていませんが、暗黙のリターンを持つ別の言語(Ruby)を使用しています。if (elem.isEmpty)ブロックの後にコードがあります。コードの最後の行が返されるものです。そのため、期待したものが得られません。

編集:関数を書く簡単な方法もあります。isEmptyとcountのブール値を使用して、trueまたはfalseを自動的に返します。

def balanceMain(elem: List[Char]): Boolean =
{
    elem.isEmpty && count == 0
}

ありがとう。しかし、elem.isEmpty && count == 0がtrueを返す場合にのみ戻りたいのですが、それ以外の場合はブロックを続行します。上記は偽であっても返されます。
Jatin 2012

@ジャティン:ああ、気づかなかった。explicitこの場合、早期に返品するのが適切です。
jmdeldin 2012

@Frank:OPのコードでも定義されていません。メソッド呼び出しだと思いました。
jmdeldin 2012

4

if対応するがないステートメントを記述しないでくださいelseelseフラグメントにを追加すると、truefalseが実際には関数の最後の式であることがわかります。

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true
      else
        false
    else
      if (elem.head == '(')
        balanceMain(elem.tail, open, count + 1)
      else....

6
私はそれを適用し、3つのネストされたIFを取得しましたが、見た目は醜いです。見栄えを良くするパターンはありますか?
Capacytron 2015

4

デフォルトでは、関数の最後の式が返されます。あなたの例では、ポイントの後に、戻り値が必要な別の式があります。最後の式の前に何かを返したい場合でも、を使用する必要がありますreturn

このように例を変更してBoolean、最初の部分からを返すことができます

def balanceMain(elem: List[Char]): Boolean = {
  if (elem.isEmpty) {
    // == is a Boolean resulting function as well, so your can write it this way
    count == 0
  } else {
    // keep the rest in this block, the last value will be returned as well
    if (elem.head == "(") {
      balanceMain(elem.tail, open, count + 1)
    }
    // some more statements
    ...
    // just don't forget your Boolean in the end
    someBoolExpression
  }
}

9
「ステートメント」ではありません。「表現」
Viktor Klang 2012

0

早期返品を目的としたユースケースの一致。これにより、すべてのreturnブランチを明示的に宣言するように強制され、returnをどこかに書き込むのを忘れるという不注意な間違いを防ぎます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.