Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Mateus GP

Rpcal - Uma simples calculadora RPN

Recommended Posts

Fiz uma calculadora muito simples, mas funcional que quero compartilhar para receber conselhos ou talvez criticas (não ofensivas, irônicas, sarcásticas ou inúteis. Como recebi em outros fóruns), para melhorar minhas habilidades. Devemos continuar aprendendo sempre. Ansiar sempre mais é da natureza humana e quando este anseio é benigno deve ser saciado.

Fiz em pouco tempo, por isso há alguns pontos que serão alterados, mas é relevante a sua opinião.

 

/*
*  Copyright 2012 Mateus G. Pereira
*
*  Este arquivo (stack.c) é parte do programa Rpcal
*
*  Rpcal é um software livre; você pode redistribui-lo e/ou
*  modifica-lo dentro dos termos da Licença Pública Geral GNU como
*  publicada pela Fundação do Software Livre (FSF); na versão 2 da
*  Licença, ou (na sua opnião) qualquer versão.
*
*  Este programa é distribuido na esperança que possa ser  util,
*  mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO
*  a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a Licença
*  Pública Geral GNU para maiores detalhes.
*
*  Você deve ter recebido uma cópia da Licença Pública Geral GNU
*  junto com este programa, se não, escreva para a Fundação do Software
*  Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston,
*  MA  02110-1301 USA
*
*/

#include "stack.h"
#include <stdlib.h>

bool push (stack** st, TYPE elm)
{
   stack* nstac;

   nstac = (stack*) malloc( sizeof(stack) );

   if(nstac == NULL)
       return false;

   nstac->element = elm;
   nstac->last = *st;
   *st = nstac;
   return true;
}

TYPE pop (stack** st)
{
   stack* aux;
   TYPE elm;

   if(*st == NULL)
       return (TYPE)0;

   elm = (*st)->element;
   aux = (*st)->last;

   free(*st);
   *st = aux;

   return elm;
}

TYPE top (stack* st)
{
   if(st == NULL)
       return (TYPE)0;

   return st->element;
}

bool emp (stack* st)
{
   return st == NULL;
}

 

/*
*  Copyright 2012 Mateus G. Pereira
*
*  Este arquivo (stack.h) é parte do programa Rpcal
*
*  Rpcal é um software livre; você pode redistribui-lo e/ou
*  modifica-lo dentro dos termos da Licença Pública Geral GNU como
*  publicada pela Fundação do Software Livre (FSF); na versão 2 da
*  Licença, ou (na sua opnião) qualquer versão.
*
*  Este programa é distribuido na esperança que possa ser  util,
*  mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO
*  a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a Licença
*  Pública Geral GNU para maiores detalhes.
*
*  Você deve ter recebido uma cópia da Licença Pública Geral GNU
*  junto com este programa, se não, escreva para a Fundação do Software
*  Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston,
*  MA  02110-1301 USA
*
*/

#ifndef STACK_INCLUDED
#define STACK_INCLUDED
#define TYPE        long double

typedef enum {false = 0, true = 1} bool;

typedef struct stack
{
   TYPE element;
   struct stack* last;
} stack;

bool push (stack**, TYPE);
TYPE pop (stack**);
TYPE top (stack*);
bool emp (stack*);
#endif

 

/*
*  Copyright 2012 Mateus G. Pereira
*
*  Este arquivo (tok.c) é parte do programa Rpcal
*
*  Rpcal é um software livre; você pode redistribui-lo e/ou
*  modifica-lo dentro dos termos da Licença Pública Geral GNU como
*  publicada pela Fundação do Software Livre (FSF); na versão 2 da
*  Licença, ou (na sua opnião) qualquer versão.
*
*  Este programa é distribuido na esperança que possa ser  util,
*  mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO
*  a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a Licença
*  Pública Geral GNU para maiores detalhes.
*
*  Você deve ter recebido uma cópia da Licença Pública Geral GNU
*  junto com este programa, se não, escreva para a Fundação do Software
*  Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston,
*  MA  02110-1301 USA
*
*/

#include "tok.h"
#include <ctype.h>
#include <stdlib.h>

int strmatch (const char* s, const char* m)
{
   while(*m != '\0')
       if(*s++ != *m++)
           return 0;
   return 1;
}

void strnmake (char* s)
{
   char* lk = s;

   while(*lk != '\0')
   {
       if(!isspace(*lk))
           *s++ = toupper((*lk == ',') ? '.':*lk);
       lk++;
   }
   *s = '\0';
}

int strntk (char* d, char* str)
{
   static char* s = NULL;

   *d = '\0';

   if(str != NULL)
       s = str;

   if(s == NULL)
       return 0;

   while(isspace(*s))
       s++;

   if(*s == '\0')
       return 0;

   while(!isspace(*s) && (*s != '\0'))
       *d++ = *s++;

   *d = '\0';
   return 1;
}

 

/*
*  Copyright 2012 Mateus G. Pereira
*
*  Este arquivo (tok.h) é parte do programa Rpcal
*
*  Rpcal é um software livre; você pode redistribui-lo e/ou
*  modifica-lo dentro dos termos da Licença Pública Geral GNU como
*  publicada pela Fundação do Software Livre (FSF); na versão 2 da
*  Licença, ou (na sua opnião) qualquer versão.
*
*  Este programa é distribuido na esperança que possa ser  util,
*  mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO
*  a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a Licença
*  Pública Geral GNU para maiores detalhes.
*
*  Você deve ter recebido uma cópia da Licença Pública Geral GNU
*  junto com este programa, se não, escreva para a Fundação do Software
*  Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston,
*  MA  02110-1301 USA
*
*/

#ifndef TOK_INCLUDED
#define TOK_INCLUDED
int strmatch (const char*, const char*);
int strntk (char*, char*);
void strnmake (char*);
#endif

 

/*
*  Copyright 2012 Mateus G. Pereira
*
*  Este arquivo (rpn.c) é parte do programa Rpcal
*
*  Rpcal é um software livre; você pode redistribui-lo e/ou
*  modifica-lo dentro dos termos da Licença Pública Geral GNU como
*  publicada pela Fundação do Software Livre (FSF); na versão 2 da
*  Licença, ou (na sua opnião) qualquer versão.
*
*  Este programa é distribuido na esperança que possa ser  util,
*  mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO
*  a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a Licença
*  Pública Geral GNU para maiores detalhes.
*
*  Você deve ter recebido uma cópia da Licença Pública Geral GNU
*  junto com este programa, se não, escreva para a Fundação do Software
*  Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston,
*  MA  02110-1301 USA
*
*/

#include "rpn.h"
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

static stack* stk = NULL;
static TYPE res = 0;

bool rp_pop (TYPE* value)
{
   if(emp(stk))
       return false;
   *value = pop(&stk);
   return true;
}

TYPE rp_top ()
{
   return top(stk);
}

bool rp_push (TYPE value)
{
   return push(&stk, value);
}

void rp_free ()
{
   res = 0;
   while(!emp(stk))
       pop(&stk);
}

TYPE rp_res ()
{
   return res;
}

bool op_adc ()
{
   TYPE lvalue, hvalue;

   if(rp_pop(&lvalue))
   {
       if(rp_pop(&hvalue))
       {
           res = hvalue + lvalue;
           return rp_push(res);
       }
       else
           rp_push(lvalue);
   }
   return false;
}

bool op_sub ()
{
   TYPE lvalue, hvalue;

   if(rp_pop(&lvalue))
   {
       if(rp_pop(&hvalue))
       {
           res = hvalue - lvalue;
           return rp_push(res);
       }
       else
           rp_push(lvalue);
   }
   return false;
}

bool op_div ()
{
   TYPE lvalue, hvalue;

   if(rp_pop(&lvalue))
   {
       if(rp_pop(&hvalue))
       {
           res = hvalue / lvalue;
           return rp_push(res);
       }
       else
           rp_push(lvalue);
   }
   return false;
}

bool op_pow ()
{
   TYPE lvalue, hvalue;

   if(rp_pop(&lvalue))
   {
       if(rp_pop(&hvalue))
       {
           res = pow(hvalue, lvalue);
           return rp_push(res);
       }
       else
           rp_push(lvalue);
   }
   return false;
}

bool op_ln ()
{
   TYPE lvalue;

   if(rp_pop(&lvalue))
   {
       res = log(lvalue);
       return rp_push(res);
   }
   return false;
}

bool op_log ()
{
   TYPE lvalue;

   if(rp_pop(&lvalue))
   {
       res = log10(lvalue);
       return rp_push(res);
   }
   return false;
}

bool op_lg ()
{
   TYPE lvalue;

   if(rp_pop(&lvalue))
   {
       res = log2(lvalue);
       return rp_push(res);
   }
   return false;
}

bool op_exp ()
{
   TYPE lvalue;

   if(rp_pop(&lvalue))
   {
       res = exp(lvalue);
       return rp_push(res);
   }
   return false;
}

bool op_cos ()
{
   TYPE lvalue;

   if(rp_pop(&lvalue))
   {
       res = cos(lvalue);
       return rp_push(res);
   }
   return false;
}

bool op_sen ()
{
   TYPE lvalue;

   if(rp_pop(&lvalue))
   {
       res = sin(lvalue);
       return rp_push(res);
   }
   return false;
}

bool op_tan ()
{
   TYPE lvalue;

   if(rp_pop(&lvalue))
   {
       res = tan(lvalue);
       return rp_push(res);
   }
   return false;
}

bool op_inv ()
{
   TYPE lvalue;

   if(rp_pop(&lvalue))
   {
       res = 1/lvalue;
       return rp_push(res);
   }
   return false;
}

bool op_mul ()
{
   TYPE lvalue, hvalue;

   if(rp_pop(&lvalue))
   {
       if(rp_pop(&hvalue))
       {
           res = hvalue * lvalue;
           return rp_push(res);
       }
       else
           rp_push(lvalue);
   }
   return false;
}

 

/*
*  Copyright 2012 Mateus G. Pereira
*
*  Este arquivo (rpn.h) é parte do programa Rpcal
*
*  Rpcal é um software livre; você pode redistribui-lo e/ou
*  modifica-lo dentro dos termos da Licença Pública Geral GNU como
*  publicada pela Fundação do Software Livre (FSF); na versão 2 da
*  Licença, ou (na sua opnião) qualquer versão.
*
*  Este programa é distribuido na esperança que possa ser  util,
*  mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO
*  a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a Licença
*  Pública Geral GNU para maiores detalhes.
*
*  Você deve ter recebido uma cópia da Licença Pública Geral GNU
*  junto com este programa, se não, escreva para a Fundação do Software
*  Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston,
*  MA  02110-1301 USA
*
*/

#ifndef RPN_INCLUDED
#define RPN_INCLUDED
#include "stack.h"

bool rp_push (TYPE);
bool rp_pop (TYPE*);
void rp_free ();
TYPE rp_top ();
TYPE rp_res ();
bool op_adc ();
bool op_sub ();
bool op_div ();
bool op_mul ();
bool op_cos ();
bool op_exp ();
bool op_lg  ();
bool op_log ();
bool op_ln  ();
bool op_pow ();
bool op_tan ();
bool op_sen ();
bool op_inv ();
#endif

 

/*
*  Copyright 2012 Mateus G. Pereira
*
*  Este arquivo é parte do programa Rpcal
*
*  Rpcal é um software livre; você pode redistribui-lo e/ou
*  modifica-lo dentro dos termos da Licença Pública Geral GNU como
*  publicada pela Fundação do Software Livre (FSF); na versão 2 da
*  Licença, ou (na sua opnião) qualquer versão.
*
*  Este programa é distribuido na esperança que possa ser  util,
*  mas SEM NENHUMA GARANTIA; sem uma garantia implicita de ADEQUAÇÂO
*  a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a Licença
*  Pública Geral GNU para maiores detalhes.
*
*  Você deve ter recebido uma cópia da Licença Pública Geral GNU
*  junto com este programa, se não, escreva para a Fundação do Software
*  Livre(FSF) Inc., 51 Franklin St, Fifth Floor, Boston,
*  MA  02110-1301 USA
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "rpn.h"
#include "tok.h"

void rpnhelp()
{
   puts("Calculadora simples utilizando o sistema lógico de Notação Polonesa Reversa\n" \
        "\tajuda\tExibe este texto.\n" \
        "\tsair\tFecha a calculadora.\n" \
        "\tc\tLimpa a memoria.\n" \
        "Funções disponiveis:\n" \
        "\t+, -, *, /, ^, 1/x, log, lg, ln, exp, cos, sen, tan.\n" \
        "Exemplos:\n" \
        "\tAdição: 7 2 +\n" \
        "\tSubtração: 3,5 2 -\n" \
        "\tMultiplicação: 5,6 7,2 *\n" \
        "\tDivisão: 9,23 12 /");
}

bool rpndo (const char* s)
{
   bool nerror = false;
   switch(*s)
   {
   case 'C':
       if(*(s + 1) != 'O')
       {
           rp_free();
           nerror = true;
       }
       break;
   case '^':
       nerror = op_pow();
       break;
   case '+':
       nerror = op_adc();
       break;
   case '*':
       nerror = op_mul();
       break;
   case '/':
       nerror = op_div();
       break;
   case '-':
       if(!isdigit(*(s + 1)) == true)
       {
           nerror = op_sub();
           break;
       }
   default:
       if(((isdigit(*s) || (*s == '-')) && (*(s + 1) != '/')) != false)
           nerror = rp_push(strtod(s, NULL));
       else if(((*(s + 1) == '/') && (*(s + 2) == 'X')) != false)
           nerror = op_inv();
   }

   if(strmatch(s, "AJUDA") != false)
   {
       rpnhelp();
       return true;
   }
   else if(strmatch(s, "LOG") != false)
       nerror = op_log();
   else if(strmatch(s, "LG") != false)
       nerror = op_lg();
   else if(strmatch(s, "LN") != false)
       nerror = op_ln();
   else if(strmatch(s, "EXP") != false)
       nerror = op_exp();
   else if(strmatch(s, "COS") != false)
       nerror = op_cos();
   else if(strmatch(s, "SEN") != false)
       nerror = op_sen();
   else if(strmatch(s, "TAN") != false)
       nerror = op_tan();
   else if(strmatch(s, "SAIR") != false)
       return false;

   if(nerror == false)
       puts("Você forneceu dados insulficientes ou operadores invalidos.\n"
            "Para mais informações digite: ajuda");
   else
   {
       printf("^ %Lf\n", rp_top());
       printf("= %Lf\n", rp_res());
   }
   return true;
}

bool rpnx (char* s)
{
   bool cont;
   char bfr[256];

   cont = strntk(bfr, s);

   while(cont != false)
   {
       strnmake(bfr);
       if(!rpndo(bfr))
           return false;
       cont = strntk(bfr, NULL);
   }
   return true;
}

int main()
{
   char line[256];
   bool nquit;

   puts("Rpcal 0.01 beta - Calculadora RPN\n" \
        "Copyright 2012 Mateus G. Pereira\n");

   do
   {
       fputs("->", stdout);
       fgets(line, 256, stdin);
       nquit = rpnx(line);
   }
   while(nquit);
   rp_free();
   return 0;
}

 

Quase esqueço de dizer que a inspiração para está calculadora surgiu do seguinte tópico: http://forum.imasters.com.br/topic/475682-calculadora-notacao-polonesa/page__view__findpost__p__1890154__fromsearch__1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Gostei da organização.

 

Tenho algumas sugestões simples:

 

- Aproveitar do fato de que o `return 0;` é implícito em C99 e C11. Isto não causaria perda de portabilidade com C89, já que o código atual usa stdbool, que é um recurso destas versões mais novas.

 

- Ao declarar identificadores com tipo de ponteiro para (T), deixar os `*` próximos aos nomes sendo declarados, ao invés do nome do tipo inicial. Isto para não confundir iniciantes (que poderiam pensar que `int* a, b;` declara dois ponteiros para (int), por exemplo).

 

- Não usar cast sobre o retorno de malloc. No caso geral, é desnecessário explicitar a conversão de (void *) para (T *) e vice-versa, sendo (T) um tipo de ponteiro para objeto.

 

- Passar `sizeof *foo` ao invés de `sizeof (T)` como argumento de malloc/similares, sendo `foo` um lvalue de tipo (T *), (T) um tipo de objeto ou função. A justificativa pra isso é que qualquer mudança no tipo do lvalue em questão faria com que a linha quebrasse, enquanto que se a única ligação for relativa ao nome da variável, ela não seria afetada.

 

Pra esclarecer, no seu código:

 

bool push (stack** st, TYPE elm)
{
   stack* nstac;

   nstac = (stack*) malloc( sizeof(stack) );

   if(nstac == NULL)
       return false;

   nstac->element = elm;
   nstac->last = *st;
   *st = nstac;
   return true;
}

 

Minha sugestão é que o seguinte seja feito:

 

bool push(stack **st, TYPE elm)
{
   stack *nstac = malloc(sizeof *nstack);

   if(nstac == NULL)
       return false;

   nstac->element = elm;
   nstac->last = *st;
   *st = nstac;

   return true;
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Gostei da organização.

 

Tenho algumas sugestões simples:

 

- Aproveitar do fato de que o `return 0;` é implícito em C99 e C11. Isto não causaria perda de portabilidade com C89, já que o código atual usa stdbool, que é um recurso destas versões mais novas.

 

- Ao declarar identificadores com tipo de ponteiro para (T), deixar os `*` próximos aos nomes sendo declarados, ao invés do nome do tipo inicial. Isto para não confundir iniciantes (que poderiam pensar que `int* a, b;` declara dois ponteiros para (int), por exemplo).

 

- Não usar cast sobre o retorno de malloc. No caso geral, é desnecessário explicitar a conversão de (void *) para (T *) e vice-versa, sendo (T) um tipo de ponteiro para objeto.

 

- Passar `sizeof *foo` ao invés de `sizeof (T)` como argumento de malloc/similares, sendo `foo` um lvalue de tipo (T *), (T) um tipo de objeto ou função. A justificativa pra isso é que qualquer mudança no tipo do lvalue em questão faria com que a linha quebrasse, enquanto que se a única ligação for relativa ao nome da variável, ela não seria afetada.

 

Pra esclarecer, no seu código:

 

bool push (stack** st, TYPE elm)
{
   stack* nstac;

   nstac = (stack*) malloc( sizeof(stack) );

   if(nstac == NULL)
       return false;

   nstac->element = elm;
   nstac->last = *st;
   *st = nstac;
   return true;
}

 

Minha sugestão é que o seguinte seja feito:

 

bool push(stack **st, TYPE elm)
{
   stack *nstac = malloc(sizeof *nstack);

   if(nstac == NULL)
       return false;

   nstac->element = elm;
   nstac->last = *st;
   *st = nstac;

   return true;
}

 

Muito obrigado pelos conselhos, mas na realidade já os conhecia. Simples costumes e manias que me fazem escrever códigos assim. É como uma assinatura, semelhante a um guitarrista que coloca fitas adesivas nos dedos. Não é uma escolha consciente é automático, algumas vezes nem percebo no momento o que fiz, só depois de reler o código.

Vou tentar seguir suas sugestões, vai ser difícil, mas tentarei.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.