【问题标题】:How to split this formula into function in c++?如何将此公式拆分为c ++中的函数?
【发布时间】:2016-03-06 19:08:29
【问题描述】:

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

 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 的公式几乎相同,唯一的区别是一个使用 sinus,第二个使用 cosinus。然后我应该创建两个几乎相同的功能还是可以合二为一?

【问题讨论】:

    标签: c++ function oop


    【解决方案1】:

    可用选项:

    (1) 使用一个标志来表示是使用余弦还是正弦:

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

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

    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 风格:传递变量的输出指针。 (再次不安全)

    void calculateObstaclePosition(double robotX, double robotY, ..., double* outX, double* outY)
    {
       double angle = ...;
       *outX = robotX + ...
       *outY = robotY + ...;
    }
    

    【讨论】:

    • 哪一个更好,更符合oop规则?发送布尔值是否符合 oop 规则?
    • 其他潜在选择:(a) 使用 std::pair 代替自定义结构。 (b) 传递一个函数指针(尽管在这种情况下这不太可能非常有效,并且会变得不必要地复杂)
    • 恕我直言,使用 #1 的指令开销使得调用它不值得。我会选择#2,除了重命名结构。名称 vec2Dstd::vector 太接近。我会称之为PointCoordinate。通常,物理向量具有大小和方向。
    • @Thomas Matthews 是的昔日仪式
    • @Mateusz 可能现在 C14 编译器已经足够聪明了,否则为什么还要麻烦一些巨大的 std 类呢?
    【解决方案2】:
    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);
    }
    

    那怎么样?您可以创建一些类来传递和获取函数的值。

    【讨论】:

      【解决方案3】:

      这样声明:

      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 的评估成本不是很高,您也不应该担心这一点很多。如果代价高昂,请像现在一样将它们作为参数传递,而不是传递整个机器人或首先将它们评估为临时变量并在您的 return 语句中使用它。

      这个声明的好处是,它可以防止你声明两个不同的函数并将 x 和 y 的值联系在一起。如果您更喜欢 .x.y 的表示法,请使用

       struct Coords{double x, doubley};
      

      而不是std::array

      【讨论】:

      • 请注意,这允许您只计算一次 cos 和 sin 的参数,感觉很好
      【解决方案4】:

      对于这个简单的函数,恕我直言并不重要,但对于更复杂的函数,您可以使用函数指针来选择更大函数中使用的特定函数:

      // 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);
      }
      

      【讨论】:

      • 为什么不预先计算参数并使用三元运算符?
      【解决方案5】:

      如果您只想计算一次,也可以使用function pointer 并传入 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);
      

      【讨论】:

        【解决方案6】:

        我建议使用模板部分专业化的解决方案,这样可以最大限度地减少写作。代码中没有条件,运行时也没有条件。让我们定义依赖于 X 或 Y 作为 sin 或 cos 工作的特殊函数。

        为 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); }
        };
        

        定义依赖于 X 或 Y 作为 sin 或 cos 工作的函数。所以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);
        

        【讨论】:

          【解决方案7】:

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

          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;
          }
          

          【讨论】:

            猜你喜欢
            • 2013-01-14
            • 1970-01-01
            • 1970-01-01
            • 2018-12-16
            • 1970-01-01
            • 2020-09-13
            • 2022-12-04
            • 1970-01-01
            相关资源
            最近更新 更多