繁体   English   中英

如何在c ++中将此公式拆分为函数?

[英]How to split this formula into function in c++?

我的代码使用公式来查找机器人声纳传感器发现障碍的位置。 代码如下所示:

 obstacleX = robot.x + robot.sensorReadings.at(i) * cos((robot.deg + i * angleBetweenSensors) * PI / 180);
 obstacleY = robot.y + robot.sensorReadings.at(i) * sin((robot.deg + i * angleBetweenSensors) * PI / 180);

而且我想让它成为一个功能,不要多次重复这个公式(并使其更容易更改)。 我会做这样的功能:

double calculateObstaclePosition(double robotX, double sesnorReading, double robotDegree, int angleBetweenSensors){
      return robotX + sesnorReading * cos((robotDegree + i * angleBetweenSensors) * PI / 180);
}

并经过

obstacleX = calculateObstaclePosition(robot.x, robot.sensorReadings.at(i), robot.deg, angleBetweenSensors);

但X和Y的公式几乎相同,唯一的区别是使用窦,第二个使用cosinus。 我应该创建两个几乎相同的函数,还是可以在一个函数中完成?

可用选项:

(1)使用标志表示您是否要使用余弦或正弦:

double calculateObstaclePosition(double robotC, ..., bool useCos)
{
   double angle = (robotDegree + i * angleBetweenSensors) * PI / 180;
   return robotC + sensorReading * (useCos ? cos(angle) : sin(angle));
}

(2)创建一些2D矢量数据类型并一次返回两个坐标

struct vec2D
{
   double x, y;
};
vec2D calculateObstaclePosition(double robotX, double robotY, ...)
{
   vec2D pos;   
   double angle = (robotDegree + i * angleBetweenSensors) * PI / 180;
   pos.x = robotX + sensorReadingX * cos(angle);
   pos.y = robotY + sensorReadingY * sin(angle);
   return pos;
}

或者使用课程或其他东西。 也可以转换机器人类使用vec2D作为坐标。

(3)模糊一:传递要数值函数的指针。 不安全!

typedef double (*numfuncptr)(double a);
double calculateObstaclePosition(double robotC, ..., numfuncptr trig)
{
   double angle = ...;
   return robotC + sensorReading * trig(angle);
}

(4)不那么模糊,但是C风格,因此不是OOP-esque:传递变量的输出指针。 (再次不安全)

void calculateObstaclePosition(double robotX, double robotY, ..., double* outX, double* outY)
{
   double angle = ...;
   *outX = robotX + ...
   *outY = robotY + ...;
}
struct Robot {
    double x;
    double y;
    double deg; // robotDegree
    double angle; // angleBetweenSensors

    std::vector<double> sensorReadings; // sensorReading
};

std::pair<double, double> calculateObstaclePosition(const Robot &robot, int i)
{
  double obstacleX = robot.x + robot.sensorReadings.at(i) * cos((robot.deg + i * robot.angle) * PI / 180);
  double obstacleY = robot.y + robot.sensorReadings.at(i) * sin((robot.deg + i * robot.angle) * PI / 180);

  return std::make_pair(obstacleX, obstacleY);
}

那个怎么样? 您可以创建一些要传递的类,并将值传递给函数。

声明如下:

std::array<double,2> calculateObstaclePosition(const Robot& robot, int angleBetweenSensors)
{
    return {
        robot.x + robot.sensorReadings.at(i) * cos((robot.deg + i * angleBetweenSensors) * PI / 180) ,
        robot.y + robot.sensorReadings.at(i) * sin((robot.deg + i * angleBetweenSensors) * PI / 180)
    };
}

并称之为

std::array<double,2> obstacle = calculateObstaclePosition(robot,angleBetweenSensors);

它不会阻止你做两次计算,但鉴于参数xysensorReadings.at(i)robot.deg不是很昂贵,你不应该太担心。 如果成本很高,请将它们作为参数传递,而不是传递整个机器人或首先将它们计算为临时变量并在返回语句中使用它。

这个声明的好处是,它可以防止你声明两个不同的函数,并将x和y的值绑定在一起。 如果你喜欢.x.y更好的符号,请使用

 struct Coords{double x, doubley};

而不是std::array

在这个简单函数的情况下,IMHO并不重要,但是在更复杂的函数的情况下,您可以使用函数指针来选择更大函数中使用的特定函数:

// enum to define which arguyment is calculated - X or Y
enum XorYEnum  { X, Y  };

double calculateObstaclePosition(double robotX, double sesnorReading, double robotDegree, int angleBetweenSensors, XorYEnum XorY)
{
    // Select the sin or cos function and assign function pointer
    double (* SinOrCosFunc)(double);
    if (XorY == X)
        SinOrCosFunc = cos;
    else
        SinOrCosFunc = sin;

    // Calculate
    return robotX + sesnorReading * SinOrCosFunc((robotDegree + i * angleBetweenSensors) * PI / 180);
}

如果你只想进行一次计算,你也可以使用函数指针并传入sin或cos。

例如

double calculateObstaclePosition(double (*trigFunction)(double), double robotX, double sesnorReading, double robotDegree, int angleBetweenSensors){
      return robotX + sesnorReading * trigFunction((robotDegree + i * angleBetweenSensors) * PI / 180);
}

然后通过传入sin或cos作为第一个参数来调用它

double posCur = calculateObstaclePosition(sin, param2, param3, param4, param5);

要么

double posCur = calculateObstaclePosition(cos, param2, param3, param4, param5);

我建议使用模板部分特化的解决方案,允许最小化写入。 代码中没有条件,运行时没有条件。 让我们定义一些特殊函数,这些函数可以作为sin或cos依赖于X或Y.

定义要引用的X和Y的枚举:

typedef enum { X, Y } xy_enum;

用于编译时选择的部分专用模板类:

template<xy_enum T>
struct _xcys // x cos y sin
{
    static double f( double t ) { return cos(t); }
};

template<>  // explicit specialization for T = Y
struct _xcys<Y> // x cos y sin
{
    static double f( double t ) { return sin(t); }
};

template<xy_enum T>
struct _xsyc // x sin y cos
{
    static double f( double t ) { return sin(t); }
};

template<>  // explicit specialization for T = Y
struct _xsyc<Y> // x sin y cos
{
    static double f( double t ) { return cos(t); }
};

定义作为sin或cos依赖于X或Y的xcys()因此xcys()对X的作用为cos,对Y作为sin。 并且xsyc()适用于X作为sin,而Y作为cos。

template<xy_enum T> // x sin y cos
double xcys ( double t ) { return _xcys<T>::f(t); }
template<xy_enum T> // x sin y cos
double xsyc ( double t ) { return _xsyc<T>::f(t); }

简单的测试

std::cout << xcys<X>(0)      << " " << xcys<Y>(0) << std::endl;
std::cout << xcys<X>(M_PI/2) << " " << xcys<Y>(M_PI/2) << std::endl;
std::cout << xsyc<X>(0)      << " " << xsyc<Y>(0) << std::endl;
std::cout << xsyc<X>(M_PI/2) << " " << xsyc<Y>(M_PI/2) << std::endl;

结果输出:

 1 0 ~0 1 0 1 1 ~0 

最后你的代码有两个这样的函数:

double calculateObstaclePosition(double robotX, double sesnorReading, double robotDegree, int angleBetweenSensors){
      return robotX + sesnorReading * cos((robotDegree + i * angleBetweenSensors) * PI / 180);
}
double calculateObstaclePosition(double robotY, double sesnorReading, double robotDegree, int angleBetweenSensors){
      return robotY + sesnorReading * sin((robotDegree + i * angleBetweenSensors) * PI / 180);
}

可以使用一个模板函数重写:

template< xy_enum T >
double calculateObstaclePosition(double robotXY, double sesnorReading, double robotDegree, int angleBetweenSensors){
      return robotXY + sesnorReading * xcys<T>((robotDegree + i * angleBetweenSensors) * PI / 180);
}

并为x和y调用单个函数两次:

obstacleX = calculateObstaclePosition<X>(robot.x, robot.sensorReadings.at(i), robot.deg, angleBetweenSensors);
obstacleY = calculateObstaclePosition<Y>(robot.y, robot.sensorReadings.at(i), robot.deg, angleBetweenSensors);

计算障碍物X和障碍物的公式有一些重复。 您需要先简化它们。 之后,您将在实现中看到非常少的代码重复。 以下是我的代码示例:

template <typename T>
std::vector<T> cal(Location<T> &robot, T angleBetweenSensors) {
    const size_t N = robot.sensorReadings.size();
    std::vector<T> results(2 * N, 0);

    // Precompute this value to improve performance.
    constexpr T angleScale = M_PI / 180;
    for (size_t i = 0; i < N; ++i) {
        // Main code
        T alpha = (robot.deg + i * angleBetweenSensors) * angleScale;
        T sensorVal = robot.sensorReadings.at(i);
        T obstacleX = robot.x + sensorVal * cos(alpha);
        T obstacleY = robot.y + sensorVal * sin(alpha);

        // Use obstacleX and obstacleY here
        results[2 * i] = obstacleX;
        results[2 * i + 1] = obstacleY;
    }
    return results;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM