23-24C++ (39)–Random function, function template, automatic type, anonymous function

1. Random function

C++ uses the random_device type variable of the random header file to generate random numbers. This random number generator uses the Mersenne twister algorithm to quickly generate high-quality pseudo-random unsigned ints.

1. Pseudo-random numbers and true random numbers. Pseudo-random numbers: seemingly random numbers, with repeatability and predictability. True random numbers: appearing in a non-deterministic way, with non-repeatability and unpredictability

2. Computers are not suitable for generating true random numbers and can only generate pseudo-random numbers by formulas. For most applications, pseudorandom numbers are sufficient. Real random numbers can use the decay period and noise frequency of radioactive elements, which is difficult to achieve.

3. Criteria for evaluating the performance of pseudo-random number generators: the period of the pseudo-random sequence.

4. Case

(1) Generate 20 random unsigned integer values

Description:

If you want to generate signed integers, you can use cout << rd() << endl;

Modify to: cout << (int)rd() << endl;

or int a = rd(); cout << a << endl;

#include <iostream>
#include <random>
using namespace std;
int main()
{
random_device rd;
for (int i = 0; i < 20; i + + )
{
cout << rd() << endl;
}
return 0;
}

(2) Generate random integers between 0 and 99

Change rd() in question (1) to rd() % 100

Description: The remainder of an unsigned integer divided by 100, ranging from 0 to 99.

(3) Generate a random integer between a~b (the range includes a and b). Modify rd() in question (1) to rd() % (b – a + 1) + a

Note: b – a + 1 represents the number of different integers in the range of a~b, rd() % (b – a + 1) generates random integers between 0~b-a.

(4) Generate a random floating point number between 1.0~9.9 (one decimal part). Modify rd() in question (1) to (rd() % 90 + 10) / 10.0

Note: First consider generating an integer between 10 and 99, and then divide it by 10.0.

(5) Generate a random integer between -20~10. Change rd() in question (1) to (int)(rd() % 31) -20

Note: The rd() type is unsigned and must be converted to signed int after taking the remainder.

2. Function template

Templates are the foundation of C++ generic programming. Generic programs are coded independently of the specific data types, and the specific data types are determined when the program is compiled.

Suppose you need to write a function that compares two values and returns 0 if the two values are equal, -1 if argument 1 is less than argument 2, and 1 if argument 1 is greater than argument 2. If both values are integers,

Then the function can be written as:

int compare(int a, int b)
{
if (a == b)
{
return 0;
}
else if (a < b)
{
return -1;
}
else
{
return 1;
}
}

When there is only one sentence in the braces of if, for, and while, you can omit the braces (but it is not recommended). In order to save space, the above code can also be written as:

int compare(int a, int b)
{
if (a == b) return 0;
else if (a < b) return -1;
else return 1;
}

But if the size of two floating point numbers is compared, the function must be written as:

int compare(double a, double b)
{
if (a == b) return 0;
else if (a < b) return -1;
else return 1;
}

There are also possible more types of data comparisons,Although C++ allows functions of the same name with different parameter types (overloading in the next lesson), this is not good solution.

1. Define function template

Instead of defining a new function for each data type, you can define a general function template. A function template is a formula used to generate a version of a function for a specific data type. A template definition begins with the keyword template , followed by a list of template parameters representing the specific data types used in the function definition.

When using templates, you can specify template arguments implicitly or explicitly and bind them to template parameters.

The compare function can be written as a template:

#include <iostream>
using namespace std;
template<typename T>
int compare(T a, T b)
{
if (a == b) return 0;
else if (a < b) return -1;
else return 1;
}

Description:

The template parameter list of the compare function declares a type parameter named T, where T represents a data type. Which data type T specifically represents is determined at compile time based on the call to the compare function.

For example in the main function

int main()
{
cout << compare(1, 0) << endl;
return 0;
}

The actual parameter type is int. The compiler will infer that the template actual parameters a and b are int and bind the int to the template parameter T.

The compiler uses the inferred template parameters to instantiate a specific version of the function.

If the code of the main function is:

int main()
{
cout << compare(1, 0) << endl;
cout << compare(1.5, 2.1) << endl;
return 0;
}

Then the compiler will instantiate two different versions of the compare function.

For the first call, the compiler generates compare with T replaced by int.

For the second call, the compiler-generated compare has T replaced with double.

2. The case of multiple template parameters

In the above code, if cout << compare(1.5, 2) << endl; is used in the main function, a compilation error will be triggered because the type of T inferred from 1.5 and 2 is inconsistent. If you want to support comparison of two actual parameters of different types, you need to define two template parameters in the template parameter list, for example, named T1 and T2.

#include
using namespace std;
template
int compare(T1 a, T2 b)
{
if (a == b) return 0;
else if (a < b) return -1;
else return 1;
}
int main()
{
cout << compare(1, 0) << endl;
cout << compare(1.5, 2.1) << endl;
return 0;
}

The running results are as follows--

3. Case

(1) Find the sum of two numbers of the same type

