web-dev-qa-db-ja.com

Scalaで戻る

私は初心者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およびcount == 0の場合にtrueを返します。それ以外の場合は、falseを返します。

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

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

68
Jatin

returnキーワードを省略することほど単純ではありません。 Scalaでは、returnがない場合、最後の式が戻り値として使用されます。したがって、最後の式が返すものである場合、returnキーワードを省略できます。しかし、返したいものがnot最後の式である場合、Scala あなたがしたいことを知らないそれを返します

例:

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

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

さて、if(else)式の後にafter何かを追加すると:

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

  if (somethingElse)
    1
  else
    2
}

現在、最後の式は、Intに評価されるif/else式です。したがって、fの戻り値の型はIntになります。本当に文字列を返すようにしたい場合は、Scalaがアイデアなしを持っているので問題になります。したがって、文字列を変数に格納し、2番目のif/else式の後に返すか、文字列部分が最後に発生するように順序を変更することにより、修正する必要があります。

最後に、あなたのようなif-else式がネストされていてもreturnキーワードを避けることができます:

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
  }

}

119
dhg

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

この時点で、投稿の本質を引用させてください。最も重要な声明は、冒頭にあります。これをポスターとして印刷して、壁に置いてください:-)

returnキーワードは「オプション」または「推論」ではありません。プログラムの意味が変わるため、使用しないでください。

関数をインライン化すると、実際に何かを壊す1つの例を示します

// 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のような命令型言語から来たとき、これは最初は奇妙に思えるかもしれませんが、一度このスタイルに慣れると意味があります。別の引用で締めくくりましょう。

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

19
Grmpfhmbl

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

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

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

対応するifなしでelseステートメントを記述しないでください。フラグメントにelseを追加すると、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....
4
Bart Schuller

デフォルトでは、関数の最後の式が返されます。この例では、戻り値が必要なポイントの後に別の式があります。最後の式の前に何かを返したい場合、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
  }
}
4
Tharabas

早期返還のためにユースケースの一致。すべてのリターンブランチを明示的に宣言することを強制し、どこかにreturnを書くのを忘れるという不注意なミスを防ぎます。

0
Zhu Jinxuan