калькулятор на C - введите целую формулу

Я пытаюсь написать калькулятор на C и хочу сделать такой, который может вычислять несколько входов, например (5 * 9 + 1 -2). Эти входные данные могут быть совершенно случайными, и я застрял в том, как это сделать.

Я знаю, как инициализировать переменную и попросить пользователя ввести число и все такое, но если пользователь захочет сложить 50 случайных чисел, этот калькулятор не сможет этого сделать.

Надеюсь, вы можете помочь или поделиться некоторыми советами

Спасибо !


person Cray    schedule 03.09.2015    source источник
comment
Что вы пробовали до сих пор?   -  person L.Butz    schedule 03.09.2015
comment
Вам нужно реализовать некоторые алгоритмы, чтобы сделать это правильно, посмотрите этот ответ, чтобы получить некоторые подсказки о том, с чего начать.   -  person A4L    schedule 03.09.2015
comment
Чтобы вычислить произвольное выражение, вам понадобятся некоторые структуры данных, такие как стек или дерево выражений, для хранения узлов выражения, а затем для их обхода и вычисления. Попробуйте сначала вычислить выражения без круглых скобок, затем вам нужно будет разбить строку, используя пробелы и символы математических операций (+ - * /) в качестве разделителей. Затем, в зависимости от текущей операции, вы просто добавите текущее значение к сумме или измените его с помощью множителя/делителя.   -  person olegst    schedule 03.09.2015
comment
@L.Butz Я понятия не имею, с чего начать. Я подумал о том, чтобы сохранить ввод в строку, а затем использовать цикл for для прохождения этой строки и извлечения каждого значения и арифметического символа, и я не уверен, куда идти после этого.   -  person Cray    schedule 03.09.2015


Ответы (4)


Вам потребуется реализовать анализатор выражений, который будет учитывать приоритет операторов. Для меня двумя самыми простыми способами сделать это было бы либо реализовать достойный рекурсивный синтаксический анализатор, либо реализовать Shunting алгоритм двора.

См. это в качестве примера.

person Chris Taylor    schedule 03.09.2015

Для этого вам нужно прочитать всю строку (это не должно быть слишком сложно), затем вам нужно разобрать ее и сохранить в некоторых структурах данных.

Вот два известных мне способа хранения и использования:

  • Первый: это легко сделать, легко использовать, но не красиво и не быстро: двойной связанный список, в котором каждая ссылка содержит оператор или число и приоритет. если это оператор (вы можете использовать enum + union, если хотите что-то чище):

    struct list {
        struct list *prev;
        struct list *next;
        char operator;
        int number;
        unsigned int priority;
    }
    

    Вы перебираете свою строку и применяете простой алгоритм приоритета (псевдокод):

    var priority = 0
    var array = cut string // ["5", "+", "3", "*", "(", "6", "-", "2", ")"]
    
    check string // verify the string is correct
    
    for each element of array :
        if (element is number)
            store in list
    
        else if (element is operator)
            store in list
            if (element is '*' or '/' or '%')
                set his priority to priority + 1
            else
                set his priority to priority
    
        else if (element is open parenthesis)
            priority += 2
    
        else if (element is close parenthesis)
            priority -= 2
    

    Например :

    нить:

    5 + 3 * (6 - 2) - 1
    

    приоритеты:

      0   1    2    0
    

    Затем, чтобы сделать ваши расчеты:

    while list isn't empty:
        find operator with the highest priority // if there is more than one, take the first
        calculate this operator // with the element prev and next
        replace by result in list // operator, but also prev and next
    

    Пример, опять же, с 5 + 3 * (6 - 2) - 1 :

    первая итерация:

    5 + 3 * 4 - 1
    

    тогда

    5 + 12 - 1
    

    тогда

    17 - 1       
    

    тогда

    16
    
  • Другой (и лучше, хотя это немного сложнее, если вы не знакомы с рекурсией) один: двоичное дерево с использованием обратная полировка (см. здесь и здесь )

    Это более распространено, поэтому я не буду его объяснять.

person 4rzael    schedule 04.09.2015
comment
Еще 1 вопрос, как мне обрезать строку, для строки 'var array = cut string'. Кроме того, я не понял, что вы имели в виду под «проверить строку», чтобы убедиться, что она верна, есть ли функция для этого, или другие части уже проверяли ее? - person Cray; 05.09.2015
comment
@Cray Чтобы вырезать строку, вам придется немного проанализировать ее, но вы можете начать с сокращения ее пробелами (3 + 4 становится ["3", "+", "4"]), используя strtok. (Если вы это сделаете, каждый символ должен быть разделен пробелом...) Строка проверки проверяет синтаксические ошибки, такие как несовпадающие круглые скобки или два оператора (или числа) в строке (3 + * 4 не является допустимой операцией, а также 3 4 + 5), неизвестные символы (3 + 4NINJA тоже недопустимо) и так далее... - person 4rzael; 07.09.2015

Вы можете использовать строку при чтении и разделить эту строку, когда найдете {+,-,*,/}.. И то, что вы найдете между ними, — это ваши числа.

Попробуйте добавить свой код!

Удачи!

person Macpp    schedule 03.09.2015
comment
Я подумал о сохранении ввода в строку, а затем с помощью цикла for, чтобы пройти по этой строке и вынуть каждое значение и арифметический символ, и я не уверен, куда идти после этого. Это самый эффективный путь? - person Cray; 03.09.2015
comment
Просто.. сначала вам нужно выбрать первое значение и сохранить его в X, скажем (перед первым арифметическим символом), а затем в каждом найденном символе взять значение после него и сделать то, что этот символ предназначен для X . - person Macpp; 03.09.2015

Это не полное решение, но это что-то... попробуйте этот код:

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

#define MAX_SIZE (1000)

static char tempNumStr[50] = {0};
static double parsedDoubleArr[5] = {0};
static char parsedOperators[5] = {0};

void flushTempArr(char tmpArr[50]){
    for(int i=0; i< 50; i++)
        tmpArr[i]  = 0 ;
}

int tempNumStrIterator=0;

int parsedDoubleArrIterator=0;
int parsedOperatorsIterator=0;


int main(void)
{


    char operator;
    char sourceStr[] = "(17.5 + 8)";

    for(int i = 0; sourceStr[i] != '\0'; ++i) /*iterate over string till \0 */
    {
        if (IsDigit(sourceStr[i]))
        {
            while(sourceStr[i] != '\0' && IsDigit(sourceStr[i]))
            {
                tempNumStr[tempNumStrIterator++] = sourceStr[i];
                ++i;
            }

            sscanf(tempNumStr, "%lf", &parsedDoubleArr[parsedDoubleArrIterator++]);
            flushTempArr(tempNumStr);
            tempNumStrIterator = 0;
        }

        if (IsCalcOperator(sourceStr[i]))
        {
            parsedOperators[parsedOperatorsIterator++] = sourceStr[i];
        }

        else if (IsBracket(sourceStr[i]))
        {
            //do something
            continue;
        }
    }

    //do what you want with parsedDoubleArr and parsedOperators

    return EXIT_SUCCESS;
}
person Eran Peled    schedule 18.05.2019