12

重複の可能性:
絶え間ない虐待?

さまざまな API で使用されているのを見てきましたが-1、最も一般的なのは、0 から始まるインデックスを持つ「コレクション」を検索するときで、通常は「見つからない」インデックスを示すために使用されます。-1これは、そもそも合法的なインデックスではないため、「機能します」 。-1負の数はどれでも機能するはずですが、ある種の (書かれていない?) 慣例として、ほとんど常に使用されていると思います。

少なくとも今のところ、スコープを Java に限定したいと思います。私の質問は次のとおりです。

  • -1このような「特別な」戻り値としての使用に関する Sun の公式の言葉は何ですか?
  • James Gosling、Josh Bloch、または Java 以外の権威ある人物から、この問題に関してどのような引用がありますか?
  • この問題に関する過去の注目すべき議論は何でしたか?
4

10 に答える 10

11

これは、型に範囲チェックが含まれていない言語で一般的なイディオムです。「範囲外」の値は、いくつかの条件の 1 つを示すために使用されます。ここで、戻り値は 2 つのことを示します: 1) 見つかった文字と 2) 見つかった場所。-1 fornot foundと非負のインデックス for を使用すると、これらの両方が 1 つの値に簡潔にエンコードされ、インデックスを返す必要がないfoundという事実が示されます。not-found

Ada や Pascal など、範囲チェックが厳密な言語では、メソッドは (疑似コード) として実装される場合があります。

   bool indexOf(c:char, position:out Positive);

Positiveint のサブタイプですが、負でない値に制限されています。

これにより、見つかった/見つからないフラグが位置から分離されます。位置は出力パラメータとして提供されます - 基本的に別の戻り値です。また、指定された位置から検索を開始するための in-out パラメータにすることもできます。-1 を使用して not-found を示すことは、Positive 型の範囲チェックに違反するため、ここでは許可されません。

Java の代替手段は次のとおりです。

  • 例外をスローします。これは、文字が見つからないことは例外的な条件ではないため、ここでは適切な選択ではありません。
  • 結果をいくつかのメソッドに分割しboolean indexOf(char c); int lastFoundIndex();ます。これは、オブジェクトが状態を保持する必要があることを意味します。これは、状態がスレッド ローカル ストレージに格納されるか、同期が使用されない限り、並行プログラムでは機能しません (すべてかなりのオーバーヘッド)。
  • のように、位置と見つかったフラグを別々に返しboolean indexOf(char c, Position pos)ます。ここで、位置オブジェクトの作成は不要なオーバーヘッドと見なされる場合があります。
  • 複数値の戻り型を作成する

そのような

class FindIndex {
   boolean found;
   int position;
}

FindIndex indexOf(char c);

戻り値は明確に分離されていますが、オブジェクト作成のオーバーヘッドが発生します。FindIndexその一部は、をパラメータとして渡すことで軽減できます。

FindIndex indexOf(char c, FindIndex start);

ちなみに、複数の戻り値は Java (oak) の一部になる予定でしたが、リリースまでの時間を短縮するために 1.0 より前に廃止されました。ジェームズ・ゴズリング、彼らが含まれていればよかったと言います。それはまだ望まれる機能です

私の見解では、マジック値の使用は、オブジェクト作成のオーバーヘッドを過度に必要とせずに、多値の結果 (フラグと値) を単一の戻り値にエンコードする実用的な方法です。

ただし、マジック値を使用する場合、それらが関連する API 呼び出し間で一貫している場合は、操作がはるかにうまくいきます。例えば、

   // get everything after the first c
   int index = str.indexOf('c');
   String afterC = str.substring(index);

への呼び出しで -1 を使用するsubstringIndeOutOfBoundsException. 代わりに、負の値が文字列の末尾から始まると見なされる場合、-1 で呼び出されたときに部分文字列が "" を返す方が一貫性があった可能性があります。エラー状態の魔法の値を批判する人は、戻り値は無視できる (または正であると見なす) ことができると言っています。これらの魔法の値を便利な方法で処理する一貫した API は、-1 をチェックする必要性を減らし、よりクリーンなコードを可能にします。

于 2010-06-13T11:38:50.667 に答える
4

-1はマジックナンバーですか?

この文脈では、そうではありません。...について特別なことは何もありません-1が、負であることによって無効なインデックス値であることが保証されているという事実を除けば。

アンチパターン?

いいえ。アンチパターンとして認定するには、このイディオムに何か有害なものが必要です。-1この方法を使用しても害はないと思います。

コードの匂い?

同上。(裸のリテラルではなく、名前付き定数を使用する方が間違いなく優れたスタイル-1です。しかし、それはあなたが求めているものではないと思います。とにかく「コードの匂い」とは見なされません、IMO.)

当局からの引用とガイドライン

私が知っているわけではありません。ただし、この「デバイス」はさまざまな標準クラスで使用されていることがわかります。たとえば、文字または部分文字列が見つからなかったことを示すためにString.indexOf(...)戻ります。-1


私に関する限り、これは単に、場合によっては役立つ "アルゴリズム デバイス" にすぎません。文献を振り返ってみると、1960 年代以前にさかのぼってこのように-1(または1 から始まる配列を使用する言語で) 使用する例が見られると確信しています。0

-1他の負の数ではなくの選択は、単に個人的な好みの問題であり、(IMO) 分析する価値はありません., この文脈では.


