私の本を読みたくないですか?答えは次のとおりです。
おまけとして、これは最速のパフォーマンスのソリューションです。
public void CheckValidFoo(String Title) {
return (Title.LastIndexOf("Foo") <= 0);
}
コーディングロジックのレッスンを読んでください(この老人をユーモアにします)
この質問は古いかもしれませんが、後でこの質問を見つけて、単語の問題を論理的に抽出する方法に興味がある人のために、この非常に単純な宿題の非常にやり過ぎた分析を以下に示します。
一連のテストを実行して、どのオプションが最も高速であるかを確認しました。これは、多くの場合、ロジックの欠陥を確認するのに役立ちます。また、私の思考プロセスについても説明します。私見ですが、問題をそのコア ロジックに抽出する方法を理解することは、実世界で使用するために知っておくとよいことです。
結局のところ、それがビジネス要件ドキュメントです... 機能仕様 (アーキテクチャ設計など) に抽出する必要がある言葉の問題です。
ステップ1
余分な情報の排除
与えられた要件に基づいて、小文字の foo は重要かもしれませんし、重要でないかもしれません: foo を含まず、Foo を含まない文字列が false を返すべきであるという明示的なステートメントはありません。また、Foo を含まず、foo を含まない文字列が true を返す必要があるという明示的なステートメントもありません。
完璧な世界では、要件を明確にするために戻ってきますが、場合によっては時間がありません。締め切りがあると仮定すると、私たちが気にかけているのは、文の最初の位置にあるときのみ Foo が大文字であり、それ以外の場合は常に小文字であるという仮定で先に進むことになるので、foo を完全に無視し、"クライアント」は不平を言い、明確さが欠けていることを指摘し、なぜあなたが判断を下したのかを説明します(プロジェクトを時間通りに、また予算内に維持するため、該当する場合)。
ステップ2
論理を OR/AND の部分に分割する:
Foo をコンポーネントに分割すると、全体を見るよりも個々の部分を見ることができます。したがって、文字列を「Foo の前のもの」(「何もない」場合でも) と「Foo の後のもの」に分割した場合、または文字列を分割するために Foo が存在しない場合は、1 つの部分しか確認できません。(私たちの脳は常にこれを行っています。これはパターン認識と呼ばれます)。
IF Foo が見つからないため、文字列を分割できません
OR Fooで
分割しても、前に何もなく、後のすべての部分が 2 つしか得られません「後」)
Foo が文字列の他の場所に見つからない
いいね?まあ、それは 100% 正確ですが、いくつかの粗雑な部分を切り取り、さらに蒸留することはできます。つまり、コンピューターは人間のように考えないため、私たちの精神処理は人間にとって非効率的であることを覚えておいてください。
Foo がまったく見つからない場合は有効と見なされ、最初の Foo は有効ですが、文字列の後半の Foo は無効であるため、次のように言えます。
IF Foo が見つからない
OR
Foo が文字列の最初の位置より後のどこにも見つからない
きついですね。今すぐやめないでください。もっとうまくやることができます。
先頭に Foo があればそれでいいじゃないですか。したがって、「Foo が見つかりません」または「Foo が最初に見つかり、かつ Foo が他に見つかりません」は、より純粋な論理 (ブール値、真/偽、白黒) の観点から見ることができます。
- LET FNA = "Foo はどこにも見つかりません"
- LET FN1 = "位置 1 に Foo が見つかりません"
- LET FN2 = "位置 1 の後に Foo が見つかりません"
- LET FF1 = "Foo が位置 1 にある"
- LET FF2 = "位置 1 の後に Foo が見つかりました"
したがって、無効であることが保証されているケースのみを無効として定義し、残りを有効としてマークします。ブール演算を使用して、すべてのユース ケースを決定します。
- LET FNA = 有効
- LET FN1 = 有効
- LET FN2 = 有効
- LET FF1 = 有効
- LET FF2 = 無効
絶対に false を返すケースのみにラベルを付けたので、計算を実行して、無効な値または false 値を取得するケースのみを確認できます。
FNA = FN1 および FN2 (したがって、FNA & X = true の場合、F1 & X も true でなければならず、F2 & X も true でなければなりません);
FNA および/または FF1 = true であるため、これら 4 つの変数のすべての組み合わせが true であることがわかります。これにより、結合する変数が 1 つだけ残り、FF2 とすべてが常に false になることがすぐにわかります。
つまり、人間の論理に翻訳し直して... このタスクがどれほど単純であるかがわかりますか?
位置 1 の後に Foo が見つかった場合のみ FALSE
または、ブール値を反転するには (要件では、有効な場合に true を返すように指定されているため):
位置 1 の後に Foo が見つからない場合、文字列は有効です。
または、コンピューターの思考のように言えば、次のようになります。
文字列の最後から 2 文字目から最後の文字までスキャンしてもFooが見つからない場合、文字列は有効です
では、これ以上蒸留することはできません。それでは、これらのさまざまなロジックをコーディングして、実際のコードでどのように機能するかを見てみましょう。
using System;
public static class Test
{
public static bool CheckFooTestA(String SearchMe, String[] FindMe)
{
//split the string like the human eye does and check the count of Foos
//and the position of the Foo or Foos to determine our logic:
string[] v = SearchMe.Split(FindMe, StringSplitOptions.None);
//Foo not found, OR foo found once and was at the beginning of the string
return (v.Length == 0 || v.Length == 1 && v[0] == String.Empty);
}
public static bool CheckFooTestB(String SearchMe, String[] FindMe)
{
//scan the way computers or non-speed readers do, and look for the first instance of Foo
int i = SearchMe.IndexOf(FindMe[0]);
//Foo not found OR
// foo found at the start of the string
// AND the last Foo found is also at the start of the string
return (i == -1 || i == 0 && SearchMe.LastIndexOf(FindMe[0]) == 0 );
}
public static bool CheckFooTestC(String SearchMe, String[] FindMe)
{
//Use the logic we distilled from the word problem to make this single check:
return (SearchMe.LastIndexOf(FindMe[0]) <= 0);
}
public static void Main()
{
String[] x = new String[]{
"Foo foo Foo bar",
"Foo foo foo bar",
"foo foo Foo bar",
"foo foo foo bar",
"asfda asdfa asf" };
var s = new []{"Foo"};
var i = 0;
bool f=false;
long End = DateTime.Now.Ticks;
long Start = DateTime.Now.Ticks;
for (; i < 1000; i++) {
f = CheckFooTestA(x[i%5],s);
}
End = DateTime.Now.Ticks;
Console.WriteLine((End - Start).ToString() + " ticks (Test A)");
i = 0;
f = false;
End = DateTime.Now.Ticks;
Start = DateTime.Now.Ticks;
for (; i < 1000; i++) {
f = CheckFooTestB(x[i%5],s);
}
End = DateTime.Now.Ticks;
Console.WriteLine((End - Start).ToString() + " ticks (Test B)");
i = 0;
f = false;
End = DateTime.Now.Ticks;
Start = DateTime.Now.Ticks;
for (; i < 1000; i++) {
f = CheckFooTestC(x[i%5],s);
}
End = DateTime.Now.Ticks;
Console.WriteLine((End - Start).ToString() + " ticks (Test C)");
}
}
Test.Main();
出力:
260510 ticks (Test A)
117150 ticks (Test B)
76160 ticks (Test C)
結果と結論
テスト A (見つかった単語を視覚的に分割/カウントする人間のロジック) は、テスト B (抽出されたロジックでインデックスを使用してスキャンする) と比較して、テスト A は 220% 以上長く実行されます!
テスト C は最高のパフォーマーで、必要な文字列のスキャンは 1 回だけです。必要な処理時間の 30% 未満で到着します (テスト A は、テスト C が同じ作業を完了するのに必要な時間の 340% 以上かかります)。
うまくいけば、どこかの学生がこれを読んで、電球が点灯します. 「機能する」ものを作成する方法はいつでも思いつくことができますが、ブール論理と、概念をその核心まで抽出する方法を理解することは、作業の質に大きな影響を与える可能性があります.