由于无聊透顶的童年生活和无趣的学生时代的阴影,让我对一些逻辑小问题充满了兴趣。就像下面这个题目:
有ABCD四个人,A说:B偷吃了苹果,B说:是D吃的,C说:不是我,D说:B说谎。现在已知条件为:这四个人中,只有一个人说的是真话。请问:偷吃了苹果的人到底是谁?
其实不用说也知道,这题肯定是有瑕疵的,它的问题是“偷吃了苹果的到底是谁?”,这个谁是否在暗示我们偷吃苹果的只有一个人,会不会有多人瓜分的情况?这个题目直接限死了一人偷吃苹果的隐含条件了(稍后我们可以证明)。
兴趣归兴趣,我解答这类问题实在是不太聪明,每次都容易把自己绕进去。包括现在的写程序有时也是一样,如果处理的逻辑复杂,就把自己绕进去了。因此,我对Linq这种语法糖特别有好感。
“那么这个问题用Linq怎么解决?”我突然提起了兴趣。
马上,就我就反应过来,这题主要包括两个条件,一个是讲真话,一个是偷吃苹果;所以我可以建一个类,就叫它People吧:
class People { //代表是否偷吃 public bool Eat { get; set; } //代表是否讲真话 public bool SayTruth { get; set; } }
这四个人中,所有的可能性只有四种,即:说真话,偷吃;说假话,偷吃;说真话,没偷吃;说假话,没偷吃。所以就有:
List<People> peopleList = new List<People> { new People{Eat = true,SayTruth = true}, new People{Eat = true,SayTruth = false}, new People{Eat = false,SayTruth = true}, new People{Eat = false,SayTruth = false}, }
四个人A,B,C,D只有这四种情况,而且有两个很明显的限制条件,即:有且仅有一个人说真话;有且仅有一个人偷吃苹果:
private static bool confirmOnlyOne(List<People> allPeople) { if (allPeople.Where(x => x.SayTruth == true).Count() == 1 && allPeople.Where(x => x.Eat == true).Count() == 1) return true; else return false; }
接下来就是主要的Linq语句了,根据题中的条件,可以很容易地写出如下拙劣的Linq语句并输出结果:
var query = from A in peopleList from B in peopleList from C in peopleList from D in peopleList where confirmOnlyOne(new List<People> { A, B, C, D }) && A.SayTruth == B.Eat && D.Eat == B.SayTruth && !C.Eat == C.SayTruth && D.SayTruth == !B.SayTruth select new { A,B,C,D }; foreach (var item in query) { Console.WriteLine("A: Eat-{0},SayTruth-{1}", item.A.Eat, item.A.SayTruth); Console.WriteLine("B: Eat-{0},SayTruth-{1}", item.B.Eat, item.B.SayTruth); Console.WriteLine("C: Eat-{0},SayTruth-{1}", item.C.Eat, item.C.SayTruth); Console.WriteLine("D: Eat-{0},SayTruth-{1}", item.D.Eat, item.D.SayTruth); }
其结果为:
很明显,偷吃苹果的是C,说真话的是D!Kid's Stuff!!
PS: 那么,如果我们不顾“只有一个人偷吃苹果”这一隐藏条件的话会怎么样呢?我试了一下,果然有多条结果,所以……该出题人已经假定了只有一个人偷吃了苹果,他应该做的是把话说得更明白一些。