Skip to content

Conversation

@Microvenator
Copy link
Contributor

No description provided.

@Microvenator Microvenator changed the title [DRAFT] C++ practice calculator C++ practice calculator Dec 21, 2025
Copy link
Contributor

@nZver nZver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Правки в рамках ёфикации.

@@ -0,0 +1,167 @@
## Подход к выполнению практики

Вычисление алгебраического выражения состоит из трех этапов: токенизация, перевод в постфиксную форму, вычисление выражения в постфиксной форме.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... трёх ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Привет. Ты оставил 10 однотипных комментариев. Вместо этого лучше оставить один и написать "здесь и везде", "и далее" или что-то такое.

Ёфикацию мы уже обсуждали. Сама я этим заниматься не буду. Максимум - мержить реквесты. Два уже замержено)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ок, мне больше нужно было в механизме review разобраться, а тут такой удобный случай подвернулся. :)

struct Operator
{
std::uint8_t priority = 0;
std::function<double(double, double)> func;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для расширяемости решения лучше, чтобы func() получала стек значений (std::vector<>&). С таким подходом можно добавлять функции с количеством значений отличных от 1.

Ни разу не настаиваю.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предлагаю не усложнять понимание кода и оставить как есть.

std::string value;
};

void save_token(Token & token, std::vector<Token> & tokens)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Функция save_token() выглядит ненужной. Если всё правильно, то токен с TokenType::Invalid может легально встретиться только первым в списке токенов. В остальных случая появление токена такого типа свидетельствует об ошибке. Сейчас save_token() корректно сработает только на первом токене, в остальных случаях просто скрывает ошибку. (Если я не ошибаюсь конечно.)

Предлагаю TokenType::Invalid переименовать в TokenType::Empty. В дальнейшем игнорировать такой токен, если он первый. Во всех остальных случаях сигнализировать об ошибке.

token = Token{};
}

void start_accumulating_number(char symbol, Token & token, std::vector<Token> & tokens)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Глядя на функции start_accumulating_number(), accumulate_number(), accumulate_operator() и accumulate_parenthesis() напрашивается класс Tokenizer. ИМХО, такой подход правильнее.

Не настаиваю, т.к. всё равно делать эти функции из-за мапы, но уже с ссылкой на Tokenizer. А про лямбды мы не рассказывали. На твоё усмотрение.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Переписала все на токенайзер + свич-кейс.

token.value = std::string{symbol};
}

void accumulate_number(char symbol, Token & token, std::vector<Token> & tokens)
Copy link
Collaborator

@khva khva Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIR, в C++23 появилась возможность обозначать неиспользуемые переменные префиксом _. Если _ не работает, то убрать либо закомментировать tokens.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да. Переделала.

token.value = std::string{symbol};
}

struct Transition
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не очень хорошо, что можно создать неинициализированный объект типа Transition. Предлагаю сделать конструктор Transition(State state, TransitionAction act), где:

using TransitionAction = std::function<void(char, Token &, std::vector<Token> &)>;

Тогда конструктор по умолчанию будет недоступен.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Эта штука больше не нужна. Удалила.

std::function<void(char, Token &, std::vector<Token> &)> action;
};

using Row = std::map<State, Transition>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Row — неудачное и неговорящее навание. Может лучше TransitionMap?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Этой штуки больше нет.


SymbolType get_symbol_type(char c)
{
if (std::isdigit(c))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А разве не так нужно делать:

std::isdigit(static_cast<unsigned char>(c))

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не, все в порядке:

int isdigit( int ch );

https://en.cppreference.com/w/cpp/string/byte/isdigit.html

Comment on lines 188 to 191
const SymbolType symbol_type = get_symbol_type(sybmol);

if (symbol_type == SymbolType::Other)
throw std::invalid_argument("Invalid token in expression");
Copy link
Collaborator

@khva khva Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне не очень нравится, что определение типа символа (токена по факту) и соответствующая ошибка происходит вне машины состояний. Это должна быть её часть. Допустим у нас было бы более сложное задание, допускались бы шестнадцатиричные значения и функции. В этом случае что означал бы символ A? Это шестнадцатиричные число или начало имени функции (например ABS)? Обрати внимание, что понять тип токена в случае ABS можно только на символе S.

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

def_type => number => def_type => operator => def_type =>parenthesis => def_type => ...

При этом текущий токен может завершаться при встрече неподходящего символа двумя способами:

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

При добавлении шага определения типа токена токенам не нужно знать друг о друге, что хорошо. Для текущих вводных шаг определения типа описан функцией get_symbol_type().

P.S. И где классы черт подери! :) Пока какой-то Си-стиль получается ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Переделано на класс Tokenizer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants