Código Simples

1. Código Simples

jorge
jorgelrs

(usa Outra)

Enviado em 26/02/2018 - 19:22h

Sou iniciante de C++ e estou com duvida no seguinte codigo... Meu problema é nessa parde do "se X for diferente a ALGUM SINAL ". Pois sem essa parte, o codigo funciona totalmente do jeito que eu quero...
Minha finalidade com essa parte " ........if ( x != '-' || '+'........... " é que caso usuario digite outro caracter que nao seja os exirgidos, apareça a mensagem destinada e volte novamente para parte "SINAL" que pede a equação.... Imagino que voces como uruarios avançados possam ter outras ideias, ou comandos mais funcionais que os usados aqui, mas gostaria muito que SE POSSIVEL, apenas corrijam o codigo... queria finalizar meu programa com apenas os comandos que adquiri ate agora... porem se nao tiver correção, e para funcionar tenha que colocar algum outro comando, ficarei muito satisfeito em aprender mais.

segue minha tentativa ↓↓

#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
//projeto de calculadora
inicio:
double a = 0;
char x = 0;
double b = 0;
cout << "primeiro "; cin >> a;
sinal:
cout << "equacao "; cin >> x;
if (x != '+' || '-' || '*' || '/') //se x for diferente de algum dos sinais, executar o texto abaixo
{
cout << "digite uma operacao matematica. Exemplos: + - * /" << endl;
goto sinal; //apos executar, volta a pedir o "sinal"
}
else//se nao for diferente, continua a equacao
{
cout << "segundo "; cin >> b;

/*se o sinal for correspondente a alguma das alternativas abaixo,
tem-se a resposta emprimida na tela, de acordo com sinal desejado. */

if (x == '+')
{
cout << "= " << a + b << endl;
}
if (x == '-')
{
cout << "= " << a - b << endl;
}
if (x == '*')
{
cout << "= " << a * b << endl;
}
if (x == '/')
{
cout << "= " << a / b << endl;
}
}
goto inicio; // volta ao começo "inicio"
}


o problema mesmo é só imprimir na tela que o sinal esta errado, pois quando digito o sinal corretamente, ainda assim imprime na tela a frase que esta incorreto, e a operação volta para o "SINAL" . E mesmo quando mudo o comando para o final do codigo, o codigo é executado todo corretamente, tem-se a resposta imprimida, e logo apos, a mensagem destinada a quando o sinal for digitado incorreto, tambem é impressa...


  


2. Re: Código Simples

Hugo Cerqueira
hrcerq

(usa Outra)

Enviado em 26/02/2018 - 23:56h

Boa noite.

Esse título (Código Simples) é um que tenho visto com frequência por aqui. Pra não ser injusto, reconheço que os anteriores não foram seus, mas gostaria de alertar para a importância de um título menos genérico (não detalhado, mas direcionado). Faço também um apelo para que use as marcações de código "há um botão no editor que cria as tags [ code ] e [ /code ] (sem os espaços), pra ficar fácil de incluir códigos já na notação do vivaolinux. Um código incluído nessas demarcações fica mais visível e com a sintaxe destacada, o que facilita o diagnóstico do problema. Para este tópico específico já fiz o "trabalho sujo" de ajustar isso.

Sobre o código: meu caro, sinto lhe informar que seu programa deve ir urgentemente para a UTI, pois apresenta um quadro crítico. Muitas correções são necessárias para que ele se torne um código "saudável".

Atendendo a seu apelo, não falarei sobre conceitos novos (não abordados no programa que você apresentou) para a resolução do problema, mas darei algumas dicas de pesquisa para que você possa refinar o programa futuramente (nem que seja apenas como aprendizado). Primeiramente, falemos sobre as correções/ajustes:

1. De maneira desnecessária, você inclui a biblioteca stdlib no seu programa, e usando a notação de C. Em C++ seria:

#include <cstdlib> 


De toda forma, ela não é necessária em seu código.

2. Da maneira como o programa está organizado, ele funciona como um loop infinito. Não é boa prática criar esse tipo de programa. Em vez disso crie algum mecanismo para que o usuário possa sair do programa. Uma forma de fazer isso seria pedir ao usuário que digite 0 no primeiro valor, caso deseje sair.

3. Desaconselho veementemente o uso da declaração "goto". Ela é uma maneira muito fácil de inserir bugs no seu programa. Você pode facilmente substituí-los por estruturas de repetição (for, while e do-while). No caso deste programa, creio que o while se encaixaria melhor. Você pode criar loops "infinitos" (entre aspas, pois devem ter uma condição de saída), usando a expressão:

while(1)
{
// Código da aplicação

if (<condição de saída>)
{
break;
}
}


4. Os operadores relacionais "ou" (||) estão sendo usados de maneira equivocada. Ela avalia, em primeira instância o valor antes do operador (neste caso, x != '+') e se esta for avaliada como true (o que equivale a valores diferentes de 0 também), então o segundo operador nem mesmo é avaliado, já que basta que um deles seja avaliado como true (e o primeiro operando já o foi).

Caso contrário (ou seja, se for false), o resultado dessa comparação será relacionado à próxima expressão. Porém a próxima expressão deveria ser x != '-' e não apenas '-' (essa expressão será sempre avaliada como true, pois é diferente de zero). O mesmo se aplica às relações seguintes, com os operadores de multiplicação e divisão.

Assim, a correção da expressão seria:

if (x != '+' || x != '-' || x != '*' || x != '/') 


Porém essa correção não está completa, pois não se encaixa na lógica que você quer. Você quer ver se x é diferente de qualquer um desses sinais. Para isso, deve usar o operador relacional "e" (&&). Assim a expressão correta mesmo seria:

if (x != '+' && x != '-' && x != '*' && x != '/') 


5. Para validação do operador, você pode criar um loop while com condição de saída (que seria o usuário digitar um operador válido). Exemplo:

while (1) 
{
cout << "Operador: ";
cin >> x;
if (x != '+' && x != '-' && x != '*' && x != '/')
{
cout << "Digite um operador válido (+,-,*,/)\n";
continue;
}
break;
}


Perceba o uso de break e continue para controlar o fluxo: caso o operador seja inválido, entrará no if, que acionará a declaração "continue", que voltará para o início do loop. Caso contrário, não entrará no bloco if, e portanto irá direto para a declaração "break", que fará com que saia do loop.

6. Mova as declarações das variáveis a,b e x para fora de qualquer loop que tenha criado na função main.

7. Prefira nomes de variáveis mais descritivos. a, x e b não dizem muita coisa. Em programas pequenos isso pode ser uma bobagem, mas experimente fazer depurações em programas grandes e complexos, cujos nomes das variáveis não dizem nada sobre seu significado. Desde cedo é bom criar o costume de usar variáveis com nomes descritivos. Assim, você poderia chamar a de operando1, b de operando2 e x de operador. Ficaria mais fácil de identificar.

8. Use textos mais descritivos. Lembre-se, o usuário precisa de instruções claras (com elas, ele já tem o costume de quebrar o programa, imagine sem). Dessa forma, em vez de "primeiro", poderia usar o texto "Digite o primeiro operando: ". O mesmo para o operador e para o segundo operando.

9. Por questões de legibilidade do código, evite colocar dois comandos na mesma linha.

Agora, algumas dicas que estão além dos conceitos usados (considere isso um extra):

1. Quando você tem um conjunto pré-definido de textos ou caracteres, considere usar os tipos enumerados. Eles são um conjunto de valores nomeados que correspondem, cada um a um valor inteiro. Eles facilitam a manipulação desses conjuntos chamando os valores por seus nomes, o que atribui significado ao valor e diminui o risco de erros decorrentes de manutenção.

Para mais informações sobre os tipos enumerados, vide:

http://www.cplusplus.com/doc/tutorial/other_data_types/ (seção "Enumerated types (enum)")

Então perceba que poderá trabalhar com uma estrutura deste tipo:

typedef enum 
{
mais=1,
menos,
vezes,
divididopor
} sinais;


Atenção: Este código deve ficar fora da função main (antes dela, especificamente).

Com isso você pode pedir ao usuário que digite um valor entre 1 e 4:

cout << "Operador (" << mais << ":soma " 
<< menos << ":subtração "
<< vezes << ":multiplicação "
<< divididopor << ":divisão): ";
cin >> x;


