不想读我的书?这就是答案:
此外,这是最快的解决方案。
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来分割字符串,那么我们只有一个部分需要考虑。 (我们的大脑一直在这样做 - 这称为模式识别)。
如果无法拆分字符串,因为未找到Foo
或
在Foo上分割给我们不超过两个部分:也就是分割后面的内容
AND(由于先前的检查仅在“之前”找到空字符串,而且在“之后”只有一个“部分”中隐含)
字符串中没有其他地方可以找到Foo
听起来不错吧? 这是100%准确的,但我们可以删减一些笨重的内容并进一步简化它 - 请记住计算机不像人类那样思考,所以我们的心理过程对于他们来说是低效的。
由于完全找不到Foo被认为是有效的,而以Foo开头是有效的,但在字符串中任何其他位置出现Foo都是无效的,我们可以说:
如果未找到Foo
或
从第一个位置开始,在字符串中没有找到Foo
听起来很紧凑,对吧?别停下。我们可以做得更好。
如果在开头找到了Foo,我们就没问题了,对吧?因此,“未找到Foo”或“在开头找到了Foo并且在任何其他位置上未找到Foo”可以从更纯粹的逻辑(布尔、真/假、黑白)角度看待:
- 让FNA =“未在任何地方找到Foo”
- 让FN1 =“未在位置1找到Foo”
- 让FN2 =“未在位置1之后找到Foo”
- 让FF1 =“在位置1找到Foo”
- 让FF2 =“在位置1之后找到Foo”
因此,我们现在只定义那些确实无效的情况,并将其余标记为有效。我们将使用布尔数学来确定所有用例。
- 让FNA = 有效
- 让FN1 = 有效
- 让FN2 = 有效
- 让FF1 = 有效
- 让FF2 = 无效
现在,我们只标记了那些绝对强制返回false的情况,我们可以进行数学运算,看看我们只得到无效/假值的情况。
FNA = FN1和FN2(因此,如果FNA和X = true,则F1和X必须为true,而F2和X也必须为true);
FNA和/或FF1 = true,因此我们知道这4个变量的所有and/or组合= true;这只留下了一个变量需要组合,我们可以很快地看出FF2和任何内容都将始终为false。
因此,换回人类逻辑...看看这个任务变得多么简单?
仅当 在位置1之后找到Foo时才为false
或者,反转布尔(因为要求在有效案例中返回true):
如果从字符串结尾开始扫描直到倒数第二个字符不找到Foo,则字符串有效。
或者,更像计算机思考的方式:
如果从字符串结尾扫描直到第二个到最后一个字符没有找到Foo,则字符串有效
现在,我们无法将其进一步简化。因此,让我们编写这些不同的逻辑部分,并查看它们在实际代码中的表现如何:
using System;
public static class Test
{
public static bool CheckFooTestA(String SearchMe, String[] FindMe)
{
string[] v = SearchMe.Split(FindMe, StringSplitOptions.None);
return (v.Length == 0 || v.Length == 1 && v[0] == String.Empty);
}
public static bool CheckFooTestB(String SearchMe, String[] FindMe)
{
int i = SearchMe.IndexOf(FindMe[0]);
return (i == -1 || i == 0 && SearchMe.LastIndexOf(FindMe[0]) == 0 );
}
public static bool CheckFooTestC(String SearchMe, String[] FindMe)
{
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)
结果和结论
与测试B(使用精简逻辑的索引扫描)相比,测试A(基于视觉上的逻辑进行单词切分/计数)运行时间长了超过220%!
测试C是最佳表现者-只需要一次字符串扫描。它的处理时间少于30%(测试A需要超过340%的时间才能完成与测试C相同的工作量)。
希望有学生看到这篇文章,灯泡能亮起来。你总可以想出让东西“工作”的方法,但理解布尔逻辑以及如何将概念简化到其核心可以对您的工作质量产生重大影响。