9

なぜ次のコード

def doSomething() = "Something"

var availableRetries: Int = 10

def process(): String = {
  while (true) {
    availableRetries -= 1
    try {
      return doSomething()
    } catch {
      case e: Exception => {
        if (availableRetries < 0) {
          throw e
        }
      }
    }
  }
}

次のコンパイラ エラーが生成されます

error: type mismatch;
 found   : Unit
 required: String
             while (true) {
             ^

?

これは C# で問題なく動作します。while は永久にループするため、終了できません。したがって、文字列以外の結果になることはありません。または、Scala で無限ループを作成する方法は?

4

6 に答える 6

17

ステートメントベースの言語であるC#(およびJavaとCとC ++)とは異なり、Scalaは式ベースの言語です。これは、構成可能性と読みやすさの点で大部分が大きなプラスですが、この場合、違いがあなたを悩ませています。

Scalaメソッドは、メソッドの最後の式の値を暗黙的に返します

scala> def id(x : String) = x
id: (x: String)String

scala> id("hello")           
res0: String = hello

Scalaでは、ほとんどすべてが表現です。ステートメントのように見えるものは、Unitと呼ばれる型の値を返す式です。値は()と書くことができます。

scala> def foo() = while(false){}
foo: ()Unit

scala> if (foo() == ()) "yes!" else "no"
res2: java.lang.String = yes!

チューリング等価言語のコンパイラーは、すべての非終了ループを検出できません(チューリング停止問題を参照)。そのため、ほとんどのコンパイラーは、ループを検出するための作業をほとんど行いません。この場合、「while(someCondition){...}」のタイプは、定数がtrueであっても、someConditionが何であってもUnitです。

scala> def forever() = while(true){}
forever: ()Unit

Scalaは、宣言された戻り型(String)が、最後の式の型である実際の戻り型(Unit)と互換性がないと判断します(while ...)

scala> def wtf() : String = while(true){}
<console>:5: error: type mismatch;
 found   : Unit
 required: String
       def wtf() : String = while(true){}

回答:最後に例外を追加してください

scala> def wtfOk() : String = {
     | while(true){}
     | error("seriously, wtf? how did I get here?")
     | }
wtfOk: ()String
于 2012-05-29T17:26:54.807 に答える
9

無限ループを機能的に定義する方法は再帰です。

@annotation.tailrec def process(availableRetries: Int): String = {
  try {
    return doSomething()
  } catch {
    case e: Exception => {
      if (availableRetries < 0) {
        throw e
      }
    }
  }
  return process(availableRetries - 1)
}

retry内部関数のないElbichloop関数:

import scala.annotation.tailrec 
import scala.util.control.Exception._ 

@tailrec def retry[A](times: Int)(body: => A): Either[Throwable, A] = { 
  allCatch.either(body) match { 
    case Left(_) if times > 1 => retry(times - 1)(body) 
    case x => x 
  } 
} 
于 2012-05-29T17:39:54.677 に答える
5

残念ながら、コンパイラは while ループを終了できないことを認識できるほど賢くありません。ただし、戻り値の型のメンバーを適切に生成できなくても、だますのは簡単です。例外をスローするだけです。

def process(): String = {
  while (true) {
    ...
  }
  throw new Exception("How did I end up here?")
}

これで、コンパイラは while ループをエスケープしてもそこで値を返すことができないことに気付くため、while ループが戻り値の型を持っているUnit(つまり、値を返さない) ことを気にしなくなります。

于 2012-05-29T16:52:34.497 に答える
4
import scala.annotation.tailrec
import scala.util.control.Exception._

def retry[A](times: Int)(body: => A) = {
  @tailrec def loop(i: Int): Either[Throwable, A] =
    allCatch.either(body) match {
      case Left(_) if i > 1 => loop(i - 1)
      case x => x
    }
  loop(times)
}

retry(10) {
  shamelessExceptionThrower()
}
于 2012-05-29T20:07:52.733 に答える
1

編集:実際の return ステートメントに気付きました。while ループ内の return ステートメントは無視されます。たとえば、REPL では次のようになります。

scala> def go = while(true){return "hi"}
<console>:7: error: method go has return statement; needs result type
   def go = while(true){return "hi"}
                        ^  

process()メソッドが a を返すことをコンパイラに伝えましたStringが、メソッド本体は単なるwhileループであり、何も返されません (それは aUnitまたは Java ですvoid)。戻り値の型を変更するか、while ループの後に String を追加してください。

def process(): Unit = {
   while(true){...}
}

また

def process(): String = {
  while(true){...}
  "done"
}
于 2012-05-29T16:26:37.133 に答える
1

私が以下に使用したセニアエルボウィッチ、およびデイブのソリューションに基づいて:

@annotation.tailrec
def retry[T](availableRetries: Int)(action: => T): T = {
  try {
    return action
  } catch {
    case e: Exception if (availableRetries > 0) => { }
  }
  retry(availableRetries - 1)(action)
}

これは、エルボウィッチとデイブのソリューションとして使用できます。

retry(3) {
  // some code
}
于 2012-05-30T08:32:23.957 に答える