2

私の問題の 1 つは、暗黙の戻り値に依存している場合、エラーが発生しやすいことです

例えば。

def foo(bar: Int): Int =
{
    if (f1(bar)) 0
    if (f2(bar)) 1 
    else -1
}

elseこの問題を修正するために、.eg のように中括弧が強制されることがあります。

def foo(bar: Int): Int =
{
    if (f1(bar)) {
        0
    } else if (f2(bar)) {
        1
    } else {
        -1
    }
}

しかし、新しいスタイルはあまりにもverbose私見です。修正する方法はありますか?

4

5 に答える 5

9

実際問題として、私はこの問題を抱えたことは一度もなく、ばかげた間違いをたくさんします。あなたは、else ステートメントをスキップしないことを完全に学習できると思います。(中括弧は何の役にも立たないことに注意してください。中括弧はスキップできますelse。)

これが本当に問題だと思う場合は、次のmatchステートメントを悪用することができます。

true match {
  case _ if f1(bar) => 0
  case _ if f2(bar) => 1
  case _ => -1
}

これにより、間違いを防ぐことができます。または、ユーティリティ メソッドを自分で作成することもできます。

trait Conditional[+A] {
  def apply[B >: A](p: => Boolean, b: => B): Conditional[B]
  def or[B >: A](default: => B): B
}
class FoundIt[+A](it: A) extends Conditional[A] {
  def apply[B >: A](p: => Boolean, b: => B) = this
  def or[B >: A](default: => B) = it
}
class NothingYet[+A] extends Conditional[A] {
  def apply[B >: A](p: => Boolean, b: => B) = {
    if (p) new FoundIt(b) else this
  }
  def or[B >: A](default: => B) = default
}
def ifelse[A](p: => Boolean, a: => A) = (new NothingYet[A]).apply(p,a)

ifelse( f1(bar), 0 )( f2(bar), 1 ) or -1 

これは長い式では少し厄介なので、次のこともできます (:pasteそこで動作させたい場合は、これを REPL の 1 つの大きなブロックに貼り付けるために使用します):

trait Predicate[+A] {
  def Or(p: => Boolean): Loadable[A]
  def Else[B >: A](default: => B): B
} 
trait Loadable[+A] {
  def Then[B >: A](b: => B): Predicate[B]
}
object NotYetTrue extends Predicate[Nothing] {
  def Or(p: => Boolean) = if (p) ActuallyLoad else SkipLoading
  def Else[B >: Nothing](default: => B) = default
}
object SkipLoading extends Loadable[Nothing] {
  def Then[B >: Nothing](b: => B) = NotYetTrue
}
object ActuallyLoad extends Loadable[Nothing] {
  def Then[B >: Nothing](b: => B) = new Loaded[B](b)
}
class Loaded[+A](a: A) extends Predicate[A] with Loadable[A] {
  def Or(p: => Boolean) = this
  def Else[B >: A](default: => B) = a
  def Then[B >: A](b: => B) = this
}
def If(p: => Boolean): Loadable[Nothing] = NotYetTrue.Or(p)

唯一のトリックは:paste、REPL で複数行のステートメントを記述し、前の行の最後に継続を配置するために使用する必要があることです。

If (f1(bar)) Then 0 Or
(f2(bar)) Then 1 Else
-1

また、ある行で使用Then { 0して次の行で再度開始する} Orか、括弧/中括弧とドットを使用してすべてを記述することもできます (これは REPL に適しています)。

If (f1(bar)) .Then (0) .
Or (f2(bar)) .Then (1) .
Else (-1)

いずれにせよ、このトリックはすべて、Scala を使用して洗練された DSL を構築する方法を示す良い例ですが、問題を解決する最善の方法ではありません。if/else ステートメントでもう少し注意することを学ぶ必要があります。そうしないと、なぜあなたが標準的な方法で物事を進めていないのか、人々は当惑するでしょう。(そして、パフォーマンスが重要な場合は遅くなる可能性があります。これmatchはかなり優れていますが、If高性能ループでは魔法はよくありません。)

于 2012-09-22T07:12:54.837 に答える
1

わかります。十分な数の人が言うことはありません、複数の条件付きは1つの関数には多すぎます!

適用するいくつかの関数を折ります。collectFirstとしても表現できます。

object Ifless extends App {
  def f1(i: Int) = i == 1
  def f2(i: Int) = i == 2
  def p1(i: Int) = if (f1(i)) Some(0) else None
  def p2(i: Int) = if (f2(i)) Some(1) else None
  def default(i: Int) = Some(-1)  //whatever
  def f(i: Int): Int = {
    val ps = List(p1 _, p2 _, default _)
    ps.foldLeft(None: Option[Int])((r,v) => r match {
      case None => v(i)
      case x => x
    }) getOrElse -1  //whatever
  }
  println(f(1))
  println(f(2))
  println(f(3))
}

すなわち、

object Ifless2 extends App {
  // imagine this is not a switchable match
  def fs: List[PartialFunction[Int, Int]] = List (
    { case 1 => 0 },
    { case 2 => 1 },
    { case _ => -1 }
  )
  def f(i: Int): Int = {
    fs.collectFirst { case pf if pf.isDefinedAt(i) => pf(i) } getOrElse -1
  }
  println(f(1))
  println(f(2))
  println(f(3))
}

また、残念なことに、scalacは、「警告:純粋な式はステートメントの位置では何もしません」という悪名高い有用なものを出力しません。

于 2012-09-22T06:53:59.820 に答える
0

ステートメントが多い場合は、中かっこが必要になります。この特定の例では、単一の式があり、中かっこを完全に削除すると、エラーがより明確になり、詳細が少なくなります。また、IDE を使用している場合は、コードを再フォーマットできます。問題を指摘するのに役立ちます。

以下は、中括弧を使用しない書式設定の例です。

  • foo正しいバージョンです。
  • foo2時々私たちは何かを忘れてしまいました。構文エラーを参照してください。
  • foo3忘れもありましelseたが、コードがブレース ブロック内にあるため、コンパイラはif (f1(bar)) 0ステートメントを有効な分離ステートメントと見なします。

ここに画像の説明を入力

スクリーンショットの前にコードを再フォーマットしfoo2た場合、if/else が一致していないことがより明白になります。

于 2012-09-22T07:27:39.733 に答える
0

何を求めているのか不明です。あなたが意図しているのか、間違ったことをいつ入力したのかを scala に知らせたい場合ifelse if、おそらく運が悪いでしょう。正しいコマンドを入力する必要があり、中かっこを追加または削除しても、コンパイル時エラーが発生するかどうかは変わりません。

かっこなしで if-elseif-else を記述する方法を知りたい場合は、次のようにします。

if (f1(bar)) 0
else if (f2(bar)) 1 
else -1
于 2012-09-22T05:43:32.943 に答える
0

おそらく、次のガイドラインが役立ちます。

(1)分岐のないifステートメントにelseは副作用が必要です (式の結果の型が であるためUnit)。したがって、上記の間違いを避けるために、常に中括弧と本文を新しい行で使用するか、そのステートメントの下に空行を強制してください。それ以外の...

if (f1(bar)) addToParent()  // addToParent has side-effect
if (f2(bar)) 1
else -1

...どちらかを使用...

if (f1(bar)) addToParent()

if (f2(bar)) 1
else -1

...また...

if (f1(bar)) {
  addToParent()
}
if (f2(bar)) 1
else -1

ifその結果、2 つのキーワードが互いに真下に並んで表示されることはありません。つまり、2 つifの が互いに上にある場合は、間違いを犯したに違いありません。

(2)複数のチェックがある場合は、明示的な中括弧を使用するか、(例のように)かなり短い場合は、それらを1行に保ちます。

if (f1(bar)) 0 else if (f2(bar)) 1 else -1

これはコンパイルされないためです:

if (f1(bar)) 0 if (f2(bar)) 1 else -1

<console>:3: error: ';' expected but 'if' found.
              if (f1(bar)) 0 if (f2(bar)) 1 else -1
                             ^

Scala が純粋な関数型言語であるか、副作用を識別できる場合、コンパイラはもちろん警告を発することができます。ここでも IDE が役に立ちます。IntelliJ IDEA でこれを試しましたが (スマートな警告がかなりあるため)、警告は表示されません。未使用のリテラル(あなたの場合のように)、または副作用がないように見えるメソッドの呼び出し(戻り値の型はそうではなくUnit、スタイル ガイド メソッドには空の括弧がありません)。

于 2012-09-22T07:32:22.067 に答える