Nesse exemplo, x deve ser uma variável do tipo inteiro. Portanto declare-o como int, e também declare uma variável do tipo "sinais" que será um tipo enum definido por você, e que será alimentada pelo valor de x:

int x = 0;
sinais operador;


Depois que o usuário preenche o valor de x, você usa esse valor para preencher o operador, fazendo uma conversão explícita:

operador = (sinais) x; 


E então, para verificar se o operador está errado, faria a comparação:

if (operador < mais || operador > divididopor) 


Mais legível, não? Se x for menor do que mais (1) ou maior do que divididopor (4), então o operador é inválido.

2. Em vez da estrutura com vários "if"s no final, um para cada operador, que tal usar um switch?

Para sintaxe do switch, vide: http://www.cplusplus.com/doc/tutorial/control/ (seção "Another selection statement: switch.")

Com essa estrutura você pode facilmente determinar as ações para cada operador (usando o tipo enumerado preferencialmente):

switch (operador) {
case 1:
cout << "= " << a + b << endl;
break;
case 2:
cout << "= " << a - b << endl;
break;
case 3:
cout << "= " << a * b << endl;
break;
case 4:
cout << "= " << a / b << endl;
break;
}


Sinto pela resposta longa, mas como costumo dizer, uma meia explicação pode ser pior do que nenhuma.

---

Atenciosamente,
Hugo Cerqueira


3. Re: Código Simples

Paulo
paulo1205

(usa Ubuntu)

Enviado em 27/02/2018 - 11:25h

hrcerq escreveu:

5. Para validação do operador, você pode criar um loop while com condição de saída (que seria o usuário digitar um operador válido). Exemplo:

while (1) 
{
cout << "Operador: ";
cin >> x;
if (x != '+' && x != '-' && x != '*' && x != '/')
{
cout << "Digite um operador válido (+,-,*,/)\n";
continue;
}
break;
}


Perceba o uso de break e continue para controlar o fluxo: caso o operador seja inválido, entrará no if, que acionará a declaração "continue", que voltará para o início do loop. Caso contrário, não entrará no bloco if, e portanto irá direto para a declaração "break", que fará com que saia do loop.


Acho que, nesse caso, a coisa poderia um pouco mais simples, e também mais lógica.

O comando continue serve para instruir algo mais ou menos assim: “comece uma nova iteração do laço de repetição, sem executar nenhum dos comandos que vêm a seguir”. Só que, no exemplo acima, o único comando que vem a seguir é um que faz com que o laço de repetição seja antecipadamente interrompido (break). Penso, portanto, que seria mais lógico inverter a condição testada, e simplesmente eliminar o continue.

while(true){
cout << "Digite o operador ('+', '-', '*' ou '/'): ";
cin >> op;
if(op=='+' || op=='-' || op=='*' || op=='/')
break;
cerr << "Você não digitou um operador válido.\n";
}


( ... )

1. Quando você tem um conjunto pré-definido de textos ou caracteres, considere usar os tipos enumerados. Eles são um conjunto de valores nomeados que correspondem, cada um a um valor inteiro. Eles facilitam a manipulação desses conjuntos chamando os valores por seus nomes, o que atribui significado ao valor e diminui o risco de erros decorrentes de manutenção.

(...)

Então perceba que poderá trabalhar com uma estrutura deste tipo:

typedef enum 
{
mais=1,
menos,
vezes,
divididopor
} sinais;


Atenção: Este código deve ficar fora da função main (antes dela, especificamente).

Com isso você pode pedir ao usuário que digite um valor entre 1 e 4:

cout << "Operador (" << mais << ":soma " 
<< menos << ":subtração "
<< vezes << ":multiplicação "
<< divididopor << ":divisão): ";
cin >> x;


Embora eu concorde que enumerações são úteis, eu discordo bastante de seu uso nesse caso específico. Os caracteres escolhidos são suficientemente claros e descritivos, e não creio que se justifique criar símbolos a mais no programa.

Para ilustrar isso, permita-me “sofisticar” um pouquinho mais o seu exemplo, amarrando o tipo de dado por trás de enumeração, e usando valores constantes inteiros ligeiramente diferentes de 1, 2, 3 e 4.

enum operacoes: char {
mais='+',
menos='-',
vezes='*',
dividido='/'
};

/* ... */

int main(){
/* ... */
char op;
while(true){
cout
<< "Escolha a operação ['"
<< mais << "' para soma, '"
<< menos << "' para subtração, '"
<< vezes << "' para multiplicação ou '"
<< dividido << "' para divisão]: "
;
// Onde está o ganho de enum no bloco acima, se você continua tendo (ou passa a ter!)
// um acoplamento manual entre o texto explicativo da operação e o código do operador
// dentro da enumeração? Ainda que você tivesse de explicar que multiplicação é com
// “*” em vez de com “×” e divisão é com “/” em vez de com “÷”, não vejo como o uso de
// “"'" << vezes << "' para multiplicação"” é melhor que “"'*' para multiplicação"” no que
// respeite a manutenção de código.
cin >> op;
if(op==mais || op==menos || op==vezes || op==dividido)
break;
cerr << "Operação '" << op << "' inválida!\n";
}
/* ... */
switch(static_cast<operacoes>(op)){
case mais: // Isso é melhor do que “case '+'”?
// bla bla bla
break;
case menos: // Isso é melhor do que “case '-'”?
// ble ble ble
break;
case vezes: // Isso é melhor do que “case '*'”?
// bli bli bli
break;
case dividido: // Isso é melhor do que “case '/'”?
// blo blo blo
break;
}
/* ... */
}


E a legibilidade piora mais ainda se imaginarmos que o código algum dia vá ser lido por alguém que fale outro idioma. Os sinais matemáticos são mais universais que palavras numa determinado língua.

Nesse exemplo, x deve ser uma variável do tipo inteiro. Portanto declare-o como int, e também declare uma variável do tipo "sinais" que será um tipo enum definido por você, e que será alimentada pelo valor de x:

int x = 0;
sinais operador;


Depois que o usuário preenche o valor de x, você usa esse valor para preencher o operador, fazendo uma conversão explícita:

operador = (sinais) x; 


Com todo o respeito, acho que aqui a coisa ficou ainda um pouco pior, com essa duplicação de variáveis e com a conversão de tipos ao estilo de C.

Uma forma mais pura, por assim dizer, seria sobrecarregar o operador >> para poder ler diretamente um dado do tipo enumerado, o que permitiria trabalhar com uma variável só e evitar conversões de tipo. No (contra)exemplo que eu mostrei acima, eu mesmo não fiz isso.

E então, para verificar se o operador está errado, faria a comparação:

if (operador < mais || operador > divididopor) 


Mais legível, não? Se x for menor do que mais (1) ou maior do que divididopor (4), então o operador é inválido.


Essa é possivelmente a única vantagem de fazer do modo como você fez. Mas existem formas alternativas de fazer:

  1. Usando char para o operador, como no programa original, pode-se fazer “if(strchr("+-*/", op))”.

  2. Em vez de enumeração, usar std::set<char> constante contendo os símbolos das operações válidas (ou std::map<char>, const char *>, para guardar também um texto descritivo).
#include <map>

const std::map<char, const char *> operacoes_validas{
make_pair('+', "soma"), make_pair('-', "subtração"),
make_pair('*', "multiplicação"), make_pair('/', "divisão")
};

int main(){
/* ... */
char op;
while(true){
cout << "Escolha a operação [";
int sep=0;
for(const auto &op_txt: operacoes_validas)
cout << (sep++? "": "; ") << '\'' << op_txt.first << "' para " << op_txt.second;
cout << "]: ";
cin >> op;
if(operacoes_validas.find(op))
break;
cerr << "A operação '" << op << "' é inválida.\n";
}
/* ... */
switch(op){
case '+': /*...*/
/* ... */
}
/* ... */
}


  3. Estendendo a ideia do mapa, pode-se incluir também um ponteiro de função e eliminar até mesmo o switch ou a tripa de ifs, concentrando mais ainda os locais onde manutenção possa ser necessária.
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <string>

using namespace std;


/*
Bloco do programa que define um tipo de dados para representar
e efetuar operações aritméticas em um par de valores do tipo
double.
*/
struct nome_operacao {
string nome;
function<double(double, double)> operacao;
};

ostream &operator<<(ostream &os, const nome_operacao &no){
return os << no.nome;
}

/*
Bloco do programa que lida com operações conhecidas.

Para remover, acrescentar ou modificar operações, só se precisa mexer
neste ponto do programa, e mais nada.
*/
const map<char, nome_operacao> operacoes_validas{
make_pair('+', nome_operacao{ "soma", [](double x, double y) -> double { return x+y; } }),
make_pair('-', nome_operacao{ "subtração", [](double x, double y) -> double { return x-y; } }),
make_pair('*', nome_operacao{ "multiplicação", [](double x, double y) -> double { return x*y; } }),
make_pair('/', nome_operacao{ "divisão", [](double x, double y) -> double { return x/y; } })
};

ostream &operator<<(ostream &os, const pair<char, nome_operacao> &op){
return os << '\'' << op.first << "' para " << op.second;
}

ostream &operator<<(ostream &os, const map<char, nome_operacao> &op_map){
if(op_map.size()>0){
auto op_i=op_map.cbegin();
os << *op_i++;
while(op_i!=op_map.cend())
os << "; " << *op_i++;
}
return os;
}


/**** Programa principal, que usa as definições dos outros blocos. ****/

// Função auxiliar para ler um valor do tipo double, e que trata diferentes
// casos de erro.
bool le_double(const char *msg, double &x){
while(true){
cout << msg;
if(cin >> x)
return true;
if(cin.eof()){
cerr << "Fim dos dados. Abortando.\n";
return false;
}
if(cin.bad()){
cerr << "Erro de leitura. Fonte de dados corrompida. Abortando.\n";
return false;
}
cerr << "Dado digitado não é numérico. Tente novamente.\n";
cin.clear(); // Remove indicação de erro.
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // Descarta caracteres até o fim da linha.
}
}

int main(){
double a, b;
char op;
if(!le_double("Digite o valor do primeiro operando: ", a))
return 1;
while(true){
cout << "Digite a operacao [" << operacoes_validas << "]: ";
cin >> op;
if(operacoes_validas.count(op))
break;
if(cin.eof()){
cerr << "Fim dos dados. Abortando.\n";
return 1;
}
if(cin.bad()){
cerr << "Erro de leitura. Fonte de dados corrompida. Abortando.\n";
return 1;
}
cerr << "Operação '" << op << "' inválida. Tente novamente.\n";
cin.clear(); // Remove indicação de erro.
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // Descarta caracteres até o fim da linha.
}
if(!le_double("Digite o valor do segundo operando: ", b))
return 1;
cout << a << op << b << '=' << operacoes_validas.at(op).operacao(a, b) << '\n';
}


Em nome da legibilidade e com os devidos tratamentos de erros, o programa, em todo caso, acaba crescendo. Esse meu último exemplo quase não lembra o programa original.


4. Re: Código Simples

jorge
jorgelrs

(usa Outra)

Enviado em 27/02/2018 - 12:11h

interessante... basicamente troquei "e" por "ou" kk que cagada... na minha cabeça fazia tanto sentido, e um erro tao simples assim eu nao vi... agradeço muito voces pelos esclarecimentos. Foi bem explicativo, muito bom mesmo... basicamente eu queria colocar em pratica oq aprendi no começo do meu curso, independente de estar inlegivel ou mal estruturado, so queria ver se minha logica se encaixava na de um programador, e se eu tinha absorvido corretamente as informaçoes... vou aderir as dicas e boas praticas de programação, muito obrigado pela força... voces sao 10/10 kk


5. Re: Código Simples

Hugo Cerqueira
hrcerq

(usa Outra)

Enviado em 27/02/2018 - 12:40h

paulo1205 escreveu:

Em nome da legibilidade e com os devidos tratamentos de erros, o programa, em todo caso, acaba crescendo. Esse meu último exemplo quase não lembra o programa original.


Como sempre, suas respostas são uma aula. Agradeço pelas considerações e vejo que realmente cometi alguns deslizes na minha proposta de solução.

Apenas para me explicar, tentei não fugir muito do básico. Estruturas genéricas e sobrecarga de operadores realmente são mais lógicos nesse caso, mas estariam em nível avançado para alguém que ainda está se familiarizando com a sintaxe básica, não? De toda forma, como você mesmo disse no final, o programa inevitavelmente acaba crescendo quando se tenta sofisticar demais.

---

Atenciosamente,
Hugo Cerqueira






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts