Don't want to read my book? Here's the answer:
As a bonus, this is the fastest solution.
public void CheckValidFoo(String Title) { return (Title.LastIndexOf("Foo") <= 0); }
Read on for coding logic lessons (this old man's humor)
This question may be old, but for those who find this question later, who are curious how to logically distill problems with the text (so please do not reduce any of this lurker who finally decided to publish), here is my massive brute force analysis for this extremely simple homework:
I checked the test suite to see which options were the fastest - this often helps me see the flaws in my logic. I am also going to explain my thought process, since, IMHO, understanding how to solve a problem with it is a good thing that you need to know for use in the real world.
In the end, the fact that Business Docs are ... vocabulary issues that need to be distilled into functional specifications (such as architectural design).
Step 1
Eliminate external information Based on requirements, the lowercase foo may or may not matter: there is no explicit statement that a string that does not contain foo and does not contain Foo should return false; There is also no explicit statement that says a string that does not contain Foo and does not contain foo should return true.
In an ideal world, you will return to clarify the requirements, but in some cases there is no time. Assuming there is a deadline, I would go ahead with the assumption that all we care about is Foo, which is uppercase only when in the first position in the sentence, lowercase at all other times, so we completely ignore foo, and if “customer”, indicate the lack of clarity and explain why you made the decision (in order to keep the project on time and on budget, if applicable).
Step 2
Divide the logic into OR / AND parts:
Breaking Foo into components allows us to look at individual parts, which may be easier than looking at the whole. So, if we break the line into “stuff before Foo” (even if it's “nothing”) and “stuff after Foo” OR, if Foo doesn't exist to split the string, we only have one part to look at. (Our brains do this all the time - this is called pattern recognition).
IF String cannot be split because Foo was not found
OR
splitting on Foo gives us no more than two parts: nothing before and just after
Foo not found anywhere in the line
It sounds good? Well, this is 100% more accurate, but we can cut and overtake it a bit - remember that computers don’t think like humans, so our mental processing is ineffective for them.
Since Foo is not found at all, it is considered valid, and Foo is valid at the beginning, but Foo is not valid anywhere else in the line, we can say:
IF Foo not found
OR
Foo not found anywhere in the line after the first position
It seems tight, right? Do not stop now. We can do better.
If Foo is found at the beginning, we're fine with him, right? Thus, "Foo is not Found" OR "Foo is found at the beginning, and Foo is not found anywhere else", you can look at a cleaner logic (logical, true / false, black and white):
- LET FNA = "Foo is nowhere to be found"
- LET FN1 = "Foo not found at position 1"
- LET FN2 = "Foo not found after position 1"
- LET FF1 = "Foo is in position 1"
- LET FF2 = "Foo found after position 1"
So, now we define as unacceptable only those cases that are guaranteed invalid, and mark the rest as valid. We will use logical math to determine all use cases.
- LET FNA = Valid
- LET FN1 = valid
- LET FN2 = valid
- LET FF1 = Valid
- LET FF2 = invalid
Now that we have only noted cases that absolutely return false, we can do the math to see the only cases where we get an invalid / false value.
FNA = FN1 and FN2 (therefore, if FNA and X = true, then F1 and X must be true, and F2 and X must also be true);
FNA and / or FF1 = true, so we know that all combinations and / or these 4 variables = true; This leaves only one variable that can be combined, and we can very quickly see that FF2 and something will always be false.
So it translates back into human logic ... look, how much easier is this task?
ONLY FALSE IF Foo is found after position 1
Or, to flip a boolean (since the requirements say to return true for valid cases):
IF Foo is NOT found after position 1, the string is valid.
Or, if it is more like computer thinking:
IF scan from end of line until second to last character finds Foo, line is true
There, now we cannot distill it further. So let the code these different bits of logic and see how they execute in real code:
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();
Conclusion:
260510 ticks (Test A) 117150 ticks (Test B) 76160 ticks (Test C)
Results and Conclusions
Test A (human logic of visual splitting into / counting found words) compared to test B (scanning using indexes with distilled logic), test A works 220% longer!
Test C is the best performer - only one scan of the required string is required. Less than 30% of the processing time is required (test A requires more than 340% of the time required to complete the same work).
So, I hope that some student read this somewhere, and the light bulb continues. You can always figure out how to make things "work", but understanding logical logic and how to separate a concept to its core can significantly affect the quality of your work.
Happy coding, that's it!