-1メソッドが例外をスローする代わりにエラーを示すために (または他の値を)返すことは、悪い考えかもしれません。ただし、ここでの問題は、返される値ではなく、メソッドが呼び出し元に明示的にエラーをテストすることを要求しているという事実です。

-1反対に、 (またはその他のもの) で表される「状態」が「エラー」/「例外的な状態」でない場合、特別な値を返すことは合理的かつ適切です。

于 2010-06-13T12:50:59.543 に答える
3

Java と JavaScript の両方-1が、インデックスが見つからない場合に使用します。インデックスは常に0-nなので、かなり明白な選択のようです。

//JavaScript
var url = 'example.com/foo?bar&admin=true';
if(url.indexOf('&admin') != -1){
  alert('we likely have an insecure app!');
}

.indexOf()このアプローチ (メソッドを持つように配列型の要素を拡張するときに使用した) は、ごく普通のことだと思います。

一方、strpos()などの PHP アプローチを試すこともできますが、複数の戻り値の型があるため混乱します (見つからない場合は FALSE を返します)。

于 2010-06-13T11:15:16.073 に答える
2

戻り値としての -1 は少し醜いですが、必要です。「見つからない」状態を通知する代替手段は、IMHO よりもはるかに悪いものです。

  • 例外をスローすることもできますが、これは理想的ではありません。例外は、なんらかの形の回復または伝播された障害を必要とする予期しない状況を通知するために使用するのが最適だからです。部分文字列の出現が見つからないことは、実際にはかなり予想されます。また、例外をスローすると、パフォーマンスが大幅に低下します。

  • (found,index) を使用して複合結果オブジェクトを使用することもできますが、これにはオブジェクトの割り当てと、呼び出し側で結果を検査するためのより複雑なコードが必要です。

  • contains と indexOf の 2 つの個別の関数呼び出しを分離することもできますが、これも呼び出し元にとって非常に面倒であり、両方の呼び出しが O(n) になり、文字列の完全なトラバーサルが必要になるため、パフォーマンスが低下します。

個人的には、-1 定数を参照するのは好きではありません。not-found のテストは常に次のようになります。

int i = someString.indexOf("substring");
if (i>=0) {
  // do stuff with found index
} else {
  // handle not found case
}
于 2010-06-13T13:22:05.223 に答える
1

51% が会社の株主間ですべてを意味する理由のように、-2 や -3 ではなく最も近くて理にかなっている...

于 2010-06-13T12:09:26.000 に答える
1

コード内のすべての定数値に対して final クラス変数を定義することをお勧めします。ただし、明示的な宣言なしで 0、1、-1、"" (空の文字列) を使用することは一般的に認められています。

于 2010-06-13T11:16:46.280 に答える
1

これは、単一のプリミティブ値のみを返す C からの継承です。Java では、単一のオブジェクトを返すこともできます。

そのため、新しいコードの場合、サブタイプが instaceof で使用される問題を示すベースタイプのオブジェクトを返すか、「not Found」例外をスローします。

既存の特別な値については、それに応じて -1 をコード名の定数にしてください - NOT_FOUND - 読者は javadocs をチェックしなくても意味を伝えることができます。

于 2010-06-13T11:17:28.557 に答える
1

と同じプラクティスが にnull適用され-1ます。それは何度も議論されてきました。

例: Java API の設計 - NULL または例外

于 2010-06-13T11:18:52.190 に答える
1

私の知る限り、このような値はセンチネル値と呼ばれますが、最も一般的な定義はこのシナリオとは少し異なります。

Java などの言語は参照渡しをサポートしないことを選択したため (これは良い考えだと思います)、個々の引数の値は変更可能ですが、関数に渡される変数は影響を受けません。この結果、1 つの型のみの戻り値を 1 つだけ持つことができます。したがって、有効な型の無効な値を選択し、それを返して追加のセマンティクスを転送します。戻り値は実際には操作の戻り値ではなく、特別なシグナルであるためです。

今、私が推測する最もクリーンなアプローチは、あなたが求めている要素がコレクションにない場合、2番目のメソッドが例外をスローするメソッドを持つことですcontainsindexOfなんで?次のことが真であると予想されるためです。

someCollection.objectAtIndex(someCollection.indexOf(someObject)) == someObject

あなたが得る可能性が高い-1のは が範囲外であるため例外ですが、このもっともらしい関係が真ではない実際の理由は、それsomeObjectが の要素ではないためですsomeCollection。それが、内部呼び出しが例外を発生させる理由です。

これはクリーンで堅牢ですが、2 つの重要な欠陥があります。

  • 通常、両方の操作には O(n) のコストがかかるため (コレクション内に逆マップがない場合)、1 つだけを実行した方がよいでしょう。
  • それは本当にかなり冗長です。

最後に、決めるのはあなた次第です。これは哲学の問題です。堅牢性を犠牲にして短さと速度の両方を達成するための「セマンティックハック」と呼んでいます。あなたの電話 ;)


グリッツback2dos

于 2010-06-13T11:41:10.700 に答える
1

これは、0 ベースの配列で最初に検出された無効な値であるため使用されます。ご存知のように、すべての型が null または何も保持できるわけではないため、何も意味しない「何か」が必要です。

公式ではなく、状況に非常に賢明であるため、慣習(書かれていない)になったばかりです。個人的には、私もそれを問題とは呼びません。API の設計も作成者に委ねられていますが、ガイドラインはオンラインで見つけることができます

于 2010-06-13T11:24:17.243 に答える