Chris.Harrison2603 Ответов: 3

Проблемы с переключателем / корпусом


У меня есть несколько проблем, я создал программу calculatir для моих студентов, чтобы посмотреть на нее, чтобы получить представление о процедурном программировании.

Проблемы, с которыми я сталкиваюсь, связаны с тем, что вычисления не работают правильно, когда вызываются функции/процедуры, и я не могу получить результаты отображения для отображения ответа.

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

Я пытался решить большую часть этого вечера, глядя на разные подходы без радости.

Спасибо

<pre>
#include <iostream>

double a, b;
double calResult;

char showMenu(void);
void addCal(void);
void subCal(void);
void mulCal(void);
void divCal(void);
void displayResult(void);
//void clearBuffer(void);

int main()
{
	char menu;
	

	std::cout << "Welcome to a basic C++ calculator\nEnter in two numbers you wish to perform a calculation on.\nThen select the option you which to perform\n\n";
	std::cout << "Enter first number: ";
	std::cin >> a;
	std::cout << "Enter second number: ";
	std::cin >> b;
	
	do
	{
		menu = showMenu();
		switch (menu)
		{
		case 1:
			addCal();
				
			//clearBuffer();
			break;

		case 2:
			subCal();
			//clearBuffer();
			break;

		case 3:
			mulCal();
			//clearBuffer();
			break;

		case 4:
			divCal();
			//clearBuffer();
			break;

		}
		displayResult();
		std::cout << "\n";

	}

	while (menu != 'X' && menu != 'x');
	{
		std::cout << "Thank you!\nGoodbye!";
		
	}
}

char showMenu(void)
{
	char option;

	std::cout << "Please select a calculation option.\n";
	std::cout << "1 - Addition\n";
	std::cout << "2 - Subtraction\n";
	std::cout << "3 - Multiplication\n";
	std::cout << "4 - Division\n";
	std::cout << "X - eXit\n\n";
	std::cout << "Enter choice (1,2,3,4 or X): ";
	std::cin >> option;
	return option;
}

void addCal(void)
{
	double add;

	add = a + b;
	add = calResult;
		
}

void subCal(void)
{

	double sub;

	sub = a - b;
	sub = calResult;
	

}

void mulCal(void)
{

	double mul;

	mul = a * b;
	mul = calResult;
	
}

void divCal(void)
{

	double div;

	div = a / b;
	div = calResult;
	
}

void displayResult(void)
{
	std::cout << "The anwser is: ", calResult;
	
}

/*void clearBuffer(void)
{
	while (getchar() != '\n');
}*/


Что я уже пробовал:

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

Я также попытался выделить результаты из add, sub, mul и div в новую переменную, которая будет использоваться в результате отображения, но из-за того, что вычисления не работают, она возвращает значение 0.0000000 (используется break для проверки).

Stefan_Lang

Когда я учился в университете, я обнаружил, что слишком сложные примеры не очень хороши, когда все, что вы хотите показать, - это одна языковая функция или концепция программирования.

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

Я хорошо знаю, что вы не можете составить осмысленные примеры кода всего за 10-15 строк. Но в этом нет необходимости. Вы можете оставить значимые вещи для упражнений.

P.S.: Я мог бы также добавить, что примеры кода не требуются для фактического запуска, хотя, конечно, это хорошая идея, чтобы убедиться, что по крайней мере сегмент, который вы показываете своим студентам, делает то, что он должен делать.

3 Ответов

Рейтинг:
9

Stefan_Lang

Если вы хотите объяснить процедурное программирование, вам следует почитать хорошую книгу на эту тему или Википедию по адресу Процедурное программирование - Википедия[^]

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

Если этот код предназначен в качестве примера для студентов, вы должны устранить следующие проблемы:
1. Не используйте глобальные переменные.
а) если у вас есть функции, требующие ввода, передайте их в качестве аргументов функций. Таким образом, вы можете видеть прямо из своего кода, что обработка этой функции зависит от этих параметров.
б) аналогично, если функция вычисляет некоторый результат, передайте его обратно вызывающему объекту в качестве возвращаемого значения. Опять же, это улучшает читабельность: назначение результата функции неявно ясно; чтение результата из какой-то явно не связанной глобальной переменной-нет.
Если вы будете следовать этим правилам, вам никогда не понадобятся глобальные переменные.

2. Назовите свои функции соответствующим образом. Точка на всякий случай, см. ниже:

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

Точка в случае: функция showMenu() возможно, изначально предполагалось просто распечатать меню, из которого пользователь может выбирать. Однако в какой-то момент вы также добавили чтение пользовательского ввода. Это немного, но эта часть явно не охвачена именем функции и очевидным первоначальным назначением этой функции. Линия menu=showMenu() это сбивает с толку, потому что вы не ожидаете никакого возврата от вызываемой функции show...(). Либо переименуйте его, чтобы отразить его расширенную функциональность (например. getCommand()), или лучше, извлеките часть, считывающую пользовательский ввод, в отдельную функцию.

4. Не используйте однобуквенные имена переменных. Хотя в некоторых случаях это может быть нормально для небольших программ, подобных этим, если вы используете эти программы для обучения, вы должны убедиться, что код настолько легко читается, насколько это возможно. Переименование a к first_operand и b к second_operand может показаться скучным, но тогда сразу становится ясно, что они означают, и они не отвлекут ваших студентов от реальной проблемы, на которой вы хотели бы сосредоточиться.

4.а) не сокращайте! Всегда помните, что другие люди не разделяют вашу справочную информацию и могут не понимать никаких аббревиатур так же ясно, как вы. Или вообще. В коде нет никаких преимуществ для использования сокращенного имени переменной или функции, поскольку современные Редакторы обеспечивают автозаполнение. (и если ваш редактор не обеспечивает автозаполнение, получите тот, который делает это немедленно!). Ваша функция addCal, subCal и т. д. заставляют меня задуматься, что означает суффикс Cal значит? Вы ведь не добавляете калькуляторы, не так ли? В любом случае, если вы последуете моему совету по пункту 1, вы должны передать два операнда в качестве аргументов функции, и тогда будет неявно ясно, что добавляется или вычитается (напоминая мне: use 'subtract', нет 'sub- последнее используется во многих различных контекстах и может быть неоднозначным).

5. почти забыл, что один: не используйте 'using namespace' ! Пространства имен используются для устранения неоднозначности имен и символов из разных источников. С 'using namespace- вы отключаете этот механизм и увеличиваете вероятность конфликтов. Кроме того, строку кода, которая вызывает функцию из другого пространства имен, легче понять, когда она полностью определена (т. е. имеет присоединенный объект namespacee). Без информации о пространстве имен, из которого он создан, читатель должен догадаться, какая это функция или символ, или где найти дополнительную информацию.

P.S.: разумной альтернативой "использованию пространства имен" являются директивы using: замените свой оператор using следующим кодом, и Вы хороши:

using std::cout;
using std::endl;
using std::cin;

6. Просто незначительная вещь: напечатать перевод строки, не для печати '\п', использовать
std::cout << std::endl;
Первые могут работать практически везде, но вторые гарантированно работают везде. Помните, что в некоторых ОС предпочтительным стилем взлома линий является \r\n, а не \n

Наконец, немного кода, чтобы показать то, что я объяснил выше:
#include <iostream>
double add(double const first_operand, double const second_operand)
{
    return first_operand + second_operand;
}
void show_commands()
{
    std::cout << "Select option from the list below:" << std::endl;
    std::cout << "'a' - add two numbers" << std::endl;
    // expand here as neccessary
    std::cout << "'q' - quit" << std::endl;
}
char get_command()
{
    char command;
    do
    {
        std::cin >> command;
        switch (command)
        {
            case 'a': //command recognized
            break;

            case 'q': //command recognized
            break;

            default:  //command not recognized
            command = 0;
            std::cout << "Please select valid command ('q' to quit)." << std::endl;
            break;
        };
    } while (command == 0);
    return command;
}
double read_value()
{
    double value = 0.0;
    std::cout << "Please enter numeric value:" << std::endl << "> ";
    std::cin >> value;
    return value;
}
void show_result(double const result)
{
    std::cout << "The result is: " << result << std::endl;
}
void calculator()
{
    char command; // must be declared here to use it in the while() condition below
    do
    {
        show_commands();
        command = get_command();
        switch (command)
        {
            case 'a':
            {
                double first_operand = read_value();
                double second_operand = read_value();
                double result = add(first_operand, second_operand);
                show_result(result);
            }
            break;
            
            default:
            command = 0; // signal to get out of loop
            break;
        };
    } while (command != 0);
}


Когда вы переходите к объектно-ориентированному программированию , вы можете немного формализовать приведенный выше код, введя объект command, который позволяет вам обрабатывать все эти операции печати меню и переключения немного более элегантно. Но это для другого QA ;-)


P.S.: проспав над этим, я пришел к выводу, что вы не должны использовать std::cout и std::cin в вашем примере: они показывают, как использовать объекты, следуя объектно-ориентированной парадигме. Если ваша цель-представить процедурное программирование, вы не должны разбавлять свой пример битами ОО-кода!

Сказав это, почему бы не упростить все и просто сделать это:
#include <stdio.h>
void hello(char const* name)
{
    printf("Hello procedural %s!\n", name);
}
int main()
{
    hello("world"); // procedural call
    return 0;
}


Richard MacCutchan

Отличный ответ. Однако я посоветовал "использовать пространство имен std", поскольку это кажется разумным в простом консольном приложении. Но я принимаю вашу точку зрения о возможных конфликтах, если ссылаются на несколько пространств имен.

Maciej Los

Соглашаться.

Stefan_Lang

Согласитесь, использование пространства имен std допустимо в исходном файле (никогда в заголовке), если речь идет о небольших программах. Я упомянул об этом только потому, что код используется в качестве примера для студентов, и я не хотел рекламировать сомнительную практику.

P.S.: Я только что понял, что использование директив (например, использование std::cout; ) совершенно нормально. Добавлю это к моему решению...

Richard MacCutchan

Учитывая пример кода, я думаю, что там уже более чем достаточно сомнительных практик. :)

Рейтинг:
25

Patrice T

У вас есть несколько проблем в этом коде.
В showMenu, Вы читаете опцию меню как символ, проблема в том, что цифра как символ записывается как "1".
Таким образом, ваш код должен быть изменен с

menu = showMenu();
switch (menu)
{
case 1:
    addCal();

    //clearBuffer();
    break;

case 2:
    subCal();
    //clearBuffer();
    break;

case 3:
    mulCal();
    //clearBuffer();
    break;

case 4:
    divCal();
    //clearBuffer();
    break;

}

к
menu = showMenu();
switch (menu)
{
case '1':
    addCal();

    //clearBuffer();
    break;

case '2':
    subCal();
    //clearBuffer();
    break;

case '3':
    mulCal();
    //clearBuffer();
    break;

case '4':
    divCal();
    //clearBuffer();
    break;

}

Изучение отладчика поможет вам понять, что делают ваши программы.

Ваш код ведет себя не так, как вы ожидаете, или вы не понимаете, почему !

Существует почти универсальное решение: запускайте свой код на отладчике шаг за шагом, проверяйте переменные.
Отладчик здесь, чтобы показать вам, что делает ваш код, и ваша задача-сравнить с тем, что он должен делать.
В отладчике нет никакой магии, он не знает, что должен делать ваш код, он не находит ошибок, он просто помогает вам, показывая, что происходит. Когда код не делает того, что ожидается, вы близки к ошибке.
Чтобы увидеть, что делает ваш код: просто установите точку останова и посмотрите, как работает ваш код, отладчик позволит вам выполнять строки 1 на 1 и проверять переменные по мере их выполнения.

Отладчик - Википедия, свободная энциклопедия[^]

Освоение отладки в Visual Studio 2010 - руководство для начинающих[^]
Базовая отладка с помощью Visual Studio 2010 - YouTube[^]

1.11 — отладка программы (пошаговое выполнение и останова) | выучить C++[^]

Отладчик здесь только для того, чтобы показать вам, что делает ваш код, и ваша задача-сравнить его с тем, что он должен делать.


Chris.Harrison2603

Спасибо, постараюсь это позже сегодня

Maciej Los

Хорошо объяснил!

Patrice T

Спасибо

Рейтинг:
16

Richard MacCutchan

Почему вы используете глобальные переменные, а не локальные, и передаете их каждой из функций калькулятора? И каждая функция должна возвращать свое значение вызывающему объекту. Таким образом, вы должны иметь что-то более похожее:

<pre>
#include <iostream>

using namespace std;   // so you don't need std:: everywhere

// lines removed for readability

int main()
{
	char menu;
    double a, b;
    double calResult;
	

	std::cout << "Welcome to a basic C++ calculator\nEnter in two numbers you wish to perform a calculation on.\nThen select the option you which to perform\n\n";
	std::cout << "Enter first number: ";
	std::cin >> a;
	std::cout << "Enter second number: ";
	std::cin >> b;
	
	do
	{
		menu = showMenu();
		switch (menu)
		{
		case '1':  // as Patrice pointed out showMenu returns a char not an int
			calResult = addCal(a, b);
				
			break;

// lines removed for readability
		}
		displayResult(calResult);
		cout << "\n";

	}

	while (menu != 'X' && menu != 'x');
	{
		cout << "Thank you!\nGoodbye!"; // don't need std:: prefix
		
	}
}

char showMenu(void)
{
// lines removed for readability
}

double addCal(double number1, double number2)
{
	double add;

	add = number1 + number2;
	return add;
		
}

void displayResult(double number)
{
	cout << "The anwser is: ", number;
	
}


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


Maciej Los

Отличный ответ!

Кстати: моя бабушка обычно говорила: "ты учишься всю свою жизнь, но умираешь, будучи глупым". Это не идеальный перевод, но я надеюсь, что он имеет смысл. Другими словами, процесс обучения должен быть непрерывным.

Richard MacCutchan

Спасибо. Я уверен, что эта фраза звучит лучше по-польски, но я полностью согласен с этим чувством.

Maciej Los

:)