Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

logisk verdi
Code Block
languagepython
def isOperand(token):
    return token[0].isdigit()

...
while (True):
    ...
    token = raw_input(" > ")
    if isOperand(token):
        operand = float(token)
	...
... 

Øverst defineres isOperand-funksjonen: Den tar inn en tekst (string) og returnerer en

sannhetsverdi (boolean, altså True eller False).

Merk at sannhetsverdien som isOperand returnerer kun er avhengig av argumentet som overføres, fordi funksjonskoden (som ofte kalles funksjonskroppen) ikke refererer til noen variabler utenfor funksjonen, f.eks. operands. Funksjonen har heller ingen side-effekter, dvs. den endrer ingenting i omgivelsene sine. Dette betyr at funksjonen er en matematisk funksjon, fordi den alltid returnerer samme verdi for samme sett av argumenter. Slike funksjoner er enklere å analysere bruken av, fordi de ikke er avhengig av omgivelsene.

Lenger ned brukes av isOperand: token overføres som argument returverdien brukes direkte som betingelsen i if-setningen.

Et problem med originalkoden i rpncalc1.py var at den ikke sjekket om det var nok operander tilgjengelig før den pop-et verdier i elif-grenen for pluss- og minus-operatorene. Disse bør begge sjekke om lista som operands-variablen refererer til har mange nok verdier.

...

Siden begge grener har behov for å sjekke dette, så kan en oppnå gjenbruk ved å definere en funksjon som gjør sjekken:

Code Block
languagepython
operands = []
...
def hasOperands(n):
    return len(operands) >= n
...
while (True):
	...
    elif token == "+":
		if hasOperands(2):
			...
    elif token == "-":
		if hasOperands(2):
			...
	...

hasOperands returnerer True dersom operands-lista er minst så mange verdier som angitt i argumentet og False om så ikke er tilfelle.

Denne funksjonen er ikke en matematisk funksjon, fordi resultatet er avhengig av verdier utenom argumentene, nemlig operands-variablen.

hasOperands-funksjonen brukes i elif-grenene for både pluss- og minus-operatorene.

Til sist definerer vi funksjoner for hver av de to operatorene pluss og minus, for å gjøre løkke-koden enda ryddigere:

Code Block
languagepython
operands = []
...
def plus():
    if hasOperands(2):
        operands.append(operands.pop() + operands.pop())

def minus():
    if hasOperands(2):
        operands.append(- (operands.pop() - operands.pop()))
...
while (True):
	...
    elif token == "+":
        plus()
    elif token == "-":
        minus()
	...

Her defineres en funksjon for hver av operatorene, for å forenkle løkke-koden. Vi får ingen gjenbruk av kode, fordi hver av funksjonene bare kalles ett sted. Begge disse funksjonene er avhengig av omgivelsene sine, både fordi de bruker en annen funksjon som er det, og fordi de selv refererer til en variabel utenfor seg selv, nemlig operands. I tillegg har begge side-effekten at lista som operands refererer til endres, dersom den er har minst to operander.

Ser du forresten hvordan feilen med minus-logikken i rpncalc1.py er rettet?

 

Hva skjer nå funksjoner kalles?

I rpncalc1-eksemplet viste vi hvordan variabler noteres på et slags ark som opprettes når Python-programmet kjøres. Noe lignende skjer når en funksjon kalles: Det opprettes et lite ark, tenk Post-It-lapp, hvor parametre og lokale variabler noteres. Dette lille arket kommer på toppen av program-arket, slik at koden inni funksjonen kan referere til variabler på både det lille funksjons-arket og det store program-arket. Figuren under illustrerer situasjonen når hasOperands er kalt med 2 som argument:

PlantUML Macro
object rpncalc2 {
	operands = []
	token = "+"
	operand = ...
}

object "1: hasOperand" as hasOperand1 {
	n = 2
}

rpncalc2 <right- hasOperand1
Til venstre

 

For ordens skyld, her er hele koden:

 

 

Dersom en senere finner en bedre måte å gjøre det på, så er det bare å bytte ut funksjonskoden, uten at koden som kaller funksjonen må endres. 

Code Block
languagepython
# rpncalc2.py
operands = []

def isOperand(token):
    return token[0].isdigit()

def hasOperands(n):
    return len(operands) <>= n

def plus():
    if hasOperands(2):
        operands.append(operands.pop() + operands.pop())
def minus():
    if hasOperands(2):
        operands.append(- (operands.pop() - operands.pop()))
while (True):
    print(operands)
    token = raw_input(" > ")
    if isOperand(token):
        operand = float(token)
        operands.append(operand)
    elif token == "exit":
        break
    elif token == "+":
        plus()
    elif token == "-":
        minus()
    else:
        print("Unsupported operator: " + token)
print("program exited")