#include<iostream>
using namespace std;
template <typename T>
T getSum(T a, T b)
{
return a + b;
}
int main()
{
cout << getSum(4, 5);
return 0;
}

The running results are as follows--

(2) Exchange two numbers of the same type

#include<iostream>
using namespace std;
template <typename T>
void f(T & amp; a, T & amp; b)
{
T c;
c = a;
a = b;
b = c;
}
int main()
{
int a = 5, b = 10;
f(a, b);
cout << a << "\\
" << b << "\\
";
return 0;
}

The running results are as follows--

【Exercise】The result of running the following program is , in which the ASCII code of the letter A is 65

#include<iostream>
using namespace std;
template<class T1, class T2>
T1 f(T1 x, T2 y)
{
return x + y;
}
int main()
{
cout << f('A', 1) << f(1, 'A') << endl;
return 0;
}

T1 determines the type of output--

The running results are as follows--

3. Automatic type

1. auto automatic type variable

When programming, you often need to assign the value of an expression to a variable, which requires you to clearly know the type of the expression when declaring the variable. Sometimes it's not easy or even possible to do this. In order to solve this problem, starting from the C++11 standard, the auto type was introduced, which allows the compiler to automatically analyze the type to which the expression belongs. Unlike the original specifiers that only define a specific type (such as double), auto allows the compiler to infer the type of the variable from the initial value. Obviously, variables defined by auto must have an initial value.

auto c = a + b;

At this point the compiler will infer the type of c based on the result of adding a and b.

If a and b are both int, then c is of type int;

If a is double and b is int, then c is of type double.

Using auto can also define multiple variables in one statement. Because a variable declaration statement can only have one data type, the data types of all variables in the statement must be the same.

auto a = 0.5, b = 1.0;

2. Automatic return type in function

When the return value type of the function is auto type, the type of the return value will be automatically inferred based on the value of return. It should be noted that if there are multiple return statements in a function whose return value type is auto, the return value of each return statement must be inferred to be of the same type.

Example: Find the sum of two numbers of any type (syntax starting from C++14, not supported by OJ)

#include<iostream>
using namespace std;
template<typename T1, typename T2>
auto getMax(T1 x, T2 y)
{
return x + y;
}
int main()
{
cout << getMax(1, 2) << endl;
cout << getMax('A', 1) << endl;
cout << getMax(1.5, 2) << endl;
return 0;
}

The running results are as follows--

4. Anonymous functions

Anonymous functions, also known as functions, use lambda expressions to define functions, which is the basis of functional programming. Anonymous functions are defined inside the function and are only visible within the function.

1. Lambda function syntax

[capture](parameter)->return-type{statement}

[capture]: Capture list, capture variables in the context for use by lambda functions.

(parameter): Parameter list, consistent with the parameter list of ordinary functions. If there are no parameters, the parentheses can be omitted along with them.

->return-type: Return type, if there is no return value, it can be omitted together with ->. When the return type is clear, it can also be omitted, and the compiler is responsible for deriving the return type.

{statement}: function body. Like a normal function, but in addition to parameters, all captured variables can be used.

2. Variable capture list

The [capture] variable capture module in the anonymous function syntax can capture variables in the external scope of the anonymous function for use in the function body of the anonymous function.

[ ] The function does not need to access any variables in the external scope

[ & amp;] function reference calls all variables in the external scope

Reference call - can access and modify the value of peripheral variables (& amp;) reference symbol

[=] The function calls all variables in the external scope by value

Call by value - you can access but cannot modify the value of peripheral variables

[=, & amp;y] function calls all variables in the outer scope by value, but calls the y variable by reference

[ & amp;, x] The function reference calls all variables in the external scope, but the x variable is called by value

[x, & amp;y] The function calls the x variable by value and the y variable by reference

3. Case

(1) Use anonymous functions to implement summation functions

#include<iostream>
using namespace std;
int main()
{
auto sum = [](int a, int b)->int {return a + b; };
int x = 5, y = 10;
cout << sum(x, y) << endl;
cout << sum(x, x) << endl;
return 0;
}

The running results are as follows--

Understand this code!

[capture](parameter)->return-type{statement}

(2) Use anonymous function to calculate the factorial of n

code show as below--

#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
auto fac = [=]()->int {
int f = 1;
for (int i = 1; i <= n; i + + )
{
f = f * i;
}
return f;
};
cout << fac() << endl;
return 0;
}

The running results are as follows--

(3) Use anonymous function to generate random integers between x~y

code show as below--

#include<iostream>
#include<random>
using namespace std;
int main()
{
int x, y;
cin >> x >> y;
random_device rd;
auto getRand = [ & amp;]()->int
{
return rd() % (y - x + 1) + x;
};
int a = getRand();
int b = getRand();
int c = getRand();
cout << "a=" << a << "\tb=" << b << "\tc=" << c << endl;
return 0;
}

The running results are as follows--

Key point to understand! ! ! ! ! ! ! ! ! ! ! ! ! ! !

All mentioned above!