缘起
王国维歪解宋词,用它表达事业的三重境界。老谭要谈谈C#中编写循环的三种方式,也来个东施效颦,不过是用唐诗来表达。王国维选择宋词,非常贴切,也给宋词增色不少。老谭没这水平,只是乱选一气。
我们的例子是报表查询:根据给定的关键字,查询符合条件的报表,即报表名字是否和关键字相匹配。关键字的结构类似于Google查询:关键字中包含多个单词,单词由空格或加号隔开,空格表示或者,加号表示而且。
例如,如果关键字是 二厂 三厂,那么名字中含有二厂或三厂的报表都能匹配上,如果关键字是化工+预测,那么名字中既含有化工,又含有预测的报表,才能匹配上。
我们要实现的函数是 bool IsMatched(string reportName, string keyword)。
方式1:我舞影零乱
我们可以采用最为传统的for循环实现这个函数:
private bool IsMatched(string reportName, string keyword)
{
string[] orItems = keyword.Split(' ');
int orItemCount = orItems.Length;
for (int i = 0; i < orItemCount; i ++)
{
string[] andItems = orItems[i].Split('+');
bool allMatched = true;
int andItemCount = andItems.Length;
for (int j = 0; allMatched && j < andItemCount; j++)
{
if (!reportName.Contains(andItem[j]))
{
allMatched = false;
break;
}
}
if (allMatched)
{
return true;
}
}
return false;
}
在这种方式下,为了实现循环,需要做相当多的额外工作:
- 定义循环控制变量,如i、j;
- 初始化循环控制变量,如设置为0;
- 定义循环控制变量的结束条件,如不超过项目个数;
- 每一个循环步骤中调整循环控制变量的值,如加1;
- 根据循环控制变量获取到要操作的对象等。
这些“额外”工作,遮蔽了要实现的逻辑,非常容易导致错误,如循环控制变量用混。
方式2:竹露滴清响
为了解决方式1中的诸多问题,C#添加了foreach关键字,支持新的循环实现方式。
private bool IsMatched(string reportName, string keyword)
{
foreach (string orItem in keyword.Split(' '))
{
bool allMatched = true;
foreach (var andItem in orItem.Split('+'))
{
if (!reportName.Contains(andItem))
{
allMatched = false;
break;
}
}
if (allMatched)
{
return true;
}
}
return false;
}
这种方式克服了方式1的缺点,循环控制不再凌乱,程序逻辑集中于处理循环变量上。这些变量,从集合中一个个涌出,像滴着清响的竹露般利索。
方式3:循环不可寻
C#采纳了函数式编程的思路,和我们熟悉的命令式编程方式混合在一起,变成了一种混合式编程语言。
private bool IsMatched(string reportName, string keyword)
{
return keyword.Split(' ').Any(orItem => orItem.Split('+').All(reportName.Contains));
}
这时,循环杳无踪影。
转换思路
利用C#操作集合时,要做好思路的转换:从命令式(imperative)到函数式(functional)的转换。这是一个大题目,这里只提本例子中用到的两点:
- 明确做什么,而不是怎么做。如检查集合中全部元素符合某个条件,用All(),检查集合中是否有一个元素符合某个条件,用Any()。具体怎么做,或者利用开发平台提供的支持,或者自己实现;
- 将函数作为参数。如Any、All都是函数,同时也是参数。显然,C#中是用λ-表达式做到这一点的。