수식을 계산할 때 곱셈과 덧셈 중 곱셈을 먼저하는 등, 우선순위가 있는 기호들이 있다는 건 기본이죠.그런데 그것을 알고리즘으로 구현하려고 하니까, 쉽게 되지 않고 코드만 복잡해지고 결과는 실패하는 일이 많았습니다.

 

그때 떠오른 것이 바로 자료구조!!

 

자료구조 수업을 들었던 게 몇 년 전인데 신기하게 떠오르더군요. 자료구조에서 후위표기식이라는 것이 있는데, 그것을 적용하여 입력을 계산하니 옳바른 결과가 나왔습니다.

 

후위표기식에 대해서 더 자세히 알아보시려면 아래를 참고하세요.

https://jamanbbo.tistory.com/54

 

[Stack]사칙연산 계산기 구현(2) - 후위 표기 수식 계산

저번 포스팅에서는 사칙연산 계산기 프로그램을 만들기 위한 중위 표기식을 후위 표기법을 이용해 수식을 표현하는 방법을 알아보았다. [Stack]사칙연산 계산기 구현(1) - 후위 표기법 이제 후위 표기 수식을 계산..

jamanbbo.tistory.com

 

코드

CalculateHelper.java

 

package com.example.calculator;

import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Stack;

//https://jamanbbo.tistory.com/54의 소스코드를 참조, 수정하였음
public class CalculateHelper {
public static double num1;
public static double num2;
public static double resultNumber;

//사용자의 input을 각각 구분하여 ArrayList에 저장하는 메소드
private ArrayList splitTokens(String equation) {
String[] constant = equation.split(" "); //공백을 기준

ArrayList constantList = new ArrayList();
double number = 0;

boolean flag = false;
for (String data : constant) {
if (data.equals(" ")) {
continue;
}
if (checkNumber(data)) {
number = number * 10 + Double.parseDouble(data);
flag = true;
} else {
if (flag) {
constantList.add(number);
number = 0;
}
flag = false;
constantList.add(data);
}
}

if (flag) {
constantList.add(number);
}

return constantList;
}

//후위 표기식으로 변형
private ArrayList infixToPostfix(ArrayList constant) {
ArrayList result = new ArrayList();
HashMap level = new HashMap();
Stack stack = new Stack();

 

//각 기호의 우선순위 레벨. 곱하기, 나누기 > 더하기, 빼기 > 기타
level.put("*", 3);
level.put("/", 3);
level.put("+", 2);
level.put("-", 2);
level.put("(", 1);

for (Object object : constant) {
if (object.equals("(")) {
stack.push(object);
} else if (object.equals(")")) {
while (!stack.peek().equals("(")) {
Object val = stack.pop();
if (!val.equals("(")) {
result.add(val);
}
}
stack.pop();
} else if (level.containsKey(object)) {
if (stack.isEmpty()) {
stack.push(object);
} else {
if (Double.parseDouble(level.get(stack.peek()).toString()) >= Double.parseDouble(level.get(object).toString())) {
result.add(stack.pop());
stack.push(object);
} else {
stack.push(object);
}
}
} else {
result.add(object);
}
}

while (!stack.isEmpty()) {
result.add(stack.pop());
}

return result;
}

//후위 표기식을 계산
private Double postFixEval(ArrayList expr) {
Stack numberStack = new Stack();
for (Object o : expr) {
if (o instanceof Double) {
numberStack.push(o);
} else if (o.equals("+")) {
num1 = (Double) numberStack.pop();
num2 = (Double) numberStack.pop();
numberStack.push(num2 + num1);
} else if (o.equals("-")) {
num1 = (Double) numberStack.pop();
num2 = (Double) numberStack.pop();
numberStack.push(num2 - num1);
} else if (o.equals("*")) {
num1 = (Double) numberStack.pop();
num2 = (Double) numberStack.pop();
numberStack.push(num2 * num1);
} else if (o.equals("/")) {
num1 = (Double) numberStack.pop();
num2 = (Double) numberStack.pop();
numberStack.push(num2 / num1);
}
}

resultNumber = (Double) numberStack.pop();

return resultNumber;
}

public Double process(String equation) {
ArrayList postfix = infixToPostfix(splitTokens(equation));
Double result = postFixEval(postfix);
return result;
}

public boolean checkNumber(String str) {
char check;

if (str.equals(""))
return false;

for (int i = 0; i < str.length(); i++) {
check = str.charAt(i);
if (check < 48 || check > 58) {
if (check != '.')
return false;
}
}
return true;
}
}

 

설명

각 메소드에 대한 것은 주석을 읽어주세요!

 

크게 복잡한 것은 없습니다. MainActivity에서 사용자의 입력이 기록된 textView의 텍스트를 CalculateHelper로 전달하면, CalculateHelper에서는 그것을 후위표기식으로 변환 후 계산까지 하고 MainActivity로 리턴합니다.

 

궁금하신 점이 있으시다면 댓글로 남겨주세요.

 

 

xml코드

activity_main.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1.5"
android:orientation="vertical">

<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">

<TextView
android:id="@+id/first_textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:text=""
android:textSize="30dp" />
</ScrollView>

<TextView
android:id="@+id/second_textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:layout_weight="2"
android:text=""
android:textSize="30dp" />

</LinearLayout>

<GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:columnCount="4"
android:orientation="horizontal"
android:rowCount="5">

<Button
android:id="@+id/clear"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="C" />

<Button
android:id="@+id/bracket"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="( )" />

<Button
android:id="@+id/percent"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="%" />

<Button
android:id="@+id/div"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="÷" />


<Button
android:id="@+id/num7"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="7" />

<Button
android:id="@+id/num8"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="8" />

<Button
android:id="@+id/num9"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="9" />

<Button
android:id="@+id/mul"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="X" />


<Button
android:id="@+id/num4"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="4" />

<Button
android:id="@+id/num5"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="5" />

<Button
android:id="@+id/num6"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="6" />

<Button
android:id="@+id/sub"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="-" />


<Button
android:id="@+id/num1"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="1" />

<Button
android:id="@+id/num2"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="2" />

<Button
android:id="@+id/num3"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="3" />

<Button
android:id="@+id/add"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="+" />


<Button
android:id="@+id/back"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="back" />

<Button
android:id="@+id/num0"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="0" />

<Button
android:id="@+id/dot"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="." />

<Button
android:id="@+id/equal"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="-5dp"
android:text="=" />

</GridLayout>
</LinearLayout>

+ Recent posts