이 페이지는 코딩도장 데이터의 읽기 전용 정적 보관본입니다.

수식 계산기

간단한 수식을 입력 받아 계산하는 프로그램을 작성한다.

(시작한지 얼마안 된 학생들 프로젝트로 뭘 시켜볼까 하다가 한 번 만들어 봤습니다.)

연관 문제: 이상한 계산기 , 순서대로 계산기

수식 계산기1


수식 계산기는 다음을 반복한다(편의상 1 라운드라고 부르자).

  1. 현재 저장된 수식을 출력한다.
  2. ">>> " 프롬프트로 입력을 받는다.
  3. 입력받은 값을 처리한다.

입력값의 종류

입력은 숫자, 연산자 또는 명령어이다.

숫자

숫자는 실수 범위에서 입력 받는다.

정수와 실수를 굳이 구분하지 않고 소숫점 이하 2~3자리까지 출력한다.

예를 들어, 정수 7을 7.0이나 7.00으로 출력해도 무방하다. 소숫점 이하의 오차는 크게 신경쓰지 않고 코딩한다.

연산자

덧셈, 뺄셈, 곱셈, 나눗셈, 나머지의 5가지 연산을 할 수 있다.

해당 연산자는 각각 "+", "-", "*", "/", "%" 이며 모두 2항 연산자이다.

우선순위는 곱셈 = 나눗셈 = 나머지 > 덧셈 = 뺄셈 이다.

명령어

"C" (Clear): 저장된 수식을 0 으로 초기화한다.

"U" (Undo): 수식을 1 라운드 전으로 되돌린다(정확한(?) 명칭은 CE, Clear Error 입니다).

수식이 숫자 하나로 이루어져 있을 때 Undo를 하면 0 이 된다.

수식이 0일 때는 Undo를 해도 이전으로 돌아갈 수 없다.

"X" (eXit): 프로그램을 종료한다.

"=" (evaluate): 수식의 값을 계산한다(연산자로 처리해도 무방함).

evaluate 명령을 실행하면 다음 라운드에는 계산 결과로서 숫자 하나로 이루어진 수식을 출력한다.

eval() 종류의 함수는 쓰지 말 것

그 외의 입력

유효하지 않은 입력은 없다고 가정하되, 가능하면 무시한다(다음 라운드에도 똑같은 상태를 유지):

소문자 명령어, 숫자와 문자가 뒤섞임, 앞뒤의 공백 등등.

입력은 연산자/숫자/명령어 하나이다. 예를 들어, "* 3" 과 같은 입력은 처리하지 않는다.

수식의 처리

초기값은 0이다.

수식은 입력값들을 공백 하나를 사이에 두고 이어 붙이다가, Clear, Undo, evaluate 명령을 실행했을 때 줄어든다.

기본적으로 숫자 뒤에는 연산자, 연산자 뒤에는 숫자가 와야 한다. 그렇지 않으면 무시한다.

예외1) 수식의 마지막이 연산자일 때 연산자를 입력하면 마지막 연산자가 입력한 연산자로 바뀐다.

예외2) 수식의 값이 0 일 때 숫자를 입력하면 수식은 입력한 숫자로 바뀐다.

실행 예

0
>>> 3
3
>>> +
3 +
>>> -
3 -
>>> 4
3 - 4
>>> *
3 - 4 *
>>> 2
3 - 4 * 2
>>> U
3 - 4 *
>>> 5
3 - 4 * 5
>>> =
-17.0
>>> U
0
>>> 6
6
>>> C
0
>>> X

수식 계산기2


괄호 처리

수식이 괄호 "(", ")" 를 포함해서 처리되도록 한다.

코딩할 떄처럼 소괄호만 사용한다.

변수(메모리) 추가

최초 실행 시, "tmp" 변수에 값 0을 저장해둔다.

매 라운드에, 수식과 함께 현재 메모리 상태를 출력한다.

메모리는 Undo, Clear 명령의 영향을 받지 않는다.

다음 명령어들을 추가한다.

"S변수명" (Save): 현재 수식의 값을 "변수명" 변수에 저장한다.

변수명이 주어지지 않으면 "tmp" 변수에 저장한다.

변수명은 공백 없는 알파벳 소문자로만 이루어진다.

숫자 하나로 이루어진 수식만 저장할 수 있다. 수식이 evaluate 되지 않았으면 무시한다.

"L변수명" (Load): "변수명" 변수에 저장된 값을 불러온다.

불러온 값은 ">>> " 프롬프트로 입력한 값과 똑같이 처리한다.

변수명이 주어지지 않으면 "tmp" 변수값을 불러온다.

없는 변수면 무시한다.

"R" (Reset): 메모리를 초기화한다.

실행 예

tmp = 0
0
>>> 12
tmp = 0
12
>>> S
tmp = 12
12
>>> C
tmp = 12
0
>>> L
tmp = 12
12
>>> +
tmp = 12
12 +
>>> 3
tmp = 12
12 + 3
>>> -
tmp = 12
12 + 3 -
>>> L
tmp = 12
12 + 3 - 12
>>> S
tmp = 12
12 + 3 - 12
>>> =
tmp = 12
3.0
>>> Sx
tmp = 12
x = 3.0
3.0
>>> X

2018/08/28 20:13

Noname

5개의 풀이가 있습니다.

내가 만들고 내가 풀기!

괄호 빼고 했습니다.


# ===================== Main Functions =============================
def view_stat(exp, vars):
    header = '''
=============================== Calculator ==============================
================================ Commands ===============================
|  C(Clear) |  X(eXit)  |  U(Undo)  |  S(Save)  |  L(Load)  |  R(Reset) |
================================ Operators ==============================
|     *     |     /     |     %     |     +     |     -     |     =     |
=============================== Variables ==============================='''
    eline = '============================== Expression ==============================='
    vline = len(header.split('\n')[1]) * '='

    print(header)

    for var_name in vars:
        print(var_name + ' = ' + vars[var_name])

    print(eline)
    print(' '.join(exp))
    print(vline)


def do_cmd(cmd, exp, vars):
    if cmd == 'C':
        reset_exp()
    elif cmd == 'U':
        exp.pop()
        if not exp:
            reset_exp()
    elif cmd == 'R':
        reset_vars()
    elif cmd[0] == 'S':
        var_name = inp[1:] if inp[1:] else 'tmp'
        if len(exp) == 1 and ' ' not in var_name and var_name.islower():
            vars[var_name] = exp[0]
    elif cmd[0] == 'L':
            var_name = inp[1:] if inp[1:] else 'tmp'
            if var_name in vars:
                proc_num(vars[var_name], exp)
    elif cmd == 'X':
        exit()


def eval_exp(exp):
    exp1 = exp[:]
    if is_op(exp1[-1]):
        exp1.pop()

    # exp2 <- exp1 에서 *, /, % 부터 계산
    exp2 = []
    while exp1:
        t = exp1.pop(0)
        if t in ['*', '/', '%']:
            opr1, op, opr2 = float(exp2[-1]), t, float(exp1.pop(0))
            if op == '*':   exp2[-1] = opr1 * opr2
            elif op == '/': exp2[-1] = opr1 / opr2
            elif op == '%': exp2[-1] = opr1 % opr2
        else:
            exp2.append(t)

    # opr1 <- exp2 에서 +, - 계산
    opr1 = float(exp2.pop(0))
    while exp2:
        op, opr2 = exp2.pop(0), float(exp2.pop(0))
        if op == '*':   opr1 *= opr2
        elif op == '/': opr1 /= opr2
        elif op == '%': opr1 %= opr2
        elif op == '+': opr1 += opr2
        elif op == '-': opr1 -= opr2

    reset_exp(num_str(opr1))


def proc_op(inp_op, exp):
    if inp_op == '=':
        eval_exp(exp)
    elif is_op(exp[-1]):
        exp[-1] = inp_op
    else:
        exp.append(inp_op)


def proc_num(inp_num, exp):
    if exp == ['0']:
        reset_exp(inp_num)
    elif is_op(exp[-1]):
        exp.append(inp_num)


# ========================== Sub Functions ============================
def reset_exp(val = '0'):
    global exp
    exp = [val]


def reset_vars():
    global vars
    vars = {'tmp':'0'}


def num_str(num):
    return str(round(num, 2))


def is_op(inp):
    return inp in list('*/%+-=')


def is_cmd(inp):
    return inp[0] in list('CXURSL')


exp, vars = [], dict()
reset_exp()
reset_vars()
while True:
    view_stat(exp, vars)
    inp = input('>>> ')

    if is_cmd(inp):
        do_cmd(inp, exp, vars)
    elif is_op(inp):
        proc_op(inp, exp)
    else:
        try:
            float(inp)
            proc_num(inp, exp)
        except ValueError:
            pass

객체지향... 이라기엔 좀 대충 만든 버전

class Environment():
    def __init__(self):
        self.vars = VariableDict()
        self.exp = Expression()

    def clear(self):
        self.exp.clear()
        self.vars.clear()


#class Expression(list):
class Expression():
    def __init__(self):
        self.clear()

    def clear(self, token = '0'):
        self.token_lst = [token]

    def __str__(self):
        return ' '.join(self.token_lst)

    def is_value(self, value = None):
        if value:
            return self.token_lst == [value]
        else:
            return len(self.token_lst) == 1

    def value(self):
        return self.token_lst[0] if self.is_value() else None

    def pop(self):
        self.token_lst.pop()
        if not self.token_lst:
            self.token_lst = ['0']

    def append(self, token):
        if Operator.is_op(token):
            if token == '=':
                self.eval() #eval_exp(exp)
            elif Operator.is_op(self.token_lst[-1]):
                self.token_lst[-1] = token
            else:
                self.token_lst.append(token)
        elif Number.is_num(token):
            if self.is_value('0'):
                self.clear(token)
            elif Operator.is_op(self.token_lst[-1]):
                self.token_lst.append(token)

    def eval(self):
        exp1 = self.token_lst[:]
        if Operator.is_op(exp1[-1]):
            exp1.pop()

        # exp2 <- exp1 에서 *, /, % 부터 계산
        exp2 = []
        while exp1:
            token = exp1.pop(0)
            if token in ['*', '/', '%']:
                exp2[-1] = Operator.calc(exp2[-1], token, exp1.pop(0))
            else:
                exp2.append(token)

        # opr1 <- exp2 에서 +, - 계산
        opr1 = exp2.pop(0)
        while exp2:
            opr1 = Operator.calc(opr1, exp2.pop(0), exp2.pop(0))

        self.clear(opr1)


#class VariableDict(dict):
class VariableDict():
    def __init__(self):
        self.var_dict = {'tmp':'0'}

    def clear(self):
        self.var_dict = {'tmp': '0'}

    def __contains__(self, var_name):
        return var_name in self.var_dict

    def __setitem__(self, var_name, var_value):
        if ' ' not in var_name and var_name.islower():
            self.var_dict[var_name] = var_value

    def __getitem__(self, var_name):
        return self.var_dict[var_name]

    def __str__(self):
        string = ''
        for var_name, var_value in self.var_dict.items():
            string += var_name + ' = ' + var_value + '\n'

        return string[:-1]


class Command():
    cmd_lst = list('CXURSL')

    @classmethod
    def is_cmd(cls, inp):
        return inp[0] in cls.cmd_lst

    @classmethod
    def do_cmd(cls, cmd, env):
        if cmd == 'C':
            env.exp.clear()
        elif cmd == 'U':
            env.exp.pop()
        elif cmd == 'R':
            env.vars.clear()
        elif cmd[0] == 'S':
            var_name = inp[1:] if inp[1:] else 'tmp'
            if env.exp.is_value():
                env.vars[var_name] = env.exp.value()
        elif cmd[0] == 'L':
            var_name = inp[1:] if inp[1:] else 'tmp'
            if var_name in env.vars:
                env.exp.append(env.vars[var_name])
        elif cmd == 'X':
            exit()


class Number:
    @classmethod
    def value(cls, num):
        return round(float(num), 2)

    @classmethod
    def str(cls, num):
        return str(round(num, 2))

    @classmethod
    def is_num(cls, string):
        try:
            float(string)
            return True
        except ValueError:
            return False


class Operator:
    @classmethod
    def is_op(cls, string):
        return string in list('*/%+-=')

    @classmethod
    def calc(cls, opr1, op, opr2):
        opr1, opr2 = Number.value(opr1), Number.value(opr2)
        result = 0
        if op == '*':
            result = opr1 * opr2
        elif op == '/':
            result = opr1 / opr2
        elif op == '%':
            result = opr1 % opr2
        elif op == '+':
            result = opr1 + opr2
        elif op == '-':
            result = opr1 - opr2

        return Number.str(result)


class Viewer:
    @classmethod
    def view_stat(cls, env):
        header = '''
=============================== Calculator ==============================
================================ Commands ===============================
|  C(Clear) |  X(eXit)  |  U(Undo)  |  S(Save)  |  L(Load)  |  R(Reset) |
================================ Operators ==============================
|     *     |     /     |     %     |     +     |     -     |     =     |
=============================== Variables ==============================='''
        eline = '============================== Expression ==============================='
        vline = len(header.split('\n')[1]) * '='

        print(header)
        print(env.vars)
        print(eline)
        print(env.exp)
        print(vline)


env = Environment()
while True:
    Viewer.view_stat(env)
    inp = input('>>> ')
    if Command.is_cmd(inp):
        Command.do_cmd(inp, env)
    else:
        env.exp.append(inp)

2018/08/28 20:51

Noname

이쁘다 ㄷ - leak, 2018/08/30 13:33
감사합니다 - Noname, 2018/08/30 15:15
와우...........감탄하고갑니다. - Hyuk, 2019/04/16 17:39

수식계산기 1

class calc:
  def __init__(self):
    self.eq = [0]
    self._func = {None: self._print_err, 'C': self.clear, 'U': self.undo, 'X': quit, '=': self.eval}
    self._opr = ['*', '/', '%', '+', '-']

  def input(self):
    self._val = self._conv_value(input('>>> '))
    self._func.get(self._val, self._eq_append)()

  def _eq_append(self):
    if self._val in self._opr:
      if self.eq[-1] in self._opr: self.eq[-1] = self._val
      else                       : self.eq.append(self._val)
    else:
      if self.eq[-1] in self._opr: self.eq.append(self._val)
      elif self.eq == [0]        : self.eq[0] = self._val
      else                       : self._print_err()

  @staticmethod
  def _print_err(): print('잘못된 입력입니다.\n')

  def _conv_value(self, v):
    try: return int(v)
    except:
      try   : return float(v)
      except: return v if v in (*self._opr, *self._func) else None

  def __str__(self):
    return ' '.join(map(lambda x: f'{x:.2f}' if isinstance(x, float) else str(x), self.eq))

  def clear(self):
    self.eq.clear()
    self.eq.append(0)

  def undo(self):
    self.eq.pop()
    if not self.eq: self.eq.append(0)

  def eval(self):
    idx = 0
    while idx < len(self.eq):
      if self.eq[idx] in self._opr[:3]: self.eq[idx-1: idx+2] = [self.solver(*self.eq[idx-1:idx+2])]
      else                            : idx += 1
    while len(self.eq) > 1:
      self.eq[:3] = [self.solver(*self.eq[:3])]

  @staticmethod
  def solver(v1, opr, v2):
    return v1+v2 if opr=='+' else v1-v2 if opr=='-' else v1*v2 if opr=='*' else v1/v2 if opr=='/' else v1%v2 if opr=='%' else None



if __name__ == '__main__':
  ex_calc = calc()
  while True:
    print(ex_calc)
    ex_calc.input()

수식계산기 2

class calc2(calc):
  def __init__(self):
    super().__init__()
    self._func['S'] = self.save
    self._func['L'] = self.load
    self._func['R'] = self.reset
    self.memo = {'tmp': 0}

  def _eq_append(self):
    if self._val == '(' and self._bracket_count() >= 0:
      if isinstance(self.eq[-1], str): self.eq.append(self._val); return
      elif self.eq == [0]            : self.eq[0] = self._val; return
    elif self._val == ')' and self._bracket_count() > 0:
      if self.eq[-1].__class__.__name__ in 'intfloat': self.eq.append(self._val); return
    if self._val.__class__.__name__ in 'intfloat' and self.eq[-1] == '(': self.eq.append(self._val); return
    super()._eq_append()

  def _bracket_count(self):
    return sum(1 if i=='(' else -1 if i==')' else 0 for i in self.eq)

  def _conv_value(self, v):
    if v[:1] in ('S', 'L', 'R'):
      self.var = self._isvar(v[1:])
      return v[:1]
    if v in ['(', ')']: return v
    return super()._conv_value(v)

  @staticmethod
  def _isvar(v):
    return 'tmp' if v == '' else v if all(i.islower() for i in v) else None

  def save(self):
    if self.var is None   : print('잘못된 변수명 입니다.\n')
    elif len(self.eq) == 1: self.memo[self.var] = self.eq[0]
    else                  : print('계산한 후 저장하세요.\n')

  def load(self):
    if self.var not in self.memo:
      print('존재하지 않는 변수입니다.\n')
    else:
      self._val = self.memo[self.var]
      self._eq_append()

  def reset(self):
    self.memo.clear()
    self.memo['tmp'] = 0

  def eval(self):
    if self._bracket_count() != 0: return print('괄호 쌍이 맞지 않습니다.\n')
    self._bracket_eval()
    super().eval()

  def _bracket_eval(self):
    while '(' in self.eq:
      end = self.eq.index(')')
      start = end - (self.eq[: end][::-1].index('(') + 1)
      self.eq[start: end+1] = self._bracket_solver(self.eq[start+1: end])

  def _bracket_solver(self, v):
    if v == []: return [0]
    idx = 0
    while idx < len(v):
      if v[idx] in self._opr[:3]: v[idx-1: idx+2] = [self.solver(*v[idx-1:idx+2])]
      else                      : idx += 1
    while len(v) > 1:
      v[:3] = [self.solver(*v[:3])]
    return v

  def print_var(self):
    print(str(self.memo)[1:-1].replace(', ', '\n').replace(':', ' =').replace("'", ''))



if __name__ == '__main__':
  ex_calc = calc2()
  while True:
    ex_calc.print_var()
    print(ex_calc)
    ex_calc.input()

상속으로 풀어봤습니다

2018/08/31 20:01

Creator

eq,lq,sg = ' ',' ',['+','-','*','/','%']


def cal(eq):

    eq,i = eq.split(),0

    while len(eq) != 1:
        if eq[i] in sg:
            if eq[i] not in sg[:2] or (eq.count('*')+eq.count('/')+eq.count('%')) == 0:

                s,a,b = eq[i],float(eq[i-1]),float(eq[i+1])

                del eq[i-1:i+2]

                if s in sg[2:]:
                    eq.insert(i-1,str(((a*b,a/b)[s == '/'],a%b)[s == '%']))

                else:
                    eq.insert(i-1,str((a+b,a-b)[s == '-']))

                i = 0
                continue
        i += 1

    return str(eq[0])



while True:
    inp = input('>>> ')
    if inp in ['C','U']:
        eq = (' ' if inp == 'C' else lq)
    elif inp == 'X':
        break
    elif inp == '=':
        eq = cal(eq)
    elif inp.isdigit() or inp in sg:
        lq = eq
        if eq[-1] in sg and inp in sg:
            eq = eq[:-1] + inp
        else:
            eq += (' ' if inp in sg  or eq[-1] in sg else '') +inp
    print(eq)

계산 부분이 많이 난잡하네요;;.........

2019/01/24 15:06

김영성

수식 계산기 1

import sys

class Expression:
    def __init__(self):
        self.L = [0]

    def _eval(self, s, a, b):
        if s == '+': ans = a + b
        elif s == '-': ans = a - b
        elif s == '*': ans = a * b
        elif s == '/': ans = a / b
        elif s == '%': ans = a % b
        return ans

    def _eval_opr(self, s):
        while s in self.L:
            idx = self.L.index(s)
            ans = self._eval(s, self.L[idx-1], self.L[idx+1])
            self.L = self.L[:idx-1] + [ans] + self.L[idx+2:]

    def evaluate(self):
        self._eval_opr('*')
        self._eval_opr('/')
        self._eval_opr('%')
        self._eval_opr('+')
        self._eval_opr('-')

    def put(self, s):
        if s == 'C': self.L = [0]
        elif s == 'U': self.L.pop(-1)
        elif s == 'X': sys.exit()
        elif s == '=': self.evaluate()
        else:
            try:
                if self.L == [0]: self.L = [float(s)]
                else: self.L.append(float(s))
            except:
                self.L.append(s)
        self.display()

    def display(self):
        print(' '.join(map(str, self.L)))


A = Expression()
A.display()
while True:
    A.put(input('>>> '))

2019/05/15 15:30

messi

using System;
using System.Collections.Generic;

namespace solution
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("\n \t수식 계산기1");
            string op = "";

            List<string> command = new List<string>{ "C", "U", "X", "=" };
            List<string> oplist = new List<string> { "+", "-", "*", "%", "/" };
            List<string> list = new List<string>();
            while(op != "X")
            {
                int cnt = list.Count;
                if (cnt % 2 == 0)
                {
                    printlist(list);
                    Console.Write(">>> ");
                    string input = Console.ReadLine();
                    if (char.IsDigit(input[0]) && char.IsDigit(input[input.Length-1]))
                        list.Add(input);
                }
                else if(cnt % 2 == 1)
                {
                    //Console.WriteLine("\n\n명령어: C(Clear), U(Undo), X(eXit), =(evaluate)");
                    //Console.WriteLine("+, -, *, /, %  중 하나를 입력하세요, ");
                    printlist(list);
                    Console.Write(">>> ");
                    string com = Console.ReadLine();
                    if (command.Contains(com))
                    {
                        if (com == "X")
                            op = "X";
                        else if (com == "C")
                        {
                            list.Clear();
                        }
                        else if (com == "U")
                            list.RemoveAt(list.Count - 1);
                        else if (com == "=")
                        {
                            calclist(list);
                        }
                    }
                    else if(oplist.Contains(com))
                        list.Add(com);
                }                
            }
            Console.WriteLine("수고!!");
        }

        private static void calclist(List<string> list)
        {
            Queue<int> que = new Queue<int>();
            Queue<string> opQ = new Queue<string>();
            string op = "";
            int fSu = 0;
            for (int i = 0; i < list.Count; i++)
            {
                if (char.IsDigit(list[i][0]))
                {
                    if (op == "*")
                        fSu *= int.Parse(list[i]);
                    else if (op == "/")
                        fSu /= int.Parse(list[i]);
                    else if (op == "%")
                        fSu = fSu % int.Parse(list[i]);
                    else
                        fSu = int.Parse(list[i]);
                    op = "";
                }
                else
                {
                    if (list[i] == "+" || list[i] == "-")
                    {
                        que.Enqueue(fSu);
                        opQ.Enqueue(list[i]);
                        op = "";
                    }
                    else
                        op = list[i];
                }
            }
            que.Enqueue(fSu);
            fSu = que.Dequeue();
            while (opQ.Count > 0)
            {
                op = opQ.Dequeue();
                if (op == "+")
                    fSu += que.Dequeue();
                else if(op == "-")
                    fSu -= que.Dequeue();
            }
            list.Clear();
            list.Add(fSu.ToString());
        }

        private static void printlist(List<string> list)
        {
            foreach (var item in list)
                Console.Write("{0} ", item);
            Console.WriteLine();
        }
    }
}

2023/08/30 16:35

insperChoi

목록으로