Калькулятор. Just for fun
# calculator.py
import wx
class Calc:
def __init__(self):
self.formula = ""
self.text = ""
self.clearText = False
def Add(self, char):
if char.isdigit() or char == '.':
if self.clearText:
self.text = char
else:
self.text = self.text + char
self.formula = self.formula + char
self.clearText = False
elif char == "Cls":
self.text = self.formula = ""
elif char == "Bck":
if len(self.formula) and self.formula[-1].isdigit():
self.formula = self.formula[:-1]
self.text = self.text[:-1]
elif char == "=":
try:
self.formula = self.text = str(eval(self.formula))
except StandardError:
self.text = "Error"
self.formula = ''
self.clearText = True
else:
self.formula = self.formula + char
self.clearText = True
def Get(self):
return self.text
class MyFrame(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, wx.Size(300, 250))
self.calc = Calc()
sizer = wx.BoxSizer(wx.VERTICAL)
self.display = wx.TextCtrl(self, -1, '', style=wx.TE_RIGHT | wx.TE_READONLY )
sizer.Add(self.display, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 4)
gs = wx.GridSizer(4, 4, 3, 3)
bNumbers = [(wx.Button(self, wx.NewId(), "%i" %i), 0, wx.EXPAND) for i in xrange(10)]
bExeTexts = ["Cls", "Bck", "="]
bExes = [(wx.Button(self, wx.NewId(), bExeTexts), 0, wx.EXPAND) for i in xrange(3)]
bOperTexts = ["/", "*", "-", "+", "."]
bOpers = [(wx.Button(self, wx.NewId(), bOperTexts), 0, wx.EXPAND) for i in xrange(5)]
buttons = bExes + [(wx.Size(10, 10), 0, wx.EXPAND)] + bNumbers + bOpers
gs.AddMany(buttons)
sizer.Add(gs, 1, wx.EXPAND)
self.SetSizer(sizer)
self.Centre()
self.Bind(wx.EVT_BUTTON, self.OnButton)
def OnButton(self, event):
self.calc.Add(self.FindWindowById(event.GetId()).GetLabel())
self.display.ChangeValue(self.calc.Get())
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, 'calculator.py')
frame.Show(True)
self.SetTopWindow(frame)
return True
def main():
app = MyApp(redirect = False)
app.MainLoop()
if __name__ == "__main__":
main()
Версия сырая - пару функций напрашиваются на дробление. Но вроде работает и работает по логике стандартного калькулятора.
Основное жульничество - в использовании функции eval().
Ну и построение интерфейса - процесс занимательный, ибо конструктор интерфейса бы насоздавал втрое больше кода.
Будет интересно, если кто-нибудь создаст аналогичное приложение с помощью более краткого кода. Подозреваю, что это нетрудно. Использование другого языка приветствуется.
def __init__(self):
self.formula = ""
self.text = ""
self.clearText = False
# создаем словарь команд
self.comDict = {"Cls" : self.OnCls, "Bck": self.OnBck,\
"=": self.OnEquals, ".": self.OnDigit}
for i in xrange(10):
self.comDict[str(i)] = self.OnDigit
for strng in ["/", "*", "-", "+"]:
self.comDict[strng] = self.OnSign
def OnDigit(self, char):
if self.clearText:
self.text = char
else:
self.text = self.text + char
self.formula = self.formula + char
self.clearText = False
def OnCls(self, char):
self.text = self.formula = ""
def OnBck(self, char):
if len(self.formula) and self.formula[-1].isdigit():
self.formula = self.formula[:-1]
self.text = self.text[:-1]
def OnEquals(self, char):
try:
self.formula = self.text = str(eval(self.formula))
except StandardError:
self.text = "Error"
self.formula = ''
self.clearText = True
def OnSign(self, char):
self.formula = self.formula + char
self.clearText = True
def Add(self, char):
self.comDict[char](char)
def Get(self):
return self.text
Хм. Неясно, заменил ли case на полиморфизм, но главное, что избавился от case.
Примечания:
1. русский комментарий можно убрать или добавить строку вначале файла:
#-*- coding:cp1251 -*-
2. тупое тестирование показало, что в логике есть ошибки, но для данного эксперимента пока это не важно.
А вот если без eval и при помощи функционального программирования? :)
А вот если без eval и при помощи функционального программирования? :)
Вот поэтому понимать ФП имхо, лучше на Хаскелле:). В Питоне можно использовать ООП и обычный императивный стиль (процедурный), и постоянно будет соблазн переписать то, что вызывает затруднения при реализации в ФП. А вот в Хаскелле можно писать либо функционально, либо [size=1]сразу повеситься[/size] через монады.
А вот если без eval и при помощи функционального программирования? :)
Мне пока не до понтов - я еще нормально ООП и даже процедурное программирование не изучил. Да и вообще, пока мне требуется, чтобы работало, имело GUI и требовало меньше писанины, чем C++. Потому добавлю еще раз переделанный класс...
def __init__(self):
self.formula = ""
self.text = ""
self.afterEquals = False
self.afterSign = False
# создаем словарь команд
self.comDict = {"Cls" : self.OnCls, "Bck": self.OnBck,\
"=": self.OnEquals, ".": self.OnDigit}
for i in xrange(10):
self.comDict[str(i)] = self.OnDigit
for strng in ["/", "*", "-", "+"]:
self.comDict[strng] = self.OnSign
def OnDigit(self, char):
if self.afterEquals:
self.formula = self.text = char
elif self.afterSign:
self.text = char
self.formula = self.formula + char
else:
self.text = self.text + char
self.formula = self.formula + char
self.afterEquals = self.afterSign = False
def OnCls(self, char):
self.text = self.formula = ""
self.afterEquals = self.afterSign = False
def OnBck(self, char):
if len(self.formula) and self.formula[-1].isdigit():
self.formula = self.formula[:-1]
self.text = self.text[:-1]
def CalcFormula(self):
if len(self.formula) and not self.afterSign:
try:
res = str(eval("1.0*" + self.formula))
if res[-2:] == ".0":
res = res[:-2]
self.formula = self.text = res
except StandardError:
self.text = "Error"
self.formula = ""
def OnEquals(self, char):
self.CalcFormula()
self.afterEquals = True
def OnSign(self, char):
self.CalcFormula()
self.formula = self.formula + char
self.afterEquals = False
self.afterSign = True
def Add(self, char):
self.comDict[char](char)
def Get(self):
return self.text
Вроде теперь соблюдается логика стандартного виндового калькулятора, да и код класса Calc почти читаем (в отличие от кода конструирования GUI). За читерство с eval пришлось платить глюками типа 1/2 = 0. Пришлось поставить заплатку.
С функциональным программированием не разобрался пока.
Пока я не понял всю прелесть функционального программирования (ФП). В примерах в основном показывают как генерировать списки, как заменить цикл. Списки я пока генерирую с помощью списковых включений (см. генерацию кнопок). Это вроде не ФП. А зачем менять циклы на рекурсии тоже не ясно.
А вообще, чтобы не просто флейм разводить, можно попытаться с помощью ФП решить простую задачку:
в строке записаны 2 операнда (2 числа) и оперетор. Вычислить выражение. То есть, заменить eval в простейшем случае.
Сможет кто-нибудь? Язык не важен.
Я и сам смогу решить, только наверняка сделаю это в императивном стиле, даже если в тексте функции будут лямбды и прочие итераторы...
Для простейшего случая (когда оператор состоит из одного символа и в выражении нет пробелов) моих знаний хватает пока на следующее:
def MyEval(formula):
def GetOperators():
for i in xrange(len(formula)):
if not formula.isdigit():
return [formula[:i], formula, formula[i + 1:]]
return None
opDict = {"+" : operator.add, "-" : operator.sub, \
"*" : operator.mul, "/" : operator.div}
op = GetOperators()
try:
return opDict[op[1]](float(op[0]), float(op[2]))
except:
return None
Но, насколько я понимаю, это не ФП.
Это понятно. Но тема учебная или, как я ее назвал - Just for fun. То есть тут позволительны всякие эксперименты.
Другое дело, что все говорят "функциональное программирование, функциональное программирование", но толкового практического примера, в котором оно бы пригодилось добиться трудно.
Соответственно, раз я его не знаю, то и говорю, что оно есть только модное выражение для понта :)