Skip to content

Instantly share code, notes, and snippets.

@gfreivasc
Created February 24, 2020 14:31
Show Gist options
  • Save gfreivasc/945c8a00f0bfa5db7e487baf4fa7360b to your computer and use it in GitHub Desktop.
Save gfreivasc/945c8a00f0bfa5db7e487baf4fa7360b to your computer and use it in GitHub Desktop.
package com.gabrielfv.sandbox.rpn;
import java.util.Scanner;
import java.util.Stack;
/**
* Calculadora RPN interativa.
*
* RPN = Reverse Polish Notation, ou Notação Polonesa Inversa.
* (https://pt.wikipedia.org/wiki/Nota%C3%A7%C3%A3o_polonesa_inversa)
*
* Cada resultado intermediário durante a execução (ou seja, resultado obtido
* enquanto ainda existem outros números na pilha) é indicado por uma seta ">".
* Quando a pilha for esvaziada pelas operações, uma dupla seta indica o estado
* ">>".
*/
public class RpnCalculator {
private static Scanner scanner = new Scanner(System.in);
/*
* Estrutura de pilha é a estrutura base de uma calculadora
* RPN. Os números são empilhados e cada operação remove os dois
* primeiros elementos da pilha, opera sobre eles e coloca o resultado
* de volta na pilha, para que seja possível prosseguir a operação.
*
* TODO: Ler sobre e entender estruturas de Pilha e Fila
* TODO: Pergunta: O que é FILO e FIFO?
*/
private static Stack<Integer> buffer = new Stack<>();
public static void main(String[] args) {
System.out.println("Digite inicie a operação (s para sair): ");
String input = "";
while (!input.equals("s")) {
input = scanner.next();
if (isInteger(input)) {
buffer.push(Integer.parseInt(input));
} else if (isOperation(input)) {
int current = parseOperation(input, buffer);
if (buffer.size() == 0) {
System.out.println(">> " + current);
} else {
System.out.println("> " + current);
}
buffer.push(current);
}
}
}
// Aqui é necessário entender com cuidado algumas definições de uma RPN.
// Cada operação depende de dois números. Se há apenas um item na pilha
// quando for requisitada a operação, nada deve ocorrer e o numeral deve
// retornar à pilha. Se não houver nada na pilha, o número 0 deve ser
// colocado. O retorno deste método é o que será colocado na pilha. Perceba
// que [buffer.pop()] é chamado duas vezes, indicando que dois números são
// retirados, enquanto apenas um (o resultado entre eles) é colocado de volta.
public static int parseOperation(String operation, Stack<Integer> buffer) {
int result = (buffer.empty()) ? 0 : buffer.pop();
if (!buffer.empty()) {
result = operate(operation, buffer.pop(), result);
}
return result;
}
// É necessário se atentar ao fato de que não é possível dividir por 0.
// Este é um problema comum em programação: Ter de tratar possíveis entradas
// inválidas que o usuário possa introduzir ao programa. No caso optamos por
// indicar o erro e em caso de operação inválida, manter o último número que
// havia anteriormente na pilha.
public static int operate(String operation, int left, int right) {
switch (operation) {
case "+": return left + right;
case "-": return left - right;
case "*": return left * right;
case "/": {
if (right == 0) {
System.out.println("ERR: Cannot divide by 0");
return left;
}
return left / right;
}
default: return left;
}
}
// Para evitar prosseguir para estados inválidos por erro de digitação,
// os métodos abaixo verificam se a entrada do usuário pertence a uma de
// duas possíveis naturezas:
// É um numeral inteiro, ou
// É uma operação reconhecida
// Para que assim possamos prosseguir pro comportamento correto
public static boolean isInteger(String input) {
if (input == null) return false;
try {
Integer.parseInt(input);
} catch (NumberFormatException ex) {
return false;
}
return true;
}
public static boolean isOperation(String input) {
if (input == null) return false;
return input.equals("+") || input.equals("-") || input.equals("*") || input.equals("/");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment