Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Excerpt

I den siste delen av rpncalcRPNCalc-eksemplet deler i koden i to moduler, én som håndterer stack-logikken og operasjonene og én del som håndterer kommunikasjonen med brukeren.

Moduler

I rpncalc2 gjorde vi koden ryddigere ved å definere funksjoner, men etterhvert som programmer blir større, er det en fordel å kunne dele opp koden i flere filer, som ofte kalles moduler. En av filene inneholder hovedprogrammet (f.eks. med main-funksjonen) som startes av brukeren, og denne vil bruke funksjoner (og variabler) definert i de andre modulene. Med en fornuftig oppdeling, kan selv store program bli oversiktlige og greie å håndtere. Moduler har på en måte samme rolle som funksjoner, men i større skala.

...

  • rpncalc4core: inneholder kjernelogikken som håndterer operand-stacken
  • rpncalc4ui: inneholder koden som håndterer interaksjon med brukeren (ui-endelsen står for user interface, som vi på norsk kaller brukergrensesnitt)

rpncalc4core-modulen

Denne modulen inneholder alle variabler og funksjoner knyttet til håndtering av operand-stacken, først og fremst operands-variablen og i tillegg funksjonene som direkte leser og endrer den. I praksis er dette første del av koden fra rpncalc3.py:

...

Det "eneste" denne modulen gjør er å deklarere og initialisere operands-variablen og tilhørende funksjoner. Når modulen kjøres/aktiveres så opprettes som vanlig et notatark kalt rpncalc4core hvor alle variablene og funksjonene noteres ned. Imidlertid blir ingen av funksjonene kjørt av modulen selv, de må kalles utenfra, av kode i andre moduler.

rpncalc4ui-modulen

Denne modulen håndterer interaksjon med brukeren og inneholder bare main-metoden (fra rpncalc3.py) og et enkelt kall til denne, altså hovedprogrammet som kjøres av brukeren.

...

PlantUML Macro
object rpncalc4core {
	operands = []
	isOperands()
	printOperands()
	popOperands()
	pushOperand()
	plus()
}

object rpncalc4ui {
	main()
}

object "1: main" as main1 {
	token = "+"
	operand = ...
}

object "1: plus" as plus1 {
}
object "1: popOperands" as popOperands1 {
	n = 2
}

rpncalc4ui <-- main1
rpncalc4core <-- plus1
rpncalc4core <-- popOperands1

main1 ..> plus1
plus1 ..> popOperands1

For å referere til navn (variabler og funksjoner) i en annen og importert modul, så brukes altså modul-navnet og punktum som prefiks, f.eks. rpncalc4core.isOperand i if-betingelsen. Dette er i grunnen samme notasjon som brukes når en kaller operands-lista sin pop-metode. En kan tenke på lista som et eget ark med funksjoner inni, men da er en inne på objektorientering...

Importerbar kode

Code Block
 ## rpncalc4core.py
operands = []
 
def isOperand(token):
    try:
        float(token)
        return True
    except ValueError:
        return False
 
def printOperands():
    print(operands)
 
def popOperands(n):
    global operands
    size = len(operands)
    if size >= n:
        result = operands[size - n : size]
        global operands
        operands = operands[0 : size - n]
        return result
    else:
        return None
 
def pushOperand(operand):
    operands.append(operand)
 
def plus():
    ops = popOperands(2)
    if not (ops is None):
        pushOperand(ops[0] + ops[1])
 
def minus():
    ops = popOperands(2)
    if not (ops is None):
        pushOperand(ops[0] - ops[1])
Code Block
# rpncalc4ui.py
import rpncalc4core
 
def main():
    while (True):
        rpncalc4core.printOperands()
        token = raw_input(" > ")
        if rpncalc4core.isOperand(token):
            operand = float(token)
            rpncalc4core.pushOperand(operand)
        elif token == "exit":
            break
        elif token == "+":
            rpncalc4core.plus()
        elif token == "-":
            rpncalc4core.minus()
        else:
            print("Unsupported operator: " + token)
    print("program exited")
 
main()