简体   繁体   English

将数学函数作为方法的输入参数C ++传递

[英]Passing a math function as method's input parameter C++

I am trying to write a general integral function and I would like to implement it in such a way so that it can accept any mathematical function. 我正在尝试编写一个通用的积分函数,我想以这样的方式实现它,以便它可以接受任何数学函数。 That is, I would like to pass the math function as an input parameter. 也就是说,我想将数学函数作为输入参数传递。 In pseudo-code: simpson_int(x*x) . 在伪代码中: simpson_int(x*x) I've heard of the function template in <functional> but I don't really have experience with templates in C++. 我听说过<functional>中的function模板,但我并不熟悉C ++中的模板。

There are some solutions that comes in my mind (and this is my approach at the problem, for sure there are more solution, and maybe what I'm pointing out is not the best), that consider the fact that you need to call the argument function more than once in the Simpson implementation (thus you need a "callable" argument ): 我脑子里有一些解决方案(这是我解决问题的方法,肯定有更多解决方案,也许我指出的不是最好的),考虑到你需要调用在Simpson实现中不止一次参数函数(因此你需要一个“可调用的”参数 ):

Function pointer 功能指针

Function pointers (more C than C++), where you declare with two arguments: the first one will be the pointer to a function with the specified types, while the second is the argument for your function. 函数指针(比C ++更多),用两个参数声明:第一个是指向具有指定类型的函数的指针,而第二个是函数的参数。 Lets make an example: 让我们举一个例子:

#include <iostream>

double power2(double x) {
  return x * x;
}

double simspon(double (*f)(double), double x) {
  return f(x);
}

int main() {
  std::cout << simspon(power2, 2);
  return 0;
}

In this case I have used no templates for reaching the result. 在这种情况下,我没有使用模板来达到结果。 But this will not take any function as first argument, but only a function that has as argument a double and returns a double. 但是这不会将任何函数作为第一个参数,而只是一个函数,其参数为double并返回double。

I think that most of c++ programmer will suggest you to avoid this method. 我认为大多数c ++程序员会建议你避免使用这种方法。

Function pointer and templates 函数指针和模板

So you maybe want to expand the previous example using templates and making it more general. 因此,您可能希望使用模板扩展前面的示例并使其更加通用。 It is quite simple to redefine the function to accept a template (an abstract type) that you actually specify only when you use it in your code: 重新定义函数以接受仅在代码中使用时实际指定的模板(抽象类型)非常简单:

#include <iostream>

double power2(double x) {
  return x * x;
}

int power2int(int x) {
  return x * x;
}

template <class T, class P>
P simspon(T (*f)(P), P x) {
  return f(x);
}

int main() {
  std::cout << simspon<double, double>(power2, 2.0);
  std::cout << simspon<int, int>(power2int, 2);
  return 0;
}

T and P are two templates: the first one is used for describing the returned value of the function pointer, while the second specify the argument of the function pointer, and the returned value of simpson .So when you are writing template <class T, classP> you are actually informing the compiler that that you are using T and P as placeholder for different type. TP是两个模板:第一个用于描述函数指针的返回值,第二个用于指定函数指针的参数,以及simpson .So的返回值,当您编写template <class T, classP>实际上,您正在通知编译器您正在使用T和P作为不同类型的占位符。 You will actually declare the type that you want later on, when you will call the function in the main . 实际上,当您将在main调用该函数时,您将实际声明您想要的类型。 This is not good code but I'm building the path to understand templates. 这不是好代码,但我正在构建理解模板的路径。 Also, you specify the type of your argument function when you actually call simpson , with the < > . 另外,在实际调用simpson ,使用< > 指定参数函数的类型

( Disclaimer: you should consider to use template <typename T ...> instead of class. But I'm used with the old class and there are situation in which typename cannot be used, there are a lot of questions on SO that dive into this.) 免责声明:您应该考虑使用template <typename T ...>而不是类,但我与老使用。 class和有情况,即typename不能用,有很多的问题上,这样的潜水进入这个。)

Using std::function 使用std::function

Instead of using a function pointer as argument you may want to create a variable that stores your function to be passed as argument of simpson . 您可能希望创建一个变量来存储您要作为simpson参数传递的函数,而不是使用函数指针作为参数。 This bring several advantages, because they are actually an object inside your code that have some predictable behavior in some unwanted circumstances (for example, in case of a null function pointer you have to check the pointer itself and handle it, in case of std::function if there is no callable pointer it throws std::bad_function_call error) 这带来了几个优点,因为它们实际上是代码中的一个对象,在某些不需要的情况下具有一些可预测的行为(例如,在null函数指针的情况下,你必须检查指针本身并处理它,如果是std::function如果没有可调用指针则抛出std::bad_function_call错误)

Here an example, and it uses again templates, as before: 这是一个例子,它再次使用模板,如前所述:

#include <iostream>
#include <functional>

double power2(double x) {
  return x * x;
}

int power2int(int x) {
  return x * x;
}

template <class T, class P>
P simspon(std::function<T(P)> f, P x) {
  return f(x);
}

int main() {
  std::function<double(double)> p_power2 = power2; 
  std::cout << simspon<double, double>(p_power2, 2.0);

  std::function<double(double)> p_power2int = power2int; 
  std::cout << simspon<int, int>(power2int, 2);
  return 0;
}

Using lambdas 使用lambdas

lambdas are closure and in your case (if you can use the standard C++14) can be used alongside the auto keyword to achieve quite a general behavior, without the explicit use of templates. lambda是闭包的,在你的情况下(如果你可以使用标准的C ++ 14)可以与auto关键字一起使用,以实现相当普遍的行为,而无需明确使用模板。 The closure are also able to capture part/the whole context, check the reference for this. 闭包还能够捕获部分/整个上下文, 检查参考

Let's see an example, in which I create two lambdas that receive different arguments and a simpson function that is quite general (actually it is not, is the compiler that defines different functions with respect to the call that you do). 让我们看一个例子,其中我创建两个接收不同参数的lambda和一个非常通用的simpson函数(实际上它不是,编译器定义了与你所做的调用相关的不同函数)。

#include <iostream>

auto lambda = [](auto x) { return x * x ; };
auto lambda_2 = [] (int x) { return x + 10; };

auto simpson(auto f, auto x) {
  return f(x);
}

int main() {
  std::cout << simpson(lambda, 2.0);
  std::cout << simpson(lambda_2, 1);
  return 0;
}

You need to compile it with the -std=c++14 flag. 您需要使用-std=c++14标志进行编译。 There are tons of advise that comes in my mind to suggest you to avoid to implement your code in this way, remember that it has only some illustrative purposes (I've more than exaggerated with the auto keyword). 我想到了很多建议,建议你避免以这种方式实现你的代码,请记住它只有一些说明性的目的(我不仅仅夸大了auto关键字)。

Function objects (the Problem class) 函数对象(问题类)

Maybe an improvement for your case is to write a general class for the mathematical functions to integrate and pass the object to your function. 也许对你的情况进行改进的是为数学函数编写一个通用类来集成并将对象传递给你的函数。 This bring several advantages: you may want to save some of the integrative result inside your function or even write the stream operator to pretty print your problem. 这带来了几个优点:您可能希望在函数中保存一些综合结果,甚至可以编写流操作符来打印您的问题。 This is the solution employed typically by mathematical libraries. 这是数学图书馆通常采用的解决方案。

In this extremely simple case, we have a class that is a problem. 在这个非常简单的情况下,我们有一个问题的类。 When you create a new instance for this class, a std::function is passed to the constructor and stored inside the class. 为此类创建新实例时, std::function将传递给构造函数并存储在类中。 The instance of the class is the argument for your simpson : 类的实例是你的simpson的参数:

#include <iostream>
#include <functional>

template <class T, class P>
class Problem {
public:
  // Attributes
  std::function<T(P)> _f;

  // Constructor
  Problem(std::function<T(P)> f) : _f(f) {};

  // Making the object callable
  P operator()(P x) { return _f(x); }
};

template <class T, class P>
P simspon(Problem<T, P> p, P x) {
  return p(x);
}

int main() {
  Problem<double, double> prb([](double x) { return x * x; });
  std::cout << simspon<double, double>(prb, 2);
  return 0;
}

Use std::function , like this for example: 使用std::function ,例如:

#include <iostream>     // std::cout
#include <functional>   // std::function

int main()
{
    std::function<double(double)> simpson_int =([](double x) { return x * x; };
    std::cout << "simpson_int(4): " << simpson_int(4) << '\n';
    return 0;
}

which outputs: 哪个输出:

simpson_int(4): 16 simpson_int(4):16

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

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