Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.
200/642
Obsah:
Navrhněte a vytvořte aplikaci realizující jednoduchý kalkulátor s posfixovou notací. Kalkulátor bude umožňovat tyto matematické operace:
+
-
*
/
nebo :
!
#
Postfixový kalkulátor je učebnicový příklad pro využití datové struktury zásobníku.
Zápis výrazu
(5+6)*(4+3)
vypadá v postfixové notaci
5 6 + 4 3 + *
... to znamená, že
Začneme s definicí grafického uživatelského rozhraní. To bude velice jednoduché, takže pro umísťování uděláte nám postačí metoda pack.
#!/usr/bin/python
# -*- coding: utf8 -*-
from Tkinter import *
##################################################
### Definice funkcí
def exit(event=None):
"""
Funkce ukončí program
"""
sys.exit(0)
##################################################
### GUI
okno = Tk()
okno.title("Reverzní kalkuláror")
okno.option_add('*Font', 'Terminus 16')
## Nadpis
Label(okno, text="Reverzní kalkulátor", borderwidth=10).pack(anchor=W)
## vstupní pole
vstupEntry = Entry(okno, width=40, borderwidth=4)
vstupEntry.pack(fill=X)
## Stavový řádek
statusLabel = Label(okno, text=u"OK", borderwidth=5, bg="#abcdef", fg="black" )
statusLabel.pack(anchor=W)
## Zásobník
zasobnikBox = Listbox(okno, height=10)
zasobnikBox.pack(fill=X)
## vyberu vstupní pole a můžu hned psát
vstupEntry.focus_set()
##################################################
## události
# při stisku Esc se aplikace zavře
okno.bind("<Escape>",exit)
##################################################
## Nekonečná smyčka
okno.mainloop()
`--> stáhnout
V této chvíli by aplikace měla být (přesto, že ještě nic nepočítá) funkční.
Pro ukládání a výběr ze zásobníku vytvoříme funkce push()
a pop()
.
# zásobník s čísly
zasobnik = []
def push(cislo):
global zasobnik
"""
Vloží číslo do zásobníku.
Návratovou hodnotou oznámí, zda se vložení povedlo.
Zároveň se oznámení vepíše do stavového řádku.
"""
try:
zasobnik.append(float(cislo))
zasobnikBox.insert(0, str(cislo) )
statusLabel.config(text=u'vkládám {} -- OK'.format(cislo), fg='black')
return True
except :
statusLabel.config(text=u'CHYBA při vkládání {}'.format(cislo), fg='red')
return False
def pop():
global zasobnik
"""
Vymaže číslo z vrcholu zásobníku
a vydá ho jakou návratovou hodnotu.
"""
zasobnikBox.delete(0)
return zasobnik.pop()
`--> stáhnout
Vstupní pole načteme z udělátka vstupEntry
. Zdrojový kód může rámcově vypadat
takto:
def makej(event=None):
vstupniData=vstupEntry.get()
for token in vstupniData.split():
# matematické operace
if token == '+':
a = pop()
b = pop()
push(a+b)
elif token == '-':
b = pop()
a = pop()
push(a-b)
# vložení čísel do zásobníku
else:
push(token)
`--> stáhnout
Vstupní pole je třeba zpracovat vždy při stisku klávesy Enter:
# při stusku Enter se provede zpracování vstupního pole
vstupEntry.bind("<Return>", makej)
`--> stáhnout
Dále je třeba zjisti zda před použitím matematické operace je k dispozici dostatečný počet argumentů. Zdrojový kód je tedy třeba obohatit o podmínky následujícího typu:
if token == '+':
if len(zasobnik) >= 2:
a = pop()
b = pop()
push(a+b)
else:
statusLabel.config(text=u'CHYBA: v zásobníku je málo čísel',fg='red')
`--> stáhnout
Tento zdrojový kód se bude několikrát opakovat, abychom si usnadnili případné pozdější úpravy je více než vhodné (přesto, že to není nutné) ho uzavřít do funkce.
def obalDvaArgumenty(fce):
"""
Obalová funkce pro kontroku zásobníku
fce -- je funkce, která se provede se dvěma
argumenty vyzvednutými ze zásobníku
"""
if len(zasobnik) >= 2:
b = pop()
a = pop()
push( fce(a,b) )
else:
statusLabel.config(text=u'CHYBA: v zásobníku je málo čísel',fg='red', bg="#555555")
def scitani(a,b):
return a+b
def makej(event=None):
vstupniData=vstupEntry.get()
# po načtení dat, vymažu vstupní pole
vstupEntry.delete(0,END)
for token in vstupniData.split():
# matematické operace
if token == '+':
obalDvaArgumenty( scitani )
elif token == '-':
obalDvaArgumenty(lambda x,y: x-y)
# vložení čísel do zásobníku
else:
push(token)
`--> stáhnout
Teď už stačí pro každou matematickou operaci vytvořit funkci nebo použít
anonymní funkci lambda
1.
Podobně bude třeba vytvořit obalovou funkci pro matematické operace s jedním argumentem.
def obalJedenArgument(fce):
"""
Obalová funkce pro kontroku zásobníku
fce -- je funkce, která se provede se jedním
argumenty vyzvednutými ze zásobníku
"""
if len(zasobnik) >= 1:
a = pop()
push( fce(a) )
else:
statusLabel.config(text=u'CHYBA: v zásobníku je málo čísel',fg='red', bg="#555555")
def makej(event=None):
...
elif token == '!':
obalJedenArgument(lambda x: x*x)
elif token == '#':
obalJedenArgument(lambda x: x**0.5)
...
`--> stáhnout
Teď už jen zařídit aby se desetinná čárka, chovala stejně jako desetinná tečka.
K tomu dobře poslouží metoda replace
datového tipu str
.
>>> "3,14 5,6".replace(',','.')
--> '3.14 5.6'
`--> stáhnout
Funkce lambda
viz funkcionální programování ↩