Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Калькулятор. Just for fun

87
21 мая 2009 года
Kogrom
2.7K / / 02.02.2008
К теме про языки программирования. Решил собрать какое-нибудь простинькое приложение на неизвестном мне языке, для иллюстрации возможностей. Попытался сделать простой калькулятор на wxPython (надоело жить без GUI). Но так как языка не знаю, то для начала переделал пример с туториала - поменял структуру, повыкидывал лишнее.
Код:
#!/usr/bin/python

# 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().

Ну и построение интерфейса - процесс занимательный, ибо конструктор интерфейса бы насоздавал втрое больше кода.

Будет интересно, если кто-нибудь создаст аналогичное приложение с помощью более краткого кода. Подозреваю, что это нетрудно. Использование другого языка приветствуется.
87
22 мая 2009 года
Kogrom
2.7K / / 02.02.2008
Мартин Фаулер пишет, что длинные функции плохо читаемы, потому раздроблю функцию Add класса Calc. Кроме того, он не рекомендует использовать конструкции case. Пока не придумал как сделать красиво - потому заменю как попало.

Код:
class Calc:
    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. тупое тестирование показало, что в логике есть ошибки, но для данного эксперимента пока это не важно.
3
23 мая 2009 года
Green
4.8K / / 20.01.2000
eval - это читерство, а class - это ООП.
А вот если без eval и при помощи функционального программирования? :)
63
23 мая 2009 года
Zorkus
2.6K / / 04.11.2006
Цитата: Green
eval - это читерство, а class - это ООП.
А вот если без eval и при помощи функционального программирования? :)


Вот поэтому понимать ФП имхо, лучше на Хаскелле:). В Питоне можно использовать ООП и обычный императивный стиль (процедурный), и постоянно будет соблазн переписать то, что вызывает затруднения при реализации в ФП. А вот в Хаскелле можно писать либо функционально, либо [size=1]сразу повеситься[/size] через монады.

87
23 мая 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Green
eval - это читерство, а class - это ООП.
А вот если без eval и при помощи функционального программирования? :)


Мне пока не до понтов - я еще нормально ООП и даже процедурное программирование не изучил. Да и вообще, пока мне требуется, чтобы работало, имело GUI и требовало меньше писанины, чем C++. Потому добавлю еще раз переделанный класс...

Код:
class Calc:
    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. Пришлось поставить заплатку.

С функциональным программированием не разобрался пока.
5
24 мая 2009 года
hardcase
4.5K / / 09.08.2005
[offtop]
Цитата: Zorkus
Вот поэтому понимать ФП имхо, лучше на Хаскелле:).


Нафиг, ML-семейство человечнее...

87
24 мая 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Green
А вот если без eval и при помощи функционального программирования? :)


Пока я не понял всю прелесть функционального программирования (ФП). В примерах в основном показывают как генерировать списки, как заменить цикл. Списки я пока генерирую с помощью списковых включений (см. генерацию кнопок). Это вроде не ФП. А зачем менять циклы на рекурсии тоже не ясно.

А вообще, чтобы не просто флейм разводить, можно попытаться с помощью ФП решить простую задачку:
в строке записаны 2 операнда (2 числа) и оперетор. Вычислить выражение. То есть, заменить eval в простейшем случае.

Сможет кто-нибудь? Язык не важен.

Я и сам смогу решить, только наверняка сделаю это в императивном стиле, даже если в тексте функции будут лямбды и прочие итераторы...

14
25 мая 2009 года
Phodopus
3.3K / / 19.06.2008
А моё ИМХО - раз есть eval, значит надо использовать eval. Если это не вызывает проблем (безпасность там и т.п.). Ибо на то оно и преимущество питона. Зачем его этого лишать?
87
25 мая 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Kogrom
Я и сам смогу решить, только наверняка сделаю это в императивном стиле



Для простейшего случая (когда оператор состоит из одного символа и в выражении нет пробелов) моих знаний хватает пока на следующее:

Код:
import operator

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

Но, насколько я понимаю, это не ФП.
87
25 мая 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Phodopus
А моё ИМХО - раз есть eval, значит надо использовать eval. Если это не вызывает проблем (безпасность там и т.п.). Ибо на то оно и преимущество питона. Зачем его этого лишать?


Это понятно. Но тема учебная или, как я ее назвал - Just for fun. То есть тут позволительны всякие эксперименты.

Другое дело, что все говорят "функциональное программирование, функциональное программирование", но толкового практического примера, в котором оно бы пригодилось добиться трудно.

Соответственно, раз я его не знаю, то и говорю, что оно есть только модное выражение для понта :)

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог