Reusable math routine with C ++

xiaoxiao2021-03-06  21

Typically, the algorithm that needs to be invoked by the user is difficult to achieve reuse. The key to realizing reuse is to find an effective way for encapsulating user-defined code.

introduction

"Code Reuse" is one of the sacred objectives pursued by software engineering. One of the main aspects of the programming method of object-oriented (Object-Oriented, OO) is also for code reuse, which can be seen from any books that describe OO programming. However, in practical applications, use C OO languages ​​to achieve code reuse than we imagined. In fact, as a authors say, because C programmers generally tend to create their own container classes, "C has a great hindrance to scientific computing software."

In this article, I show how to create reusable mathematics routines with C languages. Relative to OO, the method I use is more dependent on generic programming. For the convenience of discussion, I use a widely used estimate algorithm - NeWton-Raphson algorithm is used as an example. The Newton-Raphson algorithm must call a user-defined function. This article first gives a typical (unsatisfactory) package method for user-defined functions, and then puts forward a more satisfying package based on template and operator overloading.

NEWTON-RAPHSON algorithm

In scientific computing and financial engineering areas, many value algorithms are common (at least in theory), can be widely used to solve a class of problems. An example of a familiar thing is the Newton-Raphson routine, which can be used to find numerical solutions of equation f (x) = 0. The standard mathematical expression F (x) represents F is a function of the variable X, which is typically expressed in F (X, A, B, ...) = 0, and F is defined as a function of more than one variable. In this case, the Newton-RapHSON algorithm is attempted to fix the variables other than X and as a parameter, and the numerical solution about the variable X is found.

Since the Newton-Raphson algorithm needs to know the exact expression of the solved function, its traditional implementation method is to embed the code directly into the client application. This makes it repeatedly in the client program after the implementation code of the algorithm is repeated in the client program after a small amount of modifications of different solving functions.

Like many other mathematical routines, the specific implementation of the Newton-Raphson algorithm should be independent of a particular user. Also, repeated encoding should be avoided in any case. We naturally think that the class routine is implemented as a library function so that the client can call them directly. However, this implementation will inevitably involve how to encapsulate user-defined functions (Newton-Raphson routines need to call this function) into a form that can be passed as parameters. The following sections describe a user-defined function package method for many problems.

Usually implementation paths - function pointers

The current task is to implement the Newton-Raphson algorithm as a library routine. The client can directly call the routine to obtain an equation such as f (x, a, b, ..) = 0 to obtain the X Numerical Solution. The key to the problem is that the implementation of the algorithm must be used (can call) F (X, a, b, ...) forms of general functions, and the specific definition of the function is provided by the user in the future, and can only be run That is submitted to the library. For C and C programmers, a natural possible way is to pass the function pointer as a parameter to the library routine:

Typedef double (* p2f) (double);

Double Newtonraphson (p2f func_of_x, double x_init,

) {

...

// Vire to call functions

Double y = func_of_x (x_init);

...

}

The library routine is working very well, but this is just a function of just having only one parameter. In C , programmers can overload library functions, define one routine for user-defined functions with different parameters. But this will cause a lot of repetitions in the library code, and more bad is that you don't know how many such library routines need to be defined. Another idea is to use optional parameters, as shown in the following statement:

Typedef Double (* p2f) (double, ...);

This seems to be a discussion of this problem. But fortunate is not fortunate, C does not allow the use of optional parameters as expected by the above code. Since pointer to the function must accurately know the type and number of function parameters, the TypeDef defined function pointer can only match the function of a Double type parameter and keep up the VARARGS of the C style, and cannot be used to contain more Multi-specify the function of type parameters.

Of course, there are other ways to deliver multi-parameter functions, such as using a function housing. However, this method is not clear about how to do it except for the author, in addition to the global variable.

In order to simplify it, it is necessary to use a set of constructs containing a certain parameter, which defines a complex user function and provides a way for the library routine to call this function by passing a single parameter by passing a single parameter. This will be an object - a purely simple object. Therefore, I define a class for General Functions f (x, A, b, ...) and name it Funcobj. (To simplify the narrative, from now on, the number of parameters is fixed to 3.)

Class funcobj {

Private:

Double_A;

INT_B;

PUBLIC:

Funcobj (Double A_IN, INT B_IN);

// Define user-defined functions in the form of x, a, b

Double thefunc (double x_init);

}

You may try to call this routine by passing a pointer to the Thefunc member function pointing to the Funcobj object to the previously defined library routine. But this method cannot work, at least for two reasons. First, in the representation of the member function contains a class name, pointing to its pointer cannot be used to need a pointer to a normal function. Second, pointers pointing to member functions must be accessed by an object instance of such a class. I will solve these two problems in the next part. (Need to note is that thefunc is defined as a Static type. You cannot really solve the problem. variable.)

Use a pointer to the member function

As discussed above, the library interface must be modified to access by pointers to the member function. And the library interface should define a function template so that it is not limited to a particular class.

Template

Double Newtonraphson (T & Func, Double (T :: * func_of_x) (Double),

Double X_INIT,) {

...

// Through the object (reference) and pointer to the member function

// Call the member function

Double y = (func. * func_of_x) (x_init);

...

}

This code can work normally, but its grammar is a bit difficult to understand.

Creating a type definition of a pointer to a member function is a valid way to simplify and more readable, like the type definition of a pointer that is previously directed to a simple function. In other words, creating type definitions with parameterized types will make the program more easy to understand, as shown below:

Template

Typedef Double (T :: * p2mf) (double);

If the above code conforms to the C syntax, the type of P2MF is a pointer to the member function of class T, which requires a Double type parameter and returns a Double type value. Unfortunately, C does not support the type definition of the template. According to the practice of computer science, the final solution is to introduce another level redirection. In this example, the above type definition becomes legal:

Template struct p2mfhelper {

Typedef Double (T :: * p2mf) (double);

}

The above code demonstrates a strange but very interesting Templete and TypeDef usage. Now, I can redefine the library function as follows:

Template

Double Newtonraphson (T & Func,

P2MFHELPER :: P2MF func_of_x,

Double X_INIT,) {

...

Double y =

(func. * func_of_x) (x_init);

...

}

Note Func. * Func_of_x Both brackets on both sides The correct compilation and run of the code is required because the next function calls than the operator. * Have a higher priority. At the same time, Helper class and library functions can also be merged into a single template class and can achieve the same purpose. However, designing the Newton-Raphson routine into a class is not a good way. Needless to say, this will cause grammar to become slightly complicated.

The library routine can now be applied to a user-defined function with a number of different parameters, as long as the client programmer defines a Funcobj class to encapsulate a function of each type. However, this method also has two small problems. First, you can't transfer the pointer to the NEWTON-RAPHSON process to the Newton-Raphson. Second, a pointer to a member function will make the program be unachable and uneconomical. Let me discuss how to overcome these two shortcomings.

Use functions

Function object is a class that overloads a function call operator (). Therefore, the operator () overloaded by the function object can be used instead of the function call operator. Here is the Funcobj I redefined:

Class funcobj {

PUBLIC:

// Operator overload

Double Operator () (double x_init) {

// Operator overload realizes code

/ / Implement user-defined functions defined in X, A, and B

}

}

Now, the library routine can use the function object to make a simple definition:

Template

Double Newtonraphson (T & Func,

Double X_INIT,

) {

...

// Call the member function or () operator of the function object instance

Double y = func (x_init);

...

}

As can be seen from the code, only a parameterized variable is transmitted only to the NewtonRaphson library routine. This type can be replaced with an object reference or pointing to a pointer to a normal function. And, "function call" inside the library routine becomes more simple. As an example, look at how to use this universal library routine to solve an unsubalytic complex equation X3 2EX 7 = 0. The function object is defined as follows:

Class funcobj {

Private:

Double_A;

Double _b;

PUBLIC:

Funcobj (Double A_IN, Double B_IN):

_A (a_in), _b (b_in) {};

// Overload operator

Double Operator () (double x_in) {

Return (x_in * x_in * x_in

_A * EXP (X_IN) _B);

}

Double Solve (double x_in) {

// Call Universal Library Ride Return NewtonRaphson (* this, x_in,

Other_arguments;

}

}

In the main program, just simply call the library routine:

Void main () {

Funcobj FO (2.0, 7.0);

/ / Indirect call Newton-Raphson library routine

Double Solution_1 = fo.solve (-4.0);

// Direct call reservoir

Double Solution_2 =

Newtonraphson (fo, -4.0,

Other_arguments;

}

Note that this version of the library function is used simultaneously in the inside and outside of the function object. The main function demonstrates how to call the library routine by passing an object reference. However, it is also possible to call it to call the library function.

in conclusion

By fully utilizing the two powerful mechanisms of C , I have completed a library of reusable NEWTON-RAPHSON algorithm. The library is common and can be widely used to solve some similar problems, and the user-defined function (object) operator required to call the client program only.

The methods discussed herein can be applied to many common scientific algorithms available to solve a type of mathematical problem. For example, the numerical integral to any complex function can be processed with the same method. Two key features of the C language - Naming Functions Templates and Operators have made this simple and robust solution to be feasible. The overhead that defines the function into an object is actually negligible. In fact, many complex functions in real-app applications have been defined as an object, and when they need to be called by the General mathematical routine of the Newton-RapHSON algorithm, as long as it is easily overloaded () operators in the class. . Therefore, it is feasible to achieve a reusable local library to achieve a complete set of mathematics.

转载请注明原文地址:https://www.9cbs.com/read-46823.html

New Post(0)