калкулатор в 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