C 从命令行读入数学方程式
C read in a mathematical equation from command line
目前,我正在编写一个程序,通过使用梯形规则和这些规则的组合来计算方程的积分,以实现更高的精度。现在,正如您在下面看到的,我已经对函数进行了硬编码。是否可以读入数学方程式并对其进行评估?我知道我可以读取输入字符列表,然后评估函数(比如 if char[i] == '+' do ....)但是有更简单的方法吗?
提前致谢!
void Integral_TN (double* TN_ptr,double a,double b,int n,int my_rank,int p){
int i;
double a_loc;
double b_loc;
double hN;
*TN_ptr = 0;
hN = (b-a)/n;
a_loc = a + my_rank*n/p*hN;
b_loc = a + (my_rank+1)*n/p*hN;
*TN_ptr += (function(a_loc)+function(b_loc))/2; /*Evaluate f at the borders*/
for(i = 1; i < n/p; i++){
*TN_ptr += function(a_loc + i*hN); /*Evaluate f at the inner nodes*/
}
*TN_ptr = *TN_ptr*hN;
}
double function(double x){
double y;
y = 1/(1+x*x);
return y;
}
没有更简单的方法可以实现您想要实现的目标。如果要将特定公式应用于某些值,则必须为其定义一个函数并输入值。
如果您希望输入整个表达式(带有值和运算符)作为输入并获得所需的结果作为输出,您将不得不超越基本编程。您需要创建一个解析器。
例如,如果您提供 3+2*4
作为输入,您可能期望 11
作为输出,而无需读取单独的值 3
、2
和 4
.这可以通过在像 YACC 这样的解析器生成器中实现自定义解析器来实现。基本上,您将创建和定义关于如何解释输入的新规则。
已经回答了上述问题,但我想解决根本问题。
应用Unix philosophy。使用一种工具生成数据,使用另一种工具使用它 -- 在这里,使用梯形法则计算积分。
您可以使用的最简单的格式与 Gnuplot 支持的格式相同:
- 忽略空行
- 以
#
开头的行被忽略,可用于注释
- 每一行定义一个样本
基本上,您可以使用
非常粗略地描述正弦曲线
#x sin(x)
0.000 0.000000000
0.100 0.099833417
0.200 0.198669331
0.300 0.295520207
0.400 0.389418342
0.500 0.479425539
0.600 0.564642473
0.700 0.644217687
0.800 0.717356091
0.900 0.783326910
1.000 0.841470985
1.100 0.891207360
1.200 0.932039086
1.300 0.963558185
1.400 0.985449730
1.500 0.997494987
1.600 0.999573603
1.700 0.991664810
1.800 0.973847631
1.900 0.946300088
2.000 0.909297427
2.100 0.863209367
2.200 0.808496404
2.300 0.745705212
2.400 0.675463181
2.500 0.598472144
2.600 0.515501372
2.700 0.427379880
2.800 0.334988150
2.900 0.239249329
3.000 0.141120008
3.100 0.041580662
3.200 -0.058374143
3.300 -0.157745694
3.400 -0.255541102
3.500 -0.350783228
3.600 -0.442520443
3.700 -0.529836141
3.800 -0.611857891
3.900 -0.687766159
4.000 -0.756802495
4.100 -0.818277111
4.200 -0.871575772
4.300 -0.916165937
4.400 -0.951602074
4.500 -0.977530118
4.600 -0.993691004
4.700 -0.999923258
4.800 -0.996164609
4.900 -0.982452613
5.000 -0.958924275
5.100 -0.925814682
5.200 -0.883454656
5.300 -0.832267442
5.400 -0.772764488
5.500 -0.705540326
5.600 -0.631266638
5.700 -0.550685543
5.800 -0.464602179
5.900 -0.373876665
6.000 -0.279415498
6.100 -0.182162504
6.200 -0.083089403
您可以使用例如。 awk 生成它,就像我所做的那样:
awk 'BEGIN { printf "#x sin(x)\n" ; for (x=0.0; x<6.3; x+=0.1) printf "%.3f %11.9f\n", x, sin(x) }'
如果将其保存到文件中(将 > data.txt
附加到上述命令),则可以使用
在 Gnuplot 中绘制它
plot "data.txt" using 1:2 notitle with lines
这样的数据在C程序中很容易读取。因为我只使用 POSIX.1 系统(Linux、BSD、macOS),而 POSIX.1 提供了非常有用的 getline()
功能——它可以让你逐行阅读任何长度,动态分配足够大的缓冲区——这个特定的实现还需要 POSIX.1 支持。换句话说,除了 Windows.
之外,它基本上在任何地方都有效。
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Read x y(x) values, one pair per line, from a stream.
The arrays are dynamically allocated, and pointers stored
to *xptr and *yptr. The size of the arrays is stored at *nptr.
They are initially cleared to NULL/zero.
The function returns 0 if success, an errno error code otherwise:
EINVAL: Invalid function parameters
EIO: Read error
ENOMEM: Out of memory
EBADMSG: Malformed line
*/
int read_xy(double **xptr, double **yptr, size_t *nptr, FILE *in)
{
/* Line input buffer variables. */
char *line = NULL;
size_t size = 0;
ssize_t len;
/* Data array variables. */
double *x = NULL;
double *y = NULL;
size_t n = 0; /* Entries in x[] and y[] */
size_t nmax = 0; /* Entries allocated */
/* Temporary variables. */
double xval, yval, *newx, *newy;
/* We clear the output parameters to NULL or zero,
in case the caller is careless and does not check
the return value. Clearing them ensures they do
not contain garbage in such a case. */
if (xptr)
*xptr = NULL;
if (yptr)
*yptr = NULL;
if (nptr)
*nptr = 0;
/* We need in and nptr, and at least one of xptr and yptr. */
if (!in || !nptr || (!xptr && !yptr))
return errno = EINVAL;
/* If an error has already occurred in 'in',
we do not even try to read from it. */
if (ferror(in))
return EIO;
while (1) {
/* Read next input line. */
len = getline(&line, &size, in);
/* End of input or error? */
if (len < 0)
break;
/* Skip empty and comment lines. */
if (len == 0 ||
line[0] == '\n' || (line[0] == '\r' && line[1] == '\n') ||
line[0] == '#')
continue;
/* Parse the line. */
if (sscanf(line, " %lf %lf", &xval, &yval) != 2)
break;
/* Need to grow the dynamically allocated arrays? */
if (n >= nmax) {
/* Allocation policy.
We allocate room for at least 16 doubles,
then double the size up to 1048576 (=2^20),
then adjust to the next full multiple of 1048576.
This is not 'the best', but it is robust,
and not too wasteful.
*/
if (n < 16)
nmax = 16;
else
if (n < 1048576)
nmax = n * 2;
else
nmax = (n | 1048575) + 1048576;
/* Note: realloc(NULL, size) is equivalent to malloc(size).
If the realloc() call fails, it returns NULL,
but the original array is still valid.
Also note that free(NULL) is safe, and does nothing.
*/
newx = realloc(x, nmax * sizeof x[0]);
newy = realloc(y, nmax * sizeof y[0]);
if (newx)
x = newx;
if (newy)
y = newy;
if (!newx || !newy) {
/* One or both of the allocations failed. */
free(line);
free(x);
free(y);
return ENOMEM;
}
}
/* Save the parsed values to the arrays. */
x[n] = xval;
y[n] = yval;
n++;
}
/* We no longer need the line buffer. */
free(line);
/* Did a read error occur? */
if (ferror(in)) {
free(x);
free(y);
return EIO;
}
/* Was there no data to read? */
if (n < 1) {
free(x);
free(y);
return 0;
}
/* Reallocate the arrays to their exact sizes
(actually, allow for one extra double at the end,
because it is often useful to copy the initial
ones there if the data is considered cyclic).
*/
nmax = n + 1; /* One extra just because it is so often useful. */
newx = realloc(x, nmax * sizeof x[0]);
newy = realloc(y, nmax * sizeof y[0]);
if (newx)
x = newx;
if (newy)
y = newy;
if (!newx || !newy) {
free(x);
free(y);
return ENOMEM;
}
/* Save the array pointers. */
if (xptr)
*xptr = x;
else
free(x);
if (yptr)
*yptr = y;
else
free(y);
/* Save the number of samples read. */
*nptr = n;
/* If feof(in) is true, then we read everything
up to end of input. Otherwise, we stopped at
a line we could not parse.
*/
if (!feof(in))
return EBADMSG;
return 0;
}
该函数或类似函数应该出现在每门数值计算课程的课程材料中。它们实在是太有用了。除了系统管理员为每个进程设置的可能的内存分配限制外,这个特定的程序对其可以读取的数据大小没有固有限制。我知道一个事实,如果您有足够的 RAM,它会很高兴地成功读取数十亿行数据。
使用功能非常简单。这是一个示例 main()
,它只是从标准输入中读取此类数据——请记住,当您 运行 它时,您可以通过将 < file
附加到命令来使其从文件中读取——,并打印出数据。
int main(void)
{
double *x, *y;
size_t i, n;
int result;
result = read_xy(&x, &y, &n, stdin);
switch (result) {
case 0: /* No errors */
break;
case EBADMSG:
if (n > 1)
fprintf(stderr, "Invalid line after %zu data samples.\n", n);
else
fprintf(stderr, "Cannot parse first input line.\n");
return EXIT_FAILURE;
case ENOMEM:
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
case EIO:
fprintf(stderr, "Read error.\n");
return EXIT_FAILURE;
case EINVAL:
fprintf(stderr, "Invalid parameters to the read_xy() function!\n");
return EXIT_FAILURE;
default:
fprintf(stderr, "%s.\n", strerror(result));
return EXIT_FAILURE;
}
printf("Read %zu samples:\n", n);
for (i = 0; i < n; i++)
printf("%.9f %.9f\n", x[i], y[i]);
return EXIT_SUCCESS;
}
注意大部分是switch (result) { .. }
报错代码。这个例子很小心的告诉你,如果出现错误;原因是,作为用户,您需要知道程序何时 知道 他们可能会喷出垃圾,并且在现实生活中宁愿程序中止也不愿静静地喷出垃圾 --也许让你相信它是有效的。
虽然可以修改上面的代码使其在 Windows 上也能正常工作(例如,您将 getline()
替换为 fgets()
,并希望您使用的缓冲区大小因为它就足够了;并且可能还必须更改一些 errno
错误代码)。然而,超级计算机 500 强列表中没有 Windows 台机器是有原因的:POSIXy 系统(Unix 和 Linux)更适合科学计算。因此,如果您确实打算从事一些科学计算,您不妨设置一个 Linux 或 BSD 虚拟机,然后在那里进行开发。
目前,我正在编写一个程序,通过使用梯形规则和这些规则的组合来计算方程的积分,以实现更高的精度。现在,正如您在下面看到的,我已经对函数进行了硬编码。是否可以读入数学方程式并对其进行评估?我知道我可以读取输入字符列表,然后评估函数(比如 if char[i] == '+' do ....)但是有更简单的方法吗? 提前致谢!
void Integral_TN (double* TN_ptr,double a,double b,int n,int my_rank,int p){
int i;
double a_loc;
double b_loc;
double hN;
*TN_ptr = 0;
hN = (b-a)/n;
a_loc = a + my_rank*n/p*hN;
b_loc = a + (my_rank+1)*n/p*hN;
*TN_ptr += (function(a_loc)+function(b_loc))/2; /*Evaluate f at the borders*/
for(i = 1; i < n/p; i++){
*TN_ptr += function(a_loc + i*hN); /*Evaluate f at the inner nodes*/
}
*TN_ptr = *TN_ptr*hN;
}
double function(double x){
double y;
y = 1/(1+x*x);
return y;
}
没有更简单的方法可以实现您想要实现的目标。如果要将特定公式应用于某些值,则必须为其定义一个函数并输入值。
如果您希望输入整个表达式(带有值和运算符)作为输入并获得所需的结果作为输出,您将不得不超越基本编程。您需要创建一个解析器。
例如,如果您提供 3+2*4
作为输入,您可能期望 11
作为输出,而无需读取单独的值 3
、2
和 4
.这可以通过在像 YACC 这样的解析器生成器中实现自定义解析器来实现。基本上,您将创建和定义关于如何解释输入的新规则。
应用Unix philosophy。使用一种工具生成数据,使用另一种工具使用它 -- 在这里,使用梯形法则计算积分。
您可以使用的最简单的格式与 Gnuplot 支持的格式相同:
- 忽略空行
- 以
#
开头的行被忽略,可用于注释 - 每一行定义一个样本
基本上,您可以使用
非常粗略地描述正弦曲线#x sin(x)
0.000 0.000000000
0.100 0.099833417
0.200 0.198669331
0.300 0.295520207
0.400 0.389418342
0.500 0.479425539
0.600 0.564642473
0.700 0.644217687
0.800 0.717356091
0.900 0.783326910
1.000 0.841470985
1.100 0.891207360
1.200 0.932039086
1.300 0.963558185
1.400 0.985449730
1.500 0.997494987
1.600 0.999573603
1.700 0.991664810
1.800 0.973847631
1.900 0.946300088
2.000 0.909297427
2.100 0.863209367
2.200 0.808496404
2.300 0.745705212
2.400 0.675463181
2.500 0.598472144
2.600 0.515501372
2.700 0.427379880
2.800 0.334988150
2.900 0.239249329
3.000 0.141120008
3.100 0.041580662
3.200 -0.058374143
3.300 -0.157745694
3.400 -0.255541102
3.500 -0.350783228
3.600 -0.442520443
3.700 -0.529836141
3.800 -0.611857891
3.900 -0.687766159
4.000 -0.756802495
4.100 -0.818277111
4.200 -0.871575772
4.300 -0.916165937
4.400 -0.951602074
4.500 -0.977530118
4.600 -0.993691004
4.700 -0.999923258
4.800 -0.996164609
4.900 -0.982452613
5.000 -0.958924275
5.100 -0.925814682
5.200 -0.883454656
5.300 -0.832267442
5.400 -0.772764488
5.500 -0.705540326
5.600 -0.631266638
5.700 -0.550685543
5.800 -0.464602179
5.900 -0.373876665
6.000 -0.279415498
6.100 -0.182162504
6.200 -0.083089403
您可以使用例如。 awk 生成它,就像我所做的那样:
awk 'BEGIN { printf "#x sin(x)\n" ; for (x=0.0; x<6.3; x+=0.1) printf "%.3f %11.9f\n", x, sin(x) }'
如果将其保存到文件中(将 > data.txt
附加到上述命令),则可以使用
plot "data.txt" using 1:2 notitle with lines
这样的数据在C程序中很容易读取。因为我只使用 POSIX.1 系统(Linux、BSD、macOS),而 POSIX.1 提供了非常有用的 getline()
功能——它可以让你逐行阅读任何长度,动态分配足够大的缓冲区——这个特定的实现还需要 POSIX.1 支持。换句话说,除了 Windows.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Read x y(x) values, one pair per line, from a stream.
The arrays are dynamically allocated, and pointers stored
to *xptr and *yptr. The size of the arrays is stored at *nptr.
They are initially cleared to NULL/zero.
The function returns 0 if success, an errno error code otherwise:
EINVAL: Invalid function parameters
EIO: Read error
ENOMEM: Out of memory
EBADMSG: Malformed line
*/
int read_xy(double **xptr, double **yptr, size_t *nptr, FILE *in)
{
/* Line input buffer variables. */
char *line = NULL;
size_t size = 0;
ssize_t len;
/* Data array variables. */
double *x = NULL;
double *y = NULL;
size_t n = 0; /* Entries in x[] and y[] */
size_t nmax = 0; /* Entries allocated */
/* Temporary variables. */
double xval, yval, *newx, *newy;
/* We clear the output parameters to NULL or zero,
in case the caller is careless and does not check
the return value. Clearing them ensures they do
not contain garbage in such a case. */
if (xptr)
*xptr = NULL;
if (yptr)
*yptr = NULL;
if (nptr)
*nptr = 0;
/* We need in and nptr, and at least one of xptr and yptr. */
if (!in || !nptr || (!xptr && !yptr))
return errno = EINVAL;
/* If an error has already occurred in 'in',
we do not even try to read from it. */
if (ferror(in))
return EIO;
while (1) {
/* Read next input line. */
len = getline(&line, &size, in);
/* End of input or error? */
if (len < 0)
break;
/* Skip empty and comment lines. */
if (len == 0 ||
line[0] == '\n' || (line[0] == '\r' && line[1] == '\n') ||
line[0] == '#')
continue;
/* Parse the line. */
if (sscanf(line, " %lf %lf", &xval, &yval) != 2)
break;
/* Need to grow the dynamically allocated arrays? */
if (n >= nmax) {
/* Allocation policy.
We allocate room for at least 16 doubles,
then double the size up to 1048576 (=2^20),
then adjust to the next full multiple of 1048576.
This is not 'the best', but it is robust,
and not too wasteful.
*/
if (n < 16)
nmax = 16;
else
if (n < 1048576)
nmax = n * 2;
else
nmax = (n | 1048575) + 1048576;
/* Note: realloc(NULL, size) is equivalent to malloc(size).
If the realloc() call fails, it returns NULL,
but the original array is still valid.
Also note that free(NULL) is safe, and does nothing.
*/
newx = realloc(x, nmax * sizeof x[0]);
newy = realloc(y, nmax * sizeof y[0]);
if (newx)
x = newx;
if (newy)
y = newy;
if (!newx || !newy) {
/* One or both of the allocations failed. */
free(line);
free(x);
free(y);
return ENOMEM;
}
}
/* Save the parsed values to the arrays. */
x[n] = xval;
y[n] = yval;
n++;
}
/* We no longer need the line buffer. */
free(line);
/* Did a read error occur? */
if (ferror(in)) {
free(x);
free(y);
return EIO;
}
/* Was there no data to read? */
if (n < 1) {
free(x);
free(y);
return 0;
}
/* Reallocate the arrays to their exact sizes
(actually, allow for one extra double at the end,
because it is often useful to copy the initial
ones there if the data is considered cyclic).
*/
nmax = n + 1; /* One extra just because it is so often useful. */
newx = realloc(x, nmax * sizeof x[0]);
newy = realloc(y, nmax * sizeof y[0]);
if (newx)
x = newx;
if (newy)
y = newy;
if (!newx || !newy) {
free(x);
free(y);
return ENOMEM;
}
/* Save the array pointers. */
if (xptr)
*xptr = x;
else
free(x);
if (yptr)
*yptr = y;
else
free(y);
/* Save the number of samples read. */
*nptr = n;
/* If feof(in) is true, then we read everything
up to end of input. Otherwise, we stopped at
a line we could not parse.
*/
if (!feof(in))
return EBADMSG;
return 0;
}
该函数或类似函数应该出现在每门数值计算课程的课程材料中。它们实在是太有用了。除了系统管理员为每个进程设置的可能的内存分配限制外,这个特定的程序对其可以读取的数据大小没有固有限制。我知道一个事实,如果您有足够的 RAM,它会很高兴地成功读取数十亿行数据。
使用功能非常简单。这是一个示例 main()
,它只是从标准输入中读取此类数据——请记住,当您 运行 它时,您可以通过将 < file
附加到命令来使其从文件中读取——,并打印出数据。
int main(void)
{
double *x, *y;
size_t i, n;
int result;
result = read_xy(&x, &y, &n, stdin);
switch (result) {
case 0: /* No errors */
break;
case EBADMSG:
if (n > 1)
fprintf(stderr, "Invalid line after %zu data samples.\n", n);
else
fprintf(stderr, "Cannot parse first input line.\n");
return EXIT_FAILURE;
case ENOMEM:
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
case EIO:
fprintf(stderr, "Read error.\n");
return EXIT_FAILURE;
case EINVAL:
fprintf(stderr, "Invalid parameters to the read_xy() function!\n");
return EXIT_FAILURE;
default:
fprintf(stderr, "%s.\n", strerror(result));
return EXIT_FAILURE;
}
printf("Read %zu samples:\n", n);
for (i = 0; i < n; i++)
printf("%.9f %.9f\n", x[i], y[i]);
return EXIT_SUCCESS;
}
注意大部分是switch (result) { .. }
报错代码。这个例子很小心的告诉你,如果出现错误;原因是,作为用户,您需要知道程序何时 知道 他们可能会喷出垃圾,并且在现实生活中宁愿程序中止也不愿静静地喷出垃圾 --也许让你相信它是有效的。
虽然可以修改上面的代码使其在 Windows 上也能正常工作(例如,您将 getline()
替换为 fgets()
,并希望您使用的缓冲区大小因为它就足够了;并且可能还必须更改一些 errno
错误代码)。然而,超级计算机 500 强列表中没有 Windows 台机器是有原因的:POSIXy 系统(Unix 和 Linux)更适合科学计算。因此,如果您确实打算从事一些科学计算,您不妨设置一个 Linux 或 BSD 虚拟机,然后在那里进行开发。