Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.

200/642

Kalkulačka s reverzní notací -- příklad

Obsah:

  1. Zadání
  2. Reference
  3. Krátký rozbor
  4. GUI
  5. Zásobník
  6. Čtení vstupního pole

Zadání

Navrhněte a vytvořte aplikaci realizující jednoduchý kalkulátor s posfixovou notací. Kalkulátor bude umožňovat tyto matematické operace:

Reference

Krátký rozbor

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

  1. vždy když se narazí na číslo je uloženo do zásobníku
  2. vždy když se narazí na znak matematické operace provede se výpočet s posledními dvěma čísly ze zásobníku a výsledek se opět uloží do zásobníku.

GUI

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í.

Zásobník

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

Čtení vstupního pole

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 lambda1.

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

| navigace |

Licence Creative Commons Valid XHTML 1.0 Strict Valid CSS! Antispam.er.cz Blog: Tlapicka.net