在符合条件的所有对象中,找到最接近某个位置的对象。据说是一个非常普遍的问题。当前代码如下所示:
protected Foo FindClosestFooMatching (Vec pos, Func<Foo, bool> matches)
{
float bestDist = float.MaxValue;
Foo bestFoo = null;
foreach (Foo foo in AllFoos()) {
if (matches (foo)) {
float dist = pos.Dist (foo.Center);
if (dist <= bestDist) {
bestDist = dist;
bestFoo = foo;
}
}
}
return bestFoo;
}
我正在考虑几种重构代码的方法,但却找不到一个非常好的代码。如果你有一分钟,请试一试。 : - )
编辑:关于Eric的问题。它是具有欧几里德度量(=快速)的标准3D空间。点聚类和垃圾查询可能性未知。
答案 0 :(得分:1)
return (from f in AllFoos()
where matches(f)
orderby f.Center.Dist(pos)
select f).FirstOrDefault();
或
return AllFoos().Where(matches)
.OrderBy(f => f.Center.Dist(pos))
.FirstOrDefault();
答案 1 :(得分:1)
我认为你的解决方案很快(O(n)),简单&amp;愚蠢(在KISS方面)足以让即使是第一年的CS学生也能理解。
我可以看到的一个问题是你应该接受
的声明float dist;
离开循环。它可能会导致内存碎片。这实际上是一个小问题,因为它只是一个浮动。
另一个会改变
if (dist <= bestDist)
到
if (dist < bestDist)
保存一些变量赋值的CPU周期。但这有一些副作用,因为它会将返回的对象从最后一个最佳匹配更改为第一个最佳匹配。
答案 2 :(得分:0)
现在无法编译,但我希望这能完成这项工作。
var foos = AllFoos().Where (x => matches (x));
return foos.OrderBy (x => pos.Dist (x.Center)).FirstOrDefault ();
这里需要注意的重点是每个操作(包括OrderBy)都需要延迟执行,因此性能应该没问题。
这将是一个更好看的解决方案:
var res = from foo in AllFoos()
where matches (foo)
orderby pos.Dist (foo.Center)
select foo;
return res.FirstOrDefault ();
编辑:我发现this question是此问题的重要来源。由于OrderBy实际上首先命令整个列表,它当然很慢(正如Eric所说,O(n log n)。更好的解决方案是使用Aggregate或Jon Skeet推荐的MinBy扩展(见上面的链接)例如)。
我真的很喜欢MoreLinq解决方案,因为它将代码缩减为:
return World.AllNodes
.Where (x => matches (x.T))
.MinBy (x => pos.DistSq (x.T.Volume.Center));