Python
 Computer >> コンピューター >  >> プログラミング >> Python

組み込み関数を使わずにPythonで数式を評価するプログラムの実装方法


ここでは、加算(+)、減算(-)、乗算(*)、除算(/)を含む数式を表す文字列が与えられる問題を考えます。ただし「/」は整数除算(小数点以下を切り捨てる割り算)を表します。この数式を、eval() などの組み込み関数に頼らずに自力で評価し、結果を返すプログラムを実装していきます。

たとえば、入力が s = "2+3*5/7" の場合、出力は 4 になります。これは 2 + ((3 * 5) / 7)2 + (15 / 7)2 + 2 = 4 となるためです。

解決のアプローチ

ポイントは演算子の優先順位の扱いです。「*」と「/」は「+」や「-」よりも先に計算しなければなりません。そこで、文字列を一度反転して末尾(元の先頭)から読み進めながら、次の3段階で解析を行います。

  • get_value() … 値の読み取り:連続する数字を1つの整数にまとめる。直前に「-」があれば負の数として扱う。
  • get_term() … 項の計算:「*」「/」でつながった値をすべて処理し、優先順位の高い部分式を計算する。除算の結果は床関数(floor)で切り捨てる。
  • メイン処理 … 式全体の評価:「+」「-」で区切られた各項を順に足し引きし、最終的な答えを求める。

アルゴリズムの手順

  1. 与えられた文字列 s を反転する。
  2. get_value():符号変数 sign を 1 で初期化し、末尾が「-」なら取り除いて sign を -1 にする。その後、末尾が数字である限り value を 10 倍しながら桁を積み上げていき、sign × value を返す。
  3. get_term():まず get_value() で最初の値を取得し、末尾が「*」か「/」である限り演算子と次の値を取り出しては掛け算、または床関数による切り捨て除算を行い、term を返す。
  4. メイン処理:ans を get_term() で初期化し、文字列が空になるまで演算子と項を取り出しては「+」なら加算、「-」なら減算を行い、最後に ans を返す。

実装例(Pythonコード)

それでは、実際のコードを見てみましょう。

from math import floor


class Solution:
    def solve(self, s):
        s = list(s[::-1])

        def get_value():
            sign = 1
            if s and s[-1] == "-":
                s.pop()
                sign = -1
            value = 0
            while s and s[-1].isdigit():
                value *= 10
                value += int(s.pop())
            return sign * value

        def get_term():
            term = get_value()
            while s and s[-1] in "*/":
                op = s.pop()
                value = get_value()
                if op == "*":
                    term *= value
                else:
                    term = floor(1.0 * term / value)
            return term

        ans = get_term()
        while s:
            op, term = s.pop(), get_term()
            if op == "+":
                ans += term
            else:
                ans -= term
        return ans


ob = Solution()
s = "2+3*5/7"
print(ob.solve(s))

入力

"2+3*5/7"

出力

4

動作の流れを確認

入力 "2+3*5/7" を反転すると "7/5*3+2" となり、末尾から「2」→「+」→「3」→「*」→「5」→「/」→「7」の順に処理されます。まず get_term() が 2 を返し、ans = 2 となります。次に演算子「+」を読み込んだ後、get_term() が 3 × 5 ÷ 7 = floor(15 ÷ 7) = 2 を返すため、最終的に ans = 2 + 2 = 4 が出力されます。

計算量について

各文字はリストから一度だけ取り出されるため、時間計算量は O(n) です。また、反転した文字列をリストとして保持するため、空間計算量も O(n) となります(n は入力文字列の長さ)。eval() を使わないことで任意コード実行のリスクを避けつつ、安全に数式を評価できるのがこの手法の利点です。

  1. 組み込み関数を使わずにPythonで大文字・小文字の文字数をカウントする方法

    はじめに 本記事では、以下の問題に対する解決策について詳しく解説します。 問題文 文字列が与えられたとき、組み込みの文字判定関数を使用せずに、その文字列に含まれる大文字と小文字の数をそれぞれカウントする必要があります。 通常、この問題はPythonに標準で用意されている islower() や isupper() を使えば簡単に解決できます。しかし今回は「これらの文字判定関数を使わない」という制約があるため、文字のASCIIコード値を活用したアプローチを採用します。 ord() 関数を使うことで、文字列内の各文字のASCIIコード値を取得できます。取得した値が小文字の範囲(97〜122)

  2. 組み込み関数を使わずに大文字・小文字をカウントするPythonプログラム

    この記事では、文字列に含まれる大文字と小文字の数を数える問題を、組み込みの文字判定メソッドに頼らずに解くための考え方と実装方法を解説します。 問題の概要 文字列が入力として与えられたとき、その中に含まれる大文字の数と小文字の数をそれぞれ求める必要があります。 ここでは、isupper() や islower() のような文字判定メソッドは使わず、組み込み関数 ord() を利用して各文字のASCII値を調べることで判定を行います。 ASCII値による判定の考え方 ASCIIコードでは、英字は次のような範囲に割り当てられています。 小文字(a〜z):97〜122 大文字(A〜Z):65〜90