Написание программы на C, которая игнорирует порядок операций? - PullRequest
0 голосов
/ 03 октября 2019

Я хочу написать программу на C, которая берет простую математическую задачу от пользователя и использует разные процессы для решения каждой части, а затем отправляет значение обратно в родительский процесс. Эта программа не должна обращать внимания на порядок операций. Например, если у меня есть проблема:

3 + 4 / 7 + 4 * 2

Я хочу, чтобы моя программа выводила следующее:

Enter problem: 3 + 4 / 7 + 4 * 2
PID 20419 calculated 3+4 as 7
PID 20420 calculated 7/7 as 1
PID 20421 calculated 1+4 as 5
PID 20422 calculated 5*2 as 10
Final result: 10

У меня возникли проблемы с анализомуравнение я ввожу. Я думаю об использовании метода getline() в C, чтобы помочь мне разобрать ввод. Это то, что у меня пока есть:

#include <stdio.h>

#define MAX_LINE_LENGTH 200

int main()
{
    printf("Enter equation: ");
    //getline(&buffer, &size, stdin)
    //&buffer is the address of the first character
    //&size is the address of the variable that holds the size of the input buffer
    //stdin is the input file handle
    size_t n = MAX_LINE_LENGTH;

    //Line becomes a pointer to 200 bytes of memory for you to use
    char *line = malloc(n)
    while ((getline(&line, &n, stdin)) {
        //Parse numbers and operators here
    }

    return 0;
}

У кого-нибудь есть какие-либо предложения о том, как анализировать числа и операторов, которые я буду вводить из стандартного ввода? Если мне кажется, что я читаю число, оператор и число, я хотел бы использовать fork(). Спасибо всем заранее.

Ответы [ 2 ]

3 голосов
/ 03 октября 2019

Это не полностью отвечает на вопрос, но это была забавная маленькая игрушка. Это не относится к конечной точке данных и, несомненно, нуждается в робастификации (слово «робастификация»? Должно быть!). В качестве упражнения для читателя проведите рефакторинг кода для обработки окончательного результата и работайте с нецелыми значениями.

#include <stdio.h>
#include <ctype.h>

int main(void) {
        int c;
        int last_op = 0;
        float a[2] = {0};

        while( ( c = getchar()) != EOF ) {
                if(isspace(c)) {
                        continue;
                }
                if(isdigit(c)) {
                        a[1] = 10 * a[1] + c - '0';
                } else {
                        float tmp = a[1];
                        if( last_op ) {
                                switch(last_op) {
                                case '+': tmp = a[0] + a[1]; break;
                                case '/': tmp = a[0] / a[1]; break;
                                case '*': tmp = a[0] * a[1]; break;
                                case '-': tmp = a[0] - a[1]; break;
                                default: fprintf( stderr, "invalid input: %c", last_op );
                                }
                                printf ("calculated %f %c %f = %f\n", a[0], last_op, a[1], tmp);
                        }
                        a[0] = tmp;
                        a[1] = 0;
                        last_op = c;
                }
        }
}
1 голос
/ 03 октября 2019

Небольшой бит «робустифицированного» * ​​1002 * сложения, которое захватывает конечную точку данных, может выглядеть примерно так (использование целочисленного математического выражения намеренно, при желании изменить на число с плавающей запятой):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

#define MAX_LINE_LENGTH 200

int main()
{
    size_t  n = MAX_LINE_LENGTH,
            offset = 0;
    int oper = 0, v1 = INT_MIN;
    char *line = malloc(n);

    if (!line) {    /* validate every allocation */
        perror ("malloc-line");
        return 1;
    }

    printf("Enter equation: ");
    if (getline (&line, &n, stdin) == -1) { /* validate every input */
        fputs ("stream error or user canceled.\n", stderr);
        return 1;
    }

    for (; line[offset];) {     /* loop while not end of line */
        int nchar = 0, v2;
        if (sscanf (line + offset, "%d%n", &v2, &nchar) == 1) { /* read int? */
            if (v1 == INT_MIN)  /* if v1 not set, set v1 to value */
                v1 = v2;
            else {              /* otherwise its v2 */
                int vsave = v1;
                switch (oper) { /* switch on operator, save result in v1 */
                    case '+':   v1 += v2; break;
                    case '-':   v1 -= v2; break;
                    case '*':   v1 *= v2; break;
                    case '/':   v1 /= v2; break;
                    default:    fputs ("error: invalid operator.\n", stderr);
                                break;
                }
                /* output results of calculation */
                printf ("calculated %d%c%d as %d\n", vsave, oper, v2, v1);
            }
            offset += nchar;    /* update offset with nchars read */
        }
        else {  /* read of int failed, must be oper or end */
            size_t used = strcspn (&line[offset], "+-*/");  /* chars to oper */
            offset += used;     /* update offset */
            if (line[offset] == '\n' || line[offset] == 0)  /* end of line? */
                break;
            oper = line[offset++];  /* set oper advance to next char */
        }
    }

    free (line);    /* don't forget to free line */
}

( примечание: вы выделили с malloc и даже пропустили прямое выделение, если line = NULL; и n = 0; getline выделят достаточно места самостоятельно, поэтомуне забудьте free(line); освободить выделенную вами память (да, здесь это происходит при выходе, но, надеюсь, вы напишете программы, использующие getline в более чем main(), так что вырабатывайте хорошие привычки сейчас .. .)

Использование описанного выше спецификатора "%n" помещает количество символов, потребляемых sscanf, при чтении в переменную nchars, которая затем используется для обновления offset в line. .

Пример использования / вывода

$ ./bin/calculate
Enter equation: 3 + 4 / 7 + 4 * 2
calculated 3+4 as 7
calculated 7/7 as 1
calculated 1+4 as 5
calculated 5*2 as 10

fork() Для обработки вычислений

По сути,только изменения, необходимые после того, как ваши v1, v2 и oper относятся к fork() процессуЗатем обработайте оператор switch() в дочернем элементе, а затем в родительском wait(), пока дочерний элемент не выйдет, прежде чем выйти и захватить ваш следующий набор значений. Как указано в моем комментарии, пример в man 2 wait дает хорошее представление о процессе.

Исходя из вашего комментария, вы установили fork() в правильном месте после успешного вызова sscanf. Там все, что вы делаете, это добавляете вызов к fork() и проверки и определения которого является дочерним и родительским. Затем переместите существующий расчет в дочерний процесс и добавьте вызов к getpid(), чтобы добавить его к своему выводу. В родительском процессе зарегистрируйте ваш wait() и выйдите из него после того, как ваш ребенок закончил.

Вы можете реализовать следующие изменения, внеся указанные выше изменения:

        if (sscanf (line + offset, "%d%n", &v2, &nchar) == 1) { /* read int? */
            if (v1 == INT_MIN)  /* if v1 not set, set v1 to value */
                v1 = v2;
            else {      /* otherwise its v2 */
                int status;
                pid_t w, pid = fork();  /* fork to compute values */
                if (pid == -1) {        /* validate fork succeeded */
                    perror ("fork");
                    return 1;
                }
                if (pid == 0) {         /* if pid == 0 in child process */
                    int vsave = v1;
                    switch (oper) { /* switch on operator, save result in v1 */
                        case '+':   v1 += v2; break;
                        case '-':   v1 -= v2; break;
                        case '*':   v1 *= v2; break;
                        case '/':   v1 /= v2; break;
                        default:    fputs ("error: invalid oper.\n", stderr);
                                    break;
                    }   /* output child PID with results of calculation */
                    printf ("PID %ld calculated %d%c%d as %d\n", 
                            (long)getpid(), vsave, oper, v2, v1);
                }
                else {      /* in the parent process */
                    do {    /* wait on child PID */
                        w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
                        if (w == -1) {      /* validate waitpid return */
                            perror("waitpid");
                            exit (EXIT_FAILURE);
                        }
                    } while (!WIFEXITED(status) && !WIFSIGNALED(status));
                    exit (EXIT_SUCCESS);    /* child exited, exit parent */
                }
            }
            offset += nchar;    /* update offset with nchars read */
        }

Полный пример просто добавляетнеобходимые заголовочные файлы с оставшейся частью кода без изменений. Например:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <sys/wait.h>
#include <unistd.h>

int main (void)
{
    size_t  n = 0,
            offset = 0;
    int oper = 0, v1 = INT_MIN;
    char *line = NULL;

    printf("Enter equation: ");
    if (getline (&line, &n, stdin) == -1) { /* validate every input */
        fputs ("stream error or user canceled.\n", stderr);
        return 1;
    }

    for (; line[offset];) {     /* loop while not end of line */
        int nchar = 0, v2;
        if (sscanf (line + offset, "%d%n", &v2, &nchar) == 1) { /* read int? */
            if (v1 == INT_MIN)  /* if v1 not set, set v1 to value */
                v1 = v2;
            else {      /* otherwise its v2 */
                int status;
                pid_t w, pid = fork();  /* fork to compute values */
                if (pid == -1) {        /* validate fork succeeded */
                    perror ("fork");
                    return 1;
                }
                if (pid == 0) {         /* if pid == 0 in child process */
                    int vsave = v1;
                    switch (oper) { /* switch on operator, save result in v1 */
                        case '+':   v1 += v2; break;
                        case '-':   v1 -= v2; break;
                        case '*':   v1 *= v2; break;
                        case '/':   v1 /= v2; break;
                        default:    fputs ("error: invalid oper.\n", stderr);
                                    break;
                    }   /* output child PID with results of calculation */
                    printf ("PID %ld calculated %d%c%d as %d\n", 
                            (long)getpid(), vsave, oper, v2, v1);
                }
                else {      /* in the parent process */
                    do {    /* wait on child PID */
                        w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
                        if (w == -1) {      /* validate waitpid return */
                            perror("waitpid");
                            exit (EXIT_FAILURE);
                        }
                    } while (!WIFEXITED(status) && !WIFSIGNALED(status));
                    exit (EXIT_SUCCESS);    /* child exited, exit parent */
                }
            }
            offset += nchar;    /* update offset with nchars read */
        }
        else {  /* read of int failed, must be oper or end */
            size_t used = strcspn (&line[offset], "+-*/");  /* chars to oper */
            offset += used;     /* update offset */
            if (line[offset] == '\n' || line[offset] == 0)  /* end of line? */
                break;
            oper = line[offset++];  /* set oper advance to next char */
        }
    }

    free (line);    /* don't forget to free line */
}

Пример использования / Вывод

Выполнение вышеуказанного с тем же входом даст одинаковый вывод с дочерним PID перед каждым отдельным расчетом, например

$ ./bin/calculate_fork
Enter equation: 3 + 4 / 7 + 4 * 2
PID 18746 calculated 3+4 as 7
PID 18747 calculated 7/7 as 1
PID 18748 calculated 1+4 as 5
PID 18749 calculated 5*2 as 10

Дайте ему знать и дайте мне знать, если у вас есть дополнительные вопросы.

...