1
+ import itertools
2
+ import re
3
+ from typing import Dict
4
+
5
+
1
6
class CalcParseError (Exception ):
2
7
pass
3
8
@@ -43,7 +48,7 @@ class CalcNode:
43
48
44
49
def __init__ (self , formula = None ):
45
50
if formula :
46
- root = parse_to_calc_node (formula )
51
+ root = _parse (formula )
47
52
self .content = root .content
48
53
self .lch = root .lch
49
54
self .rch = root .rch
@@ -70,29 +75,52 @@ def __ne__(self, other):
70
75
return not self .__eq__ (other )
71
76
72
77
def __str__ (self , depth = 0 ):
73
- if self .operator is not None :
74
- lv = self .lch .__str__ (depth = depth + 1 )
75
- rv = self .rch .__str__ (depth = depth + 1 )
76
- res = "%s%s%s" % (lv , _operator_to_string (self .operator ), rv )
77
- if depth > 0 and (self .operator == add or self .operator == sub ):
78
- res = "(%s)" % res
79
- return res
80
- elif isinstance (self .content , int ):
81
- return str (self .content )
82
- else :
83
- return self .content
78
+ opens = [] # Position list of open brackets
79
+ cands = []
80
+ original_formula = self .to_string_strictly ()
81
+ for i , c in enumerate (original_formula ):
82
+ if c == '(' :
83
+ opens .append (i )
84
+ elif c == ')' :
85
+ assert len (opens ) > 0
86
+ cands .append ((opens [- 1 ], i ))
87
+ opens .pop ()
88
+ pass
89
+
90
+ values_for_identity_check = [3 , 14 , 15 , 92 ]
91
+
92
+ def likely_identical (formula : str ):
93
+ node = CalcNode (formula )
94
+ vars = node .get_all_variables ()
95
+ for combination in itertools .product (values_for_identity_check , repeat = len (vars )):
96
+ val_dict = dict (zip (vars , list (combination )))
97
+ if self .evaluate (val_dict ) != node .evaluate (val_dict ):
98
+ return False
99
+ return True
100
+
101
+ # Remove parentheses greedy
102
+ res_formula = list (original_formula )
103
+ for op , cl in cands :
104
+ tmp = res_formula .copy ()
105
+ tmp [op ] = ''
106
+ tmp [cl ] = ''
107
+ if likely_identical ("" .join (tmp )):
108
+ res_formula = tmp
109
+ simplified_form = "" .join (res_formula )
110
+
111
+ return simplified_form
84
112
85
113
def get_all_variables (self ):
86
- if self .operator is not None :
114
+ if self .is_operator_node () :
87
115
lv = self .lch .get_all_variables ()
88
116
rv = self .rch .get_all_variables ()
89
117
return lv + rv
90
- elif isinstance ( self .content , int ):
118
+ elif self .is_constant_node ( ):
91
119
return []
92
120
else :
93
121
return [self .content ]
94
122
95
- def evaluate (self , variables = None ):
123
+ def evaluate (self , variables : Dict [ str , int ] = None ):
96
124
if variables is None :
97
125
variables = {}
98
126
if self .is_operator_node ():
@@ -103,10 +131,42 @@ def evaluate(self, variables=None):
103
131
return int (self .content )
104
132
else :
105
133
if self .content not in variables :
106
- raise EvaluateError
134
+ raise EvaluateError (
135
+ "Found an unknown variable '{}'" .format (self .content ))
107
136
else :
108
137
return variables [self .content ]
109
138
139
+ def simplify (self ):
140
+ current_formula = str (self )
141
+
142
+ # Really stupid heuristics but covers the major case.
143
+ while True :
144
+ next_formula = re .sub (r"-1\+1$" , "" , current_formula )
145
+ next_formula = re .sub (r"\+0$" , "" , next_formula )
146
+ next_formula = re .sub (r"-0$" , "" , next_formula )
147
+ if next_formula == current_formula :
148
+ break
149
+ current_formula = next_formula
150
+
151
+ return CalcNode (current_formula )
152
+
153
+ def to_string_strictly (self ):
154
+ if self .is_operator_node ():
155
+ return "({lch}{op}{rch})" .format (
156
+ lch = self .lch .to_string_strictly (),
157
+ op = _operator_to_string (self .operator ),
158
+ rch = self .rch .to_string_strictly ()
159
+ )
160
+ else :
161
+ return str (self .content )
162
+
163
+
164
+ def _parse (formula : str ):
165
+ res , pos = _expr (formula + "$" , 0 ) # $ is put as a terminal character
166
+ if pos != len (formula ):
167
+ raise CalcParseError
168
+ return res
169
+
110
170
111
171
def _expr (formula , pos ):
112
172
res , pos = _term (formula , pos )
@@ -179,19 +239,5 @@ def _factor(formula, pos):
179
239
raise CalcParseError
180
240
181
241
182
- def parse_to_calc_node (formula ):
183
- """
184
- 入力
185
- formula # str : 式
186
- 出力
187
- #CalcNode : 構文木の根ノード
188
-
189
- """
190
- res , pos = _expr (formula + "$" , 0 ) # $は使わないことにする
191
- if pos != len (formula ):
192
- raise CalcParseError
193
- return res
194
-
195
-
196
- if __name__ == '__main__' :
197
- print (CalcNode ("N-1-1+1000*N*N" ).evaluate ({"N" : 10 }))
242
+ def parse_to_calc_node (formula : str ) -> CalcNode :
243
+ return CalcNode (formula )
0 commit comments