我有两个位置,我的玩家位置(2D),我循环在一个实体矢量,每个都有一个位置(2D),我希望我的功能返回到我所在位置的最近距离,但我不喜欢我知道怎么做因为我卡住了,在c#我可以使用linq,但我正在学习c ++我的功能看起来像这样:
const Entity GetNearestEntity()
{
for (Entity i : fb.Entities)
{
double xDiff = (double)abs(fb.PlayerLocation.X - i.X);
double yDiff = (double)abs(fb.PlayerLocation.Y - i.Y);
//Stuck here..
//How can i get the closest object out of my vector<Entity> collection?
}
}
帮助表示感谢!
答案 0 :(得分:12)
既然你是C ++的新手(正如你所说的那样),我想给你一个很好的选择,需要一点不同的思考,但是一旦你习惯了它,你就会喜欢它
基本理念
您正在寻找一个元素(或者您希望稍后可能对该元素进行修改),这是与某种比较方法相关的最小元素。 C ++允许您选择如何比较元素,分别针对特定查询(在这种情况下,不要将比较视为明确定义的&#34;小于&#34; /&#34;大于&#34;运算符,但它&# 39;类似的概念)。
您可以在本地为此特定方案定义此类比较。比较方法可以作为独立函数实现,作为函子(实现所谓的&#34;调用操作符&#34;的函数对象)或作为lambda函数,您应该更喜欢。
Lambda函数语法
Lambda函数是匿名函数,通常在您按字母顺序编写的相同位置使用。 lambda函数语法是你必须习惯的东西,但是一旦你做了,它就是一个强大的东西!
[](Entity a, Entity b) { return a.X < b.X; }
这是一个lambda函数,它接受两个Entity
个实例并简单地比较它们的X坐标。当然这不是你想要的,但我想首先向你展示语法。
现在我们要实现一个比较函数,它比较相对于原点的坐标(你的玩家位置),所以这是从函数的外部传递的东西,但不作为参数(因为比较函数只能将两个值进行比较)。这是通过捕获一些上下文变量来完成的。看看这里:
int captured = ...
...
[captured](Entity a, Entity b) { return a.X + captured < b.X + captured; }
仍然没有任何意义,它显示了如何在lambda函数中引用在lambda函数之外定义的变量。
针对您的特定问题的Lambda函数
现在我们可以正确编写比较方法:
[fb](Entity a, Entity b) {
double ax = fb.PlayerLocation.X - a.X;
double ay = fb.PlayerLocation.Y - a.Y;
double a = ax * ax + ay * ay; // we can save the sqrt()
double bx = fb.PlayerLocation.X - b.X;
double by = fb.PlayerLocation.Y - b.Y;
double b = bx * bx + by * by; // we can save the sqrt()
return a < b;
}
所以我们捕获fb
,我们计算两个实体a
和b
的相对坐标,计算长度的平方(这样我们可以保存sqrt)并比较这些距离。代码看起来有点臃肿;我们稍后会对此进行改进。
在STL算法中使用Lambda函数
一旦你理解了如何编写这样的比较函数,最后一步变得微不足道,因为STL provides a lot of algorithms准备使用它可以使用这些函数:
Entity closest = *std::min_element(fb.Entities.begin(), fb.Entities.end(),
[fb](Entity a, Entity b) {
double ax = fb.PlayerLocation.X - a.X;
double ay = fb.PlayerLocation.Y - a.Y;
double a = ax * ax + ay * ay; // we can save the sqrt()
double bx = fb.PlayerLocation.X - b.X;
double by = fb.PlayerLocation.Y - b.Y;
double b = bx * bx + by * by; // we can save the sqrt()
return a < b;
}
);
正如您所看到的,我只是将lambda函数直接传递给另一个函数调用(没有先定义具有特定名称的函数,因为它是匿名函数 )。此函数调用是std::min_element
,它找到两个迭代器之间的最小元素(如果要在整个容器中搜索,请使用begin / end迭代器对)。它返回另一个迭代器。使用*
前缀运算符,可以访问迭代器指向的元素。
请注意,一旦将其存储为Entity
值,它就会复制,因此修改不再直接写入向量,而是写入本地副本< / em>的。要避免这种情况,请使用Entity&
,它是向量中元素的(可修改的)引用,您的函数可以毫无问题地返回(只要引用的值在函数外部有效,它就在您的函数中情况)。
<强>改进强>
如果你写一个比较两个实体的距离函数,这就变得更简单了:(注意缺少abs
,这是因为无论如何我们将对数值求平方,任何负号都会消失)
double distance(Entity p, Entity q) {
double delta_x = p.X - q.X;
double delta_y = p.Y - q.Y;
return sqrt(delta_x * delta_x + delta_y * delta_y);
}
或者,再次保存sqrt:
double distanceSquare(Entity p, Entity q) {
double delta_x = p.X - q.X;
double delta_y = p.Y - q.Y;
return (delta_x * delta_x + delta_y * delta_y);
}
所以代码变成:
Entity closest = *std::min_element(fb.Entities.begin(), fb.Entities.end(),
[fb](Entity a, Entity b) {
return distanceSquare(a, fb.PlayerLocation) <
distanceSquare(b, fb.PlayerLocation);
}
);
另一个改进是通过(不可修改的)引用传递变量,而不是按值传递它们。这意味着不需要复制变量。将代码放入您在问题中编写的方法中,代码变为(应用了引用的调用概念并返回可修改的引用):
double distanceSquare(const Entity & p, const Entity & q) {
double delta_x = p.X - q.X;
double delta_y = p.Y - q.Y;
return (delta_x * delta_x + delta_y * delta_y);
}
Entity & GetNearestEntity()
{
return *std::min_element(fb.Entities.begin(), fb.Entities.end(),
[fb](const Entity & a, const Entity & b) {
return distanceSquare(a, fb.PlayerLocation) <
distanceSquare(b, fb.PlayerLocation);
}
);
}
(注意嵌套的return语句。内部的一个是lambda函数的一部分并返回比较逻辑的结果。外部的返回最终结果,因此具有最小距离的实体。)
最后,它看起来更干净(至少在我看来)。一旦你理解了这个概念,你就会看到它的美丽。 :)
我将向您展示的最终改进在Andrew Durward对此答案的评论中提到:我们现在已经编写了Lambda函数,每次调用fb
都会复制一次GetNearestEntity
1}}因为自上次调用以来它可能已经改变了。我们可以通过通过引用捕获来避免这种复制操作,这与通过引用调用的概念相同,但是对于捕获的变量。只需在捕获表达式中的变量名前面写&
:
//...
[&fb](const Entity & a, const Entity & b) {
return distanceSquare(a, fb.PlayerLocation) <
distanceSquare(b, fb.PlayerLocation);
}
//...
捕获语法在开始时可能看起来有点奇怪,但它提供了对中的变量的强大控制,您通过引用或捕获的封闭上下文中的变量按价值:
来源:http://www.cprogramming.com/c++11/c++11-lambda-closures.html
[] Capture nothing (or, a scorched earth strategy?) [&] Capture any referenced variable by reference [=] Capture any referenced variable by making a copy [=, &foo] Capture any referenced variable by making a copy, but capture variable foo by reference [bar] Capture bar by making a copy; don't copy anything else [this] Capture the this pointer of the enclosing class
如果您想了解有关在lambda函数中捕获变量的更多信息,请阅读here或google for "C++11 lambda capture syntax"。
演示此代码:http://ideone.com/vKAFmx
答案 1 :(得分:4)
通过跟踪最接近你的元素的索引可能更容易做到,这在'for'循环中会更容易:
Entity GetNearestEntity()
{
int closestEnt = 0;
double smallestDist = -1.0;
for (int i = 0; i < Entities.length(); i++)
{
double xDiff = (double)abs(fb.PlayerLocation.X - Entities[i].X);
double yDiff = (double)abs(fb.PlayerLocation.Y - Entities[i].Y);
double totalDist = sqrt(pow(xDiff, 2) + pow(yDiff, 2));
if ((totalDist < smallestDist) || (smallestDist == -1.0))
{
closestEnt = i;
smallestDist = totalDist;
}
}
return Entities[closestEnt];
}
这可能无法编译,因为我玩过C ++已经有一段时间了,而且我在知道这是否是正确的方法来做平直根和权力。但是,它具有仅跟踪double和int而不是对象的好处。
答案 2 :(得分:3)
您需要跟踪具有最小正方形添加的实体。像这样:
...
for (Entity i : fb.Entities)
{
double xDiff = (double)abs(fb.PlayerLocation.X - i.X);
double yDiff = (double)abs(fb.PlayerLocation.Y - i.Y);
// this is not the actual distance, for that you would need sqrt(). But
// it is enough to know if it is bigger or smaller.
dist = xDiff*xDiff + yDiff*yDiff;
closestEnt = ( dist < smallestDist ? i : closestEnt );
smallestDist = ( dist < smallestDist ? dist : smallestDist );
}
...
dist
和smallestDist
是数字而closestEnt
是Entity
,不知道你的类型。
将smallestDist
初始化为您选择的数字类型的最大可能数字。例如long smallestDist = 0xffffffff;
答案 3 :(得分:0)
int GetNearestEntity()
{
int closestEnt = 0;
int smallestDist = -1;
int i = 0;
if (fb.Entities.size() > 0)
{
for (Entity entity : fb.Entities)
{
double xDiff = (double)abs(fb.PlayerLocation.X - entity.X);
double yDiff = (double)abs(fb.PlayerLocation.Y - entity.Y);
double totalDist = sqrt(pow(xDiff,2) + pow(yDiff,2));
if ( (totalDist < smallestDist) || (smallestDist = -1))
{
closestEnt = i;
smallestDist = totalDist;
}
i++;
}
return closestEnt;
}
return 0;
}