Форум сайта python.su
1
Осваиваю программирование. Переписываю код Страуструпа и никак не могу понять в чем ошибка. На вход подаю 3 - 2 - 1 ( = (3 - 2) - 1 = 1 - 1 = 0), а получаю 1, т.е. вычисляется только (3 - 2), а -1 опускается.
class Buffer(object): def __init__(self, expr): self.expr = expr self.buffer = [] def get(self): if len(self.expr) != 0: self.buffer = self.expr.pop(0) return self.buffer def putback(self, val): self.expr.insert(0, val) def expression(buf): left = term(buf) t = buf.get() if t == '+': left += term(buf) elif t == '-': left -= term(buf) else: buf.putback(t) return left def term(buf): left = primary(buf) t = buf.get() if t == '*': left *= primary(buf) elif t == '/': left /= float(primary(buf)) else: buf.putback(t) return left def primary(buf): t = buf.get() if t.isdigit(): return int(t) elif t == '(': d = expression(buf) t = buf.get() if t != ')': print("')' expected") else: buf.putback(t) buf = Buffer(['3','-','2','-','1']) print expression(buf)
Офлайн
857
Expression:
Term
Expression + Term
Expression - Term
Term:
Primary
Term * Primary
Term / Primary
Term % Primary
Primary:
Number
( Expression )
- Primary
+ Primary
Number:
floating-point-literal
Отредактировано py.user.next (Сен. 26, 2012 05:54:36)
Офлайн
857
#!/usr/bin/env python # Expression: # Term # Expression + Term # Expression - Term # Term: # Primary # Term * Primary # Term / Primary # Term % Primary # Primary: # Number # ( Expression ) # - Primary # + Primary # Number: # floating-point-literal class Buffer(object): def __init__(self, expr): self.expr = expr self.buffer = [] def get(self): if len(self.expr) != 0: self.buffer = self.expr.pop(0) return self.buffer def putback(self, val): self.expr.insert(0, val) def expression(buf): left = term(buf) t = buf.get() while True: if t == '+': left += term(buf) t = buf.get() elif t == '-': left -= term(buf) t = buf.get() else: buf.putback(t) return left def term(buf): left = primary(buf) t = buf.get() while True: if t == '*': left *= primary(buf) t = buf.get() elif t == '/': d = float(primary(buf)) if (d == 0.0): raise ValueError('divide by zero') left /= d t = buf.get() else: buf.putback(t) return left def primary(buf): t = buf.get() if t.isdigit(): return int(t) elif t == '(': d = expression(buf) t = buf.get() if t != ')': raise ValueError("')' expected") return d else: raise ValueError('primary expected') buf = Buffer(['3','*', '(', '1','+','3', '/', '2', ')']) print expression(buf)
[guest@localhost rasp]$ ./s2.py
7.5
[guest@localhost rasp]$
Отредактировано py.user.next (Сен. 26, 2012 08:38:53)
Офлайн
1
py.user.next, спасибо большущее! Жаль, конечно, что сам до этого не додумался. Я не до конца понимал, как происходит обработка лексем в цикле, поэтому из цикла возвращал совсем другое.
Офлайн
1
Немного переписал, работает как надо теперь.
#! /usr/bin/env python # Expression: # Term # Expression + Term # Expression - Term # Term: # Primary # Term * Primary # Term / Primary # Term % Primary # Primary: # Number # ( Expression ) # - Primary # + Primary # Number: # floating-point-literal class Buffer(object): def __init__(self, expr): self.expr = expr self.buffer = [] self.isset = False def get(self): if self.isset: self.isset = False else: if len(self.expr) != 0: self.buffer = self.expr.pop(0) return self.buffer def putback(self, val): self.isset = True self.buffer = val def expression(buf): left = term(buf) t = buf.get() while True: if t == '+': left += term(buf) t = buf.get() elif t == '-': left -= term(buf) t = buf.get() else: buf.putback(t) return left def term(buf): left = primary(buf) t = buf.get() while True: if t == '*': left *= primary(buf) t = buf.get() elif t == '/': d = primary(buf) if d == 0.: raise ValueError('divide by zero') left /= d t = buf.get() elif t == '%': d = primary(buf) if d == 0.: raise ValueError('divide by zero') left %= d t = buf.get() else: buf.putback(t) return left def primary(buf): t = buf.get() if isInt(t): return int(t) elif isFloat(t): return float(t) elif t == '-': return - primary(buf) elif t == '+': return primary(buf) elif t == '(': d = expression(buf) t = buf.get() if t != ')': raise ValueError("')' expected") return d else: raise ValueError('primary expected') def isdigit(ch): for i in ch: if not i.isdigit() and i != '.': return False return True def isFloat(string): try: float(string) return True except ValueError: return False def isInt(string): try: int(string) return True except ValueError: return False def parser(expr): expr = ''.join(expr.split()) output = [] num = '' for ch in expr: if ch.isdigit() or ch == '.': num += ch elif ch in ['(', ')', '+', '-', '*', '/', '%']: if num: output.append(num) num = '' output.append(ch) else: raise ValueError("Bad character '" + ch + "'") if num: output.append(num) return output def calc(expr): return expression(Buffer(parser(expr))) if __name__ == "__main__": exprs = ['(3+2+1+9/2-98*5+7%2)-2+4/2+(((321)-43)-1)/2.', '-2-1', '(+2-1)/100.0', '100.3-2/4.5'] for e in exprs: print "eval()\t" + str(eval(e)) + "\ncalc():\t" + str(calc(e)) + "\n"
Отредактировано baslie (Сен. 26, 2012 18:20:36)
Офлайн
857
baslieelif ch in ['(', ')', '+', '-', '*', '/', '%']:
elif ch in '()+-*/%':
baslieчто это за функция ?def isdigit(ch): for i in ch: if not i.isdigit() and i != '.': return False return True
baslieprint "eval()\t" + str(eval(e)) + "\ncalc():\t" + str(calc(e)) + "\n"
>>> print "eval():\t{0}\ncalc():\t{1}\n".format(eval(e), calc(e)) eval(): 3 calc(): 3 >>>
baslieэтот список - промежуточное звено, его удобно заполнять из любого источника
Но все-таки как лучше считывать лексемы? Есть другие варианты, кроме list?
class Buffer(object): def __init__(self, expr): self.expr = expr def get(self): if self.expr: return self.expr.pop(0) def putback(self, val): self.expr.insert(0, val)
class Buffer(list): def get(self): if self: return self.pop(0) def putback(self, val): self.insert(0, val) class Buffer(list): def __init__(self, expr): list.__init__(self, expr) def get(self): if self: return self.pop(0) def putback(self, val): self.insert(0, val)
>>> class Buffer(list): ... def __init__(self, expr): ... list.__init__(self, expr * 2) ... def get(self): ... if self: ... return self.pop(0) ... def putback(self, val): ... self.insert(0, val) ... >>> buf = Buffer(['10', '+', '20', '*', '30']) >>> buf ['10', '+', '20', '*', '30', '10', '+', '20', '*', '30'] >>> buf.get() '10' >>> buf.get() '+' >>> buf ['20', '*', '30', '10', '+', '20', '*', '30'] >>> buf.putback('+') >>> buf ['+', '20', '*', '30', '10', '+', '20', '*', '30'] >>>
Отредактировано py.user.next (Сен. 27, 2012 03:46:13)
Офлайн
1
Клево, не знал что так можно 
class Buffer(list): def get(self): if self: return self.pop(0) def putback(self, val): self.insert(0, val)
def isint(string): try: int(string) return True except ValueError: return False def isfloat(string): try: float(string) return True except ValueError: return False def isnumber(output, num): if num: if isint(num) or isfloat(num): output.append(num) num = '' else: raise ValueError("Bad number '{0}'".format(num)) return output, num def parser(expr): expr = ''.join(expr.split()) output = [] num = '' for ch in expr: if ch in '.0123456789': num += ch elif ch in '()+-*/%': output, num = isnumber(output, num) output.append(ch) else: raise ValueError("Bad character '{0}'".format(ch)) output, num = isnumber(output, num) return output
Отредактировано baslie (Сен. 27, 2012 11:50:39)
Офлайн
857
def parser(expr): expr = ''.join(expr.split()) output = [] num = '' for ch in expr: if ch in '.0123456789': if ch == '.' and '.' in num: raise ValueError(1) num += ch elif ch in '()+-*/%': if num: output.append(num) num = '' output.append(ch) else: raise ValueError("Bad character '{0}'".format(ch)) if num: output.append(num) return output
def parser(expr): expr = ''.join(expr.split()) output = [] num = '' for ch in expr: if ch in '.0123456789': if ch == '.' and '.' in num: raise ValueError(1) num += ch elif ch in '()+-*/%': if num: if num == '.': raise ValueError(2) output.append(num) num = '' output.append(ch) else: raise ValueError("Bad character '{0}'".format(ch)) if num: if num == '.': raise ValueError(2) output.append(num) return output
#!/usr/bin/env python # -*- coding: utf-8 -*- # разделяет арифметическое выражение на лексемы def f(s): res = [] state = 'normal' i = 0 slen = len(s) while i < slen: c = s[i] if state == 'normal': num = '' was_period = False if c in '.0123456789': state = 'number' elif c in '()+-*/%': state = 'op' else: state = 'error' i -= 1 elif state == 'number': if c in '0123456789': num += c elif c == '.': if not was_period: was_period = True num += c else: state = 'error' i -= 1 if c not in '.0123456789' or i + 1 == slen: if num != '.': res.append(num) state = 'normal' if i + 1 < slen: i -= 1 else: state = 'error' i -= 1 elif state == 'op': if c in '()+-*/%': res.append(c) else: state = 'normal' i -= 1 elif state == 'error': raise ValueError('wrong character "{0}" ' 'at index {1}'.format(c, i)) i += 1 return res if __name__ == '__main__': print f('123.+1.') print f('(3+2+1+9/2-98*5+7%2)-2+4/2+(((321)-43)-1)/2.')
[guest@localhost py]$ ./expr.py
['123.', '+', '1.']
['(', '3', '+', '2', '+', '1', '+', '9', '/', '2', '-', '98', '*', '5', '+', '7', '%', '2', ')', '-', '2', '+', '4', '/', '2', '+', '(', '(', '(', '321', ')', '-', '43', ')', '-', '1', ')', '/', '2.']
[guest@localhost py]$
Отредактировано py.user.next (Сен. 28, 2012 02:57:46)
Офлайн
1
py.user.next, пардон за глупый вопрос гуглить лень: это примерно так же регулярки работают? на конечных автоматах?
Отредактировано baslie (Сен. 28, 2012 19:10:57)
Офлайн
14
Именно так и работают. Хотя в зависимости от регулярки они компилируются в детерминированный КА, недетерменированный КА и даже машину Тьюринга.
Ну и c in ‘0123456789’ стоило бы заменить на c in set('0123456789'). Там уже на двух элементах разница будет в 2 раза.
Отредактировано odnochlen (Сен. 28, 2012 19:48:04)
Офлайн