Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python으로 Excel 스프레드시트 수식 계산하기: 셀 참조와 공식을 처리하는 프로그램

2차원 행렬로 표현된 Excel 스프레드시트가 있다고 가정해 보겠습니다. 우리의 목표는 모든 셀의 값과 수식이 계산된 결과 행렬을 구하는 것입니다. Excel 스프레드시트는 일반적으로 아래와 같은 형태를 가집니다.

B170
35=A1+A2

문제 이해하기

스프레드시트의 열은 A, B, C...로 명명되고, 행은 1, 2, 3...으로 번호가 매겨집니다. 각 셀에는 다음 세 가지 중 하나가 들어갈 수 있습니다.

  • 값: 숫자 그 자체 (예: "7", "3")
  • 셀 참조: 다른 셀을 가리키는 주소 (예: "B1")
  • 수식: 숫자 또는 셀 참조 간의 연산식 (예: "=A1+5", "=A2+B2", "=2+5")

예를 들어 입력이 다음과 같다면,

B170
35=A1+A2

출력은 아래와 같습니다.

770
3510

그 이유는 첫 번째 행 두 번째 열의 B1이 7이고, "=A1+A2"는 7 + 3 = 10으로 계산되기 때문입니다.

해결 접근 방법

이 문제를 해결하기 위해 다음 단계를 따릅니다.

  • resolve() 함수 정의: 문자열 s를 받아 숫자라면 정수로 변환하여 반환하고, 그렇지 않으면 solve(getIdx(s))를 반환합니다.
  • getIdx() 함수 정의: 셀 주소 s를 받아 [행 인덱스, 열 인덱스] 리스트를 반환합니다. 첫 번째 값은 s의 두 번째 문자부터 끝까지를 정수로 변환한 값(1을 빼서 0 기반 인덱스로 조정)이고, 두 번째 값은 s[0]의 ASCII 코드에서 'A'의 ASCII 코드를 뺀 값입니다.
  • do() 함수 정의: a, b, 연산자 op를 받아 사칙연산을 수행합니다.
    • op가 "+"이면 a + b를 반환합니다.
    • op가 "-"이면 a - b를 반환합니다.
    • op가 "*"이면 a * b를 반환합니다.
    • op가 "/"이면 a / b를 반환합니다.
  • solve() 함수 정의: 좌표 i, j를 받아 해당 셀의 최종 값을 재귀적으로 계산합니다.
    • matrix[i][j]가 숫자라면 그 값을 그대로 반환합니다.
    • 숫자가 아니라면:
      • s := matrix[i][j]로 설정합니다.
      • s[0]이 "="이라면 수식이므로, s의 세 번째 문자부터 끝까지 탐색하면서 +, -, /, * 중 하나의 연산자를 찾아 op에 저장하고 반복을 종료합니다.
      • [a, b] := s의 두 번째 문자부터 끝까지를 op로 분리한 두 피연산자입니다.
      • [aRes, bRes] := [resolve(a), resolve(b)]로 각 피연산자를 실제 값으로 변환합니다.
      • do(aRes, bRes, op)를 반환합니다.
    • "="로 시작하지 않는다면 셀 참조이므로 solve(getIdx(s))를 반환합니다.
  • 행렬의 모든 행과 열을 순회하면서 matrix[i][j] := str(solve(i, j))로 갱신합니다.
  • 최종 행렬을 반환합니다.

구현 예제

아래 구현을 통해 더 잘 이해할 수 있습니다.

class Solution:
   def solve(self, matrix):
      def resolve(s):
         try:
            return int(s)
         except:
            return solve(*getIdx(s))

      def getIdx(s):
         return [int(s[1:]) - 1, ord(s[0]) - ord("A")]

      def do(a, b, op):
         if op == "+":
            return a + b
         if op == "-":
            return a - b
         if op == "*":
            return a * b
         if op == "/":
            return a / b

      def solve(i, j):
         try:
            return int(matrix[i][j])
         except:
            s = matrix[i][j]
            if s[0] == "=":
               for c in s[2:]:
                  if c in "+-/*":
                     op = c
                     break
               a, b = s[1:].split(op)
               aRes, bRes = resolve(a), resolve(b)
               return do(aRes, bRes, op)
            else:
               return solve(*getIdx(s))

      for i in range(len(matrix)):
         for j in range(len(matrix[0])):
            matrix[i][j] = str(solve(i, j))

      return matrix

ob = Solution()
matrix = [
   ["B1", "7", "0"],
   ["3", "5", "=A1+A2"]
]
print(ob.solve(matrix))

입력

[["B1", "7", "0"],
["3", "5", "=A1+A2"] ]

출력

[['7', '7', '0'],
['3', '5', '10']]

코드 설명

이 구현의 핵심은 재귀적 해석(recursive resolution)입니다. resolve() 함수는 피연산자가 단순 숫자인지 확인하고, 숫자가 아니라면 getIdx()를 통해 셀 좌표로 변환한 뒤 solve()를 다시 호출합니다. 이 과정 덕분에 "=A1+A2"처럼 셀 참조를 포함한 수식도 참조된 셀이 또 다른 참조나 수식을 담고 있더라도 연쇄적으로 올바르게 계산됩니다.

또한 try-except 블록을 활용해 값이 정수로 변환 가능한지 검사함으로써, 별도의 타입 체크 로직 없이 숫자 여부를 간결하게 판별할 수 있다는 점도 눈여겨볼 만합니다. 마지막으로 전체 행렬을 순회하며 각 셀을 계산된 값의 문자열로 교체하면, 모든 수식이 반영된 완성된 스프레드시트를 얻게 됩니다.