TDDE44 Programmering, grundkurs¶

FörelÀsning 2.1-2.2¶

Johan Falkenjack, johan.falkenjack@liu.se¶

  • Ofiltrerad.

FörelĂ€sningsöversikt FÖ 2.1-2.2¶

  • ~Programflöde - Anropsstacken och scope~
  • Upprepning
    • ~Rekursion~
    • Iteration
      • Fördefinierat antal upprepningar - for
      • Generell iteration - while
  • Om vi hinner, lite om:
    • Felsökning
    • Parprogrammering
    • Programmeringsparadigm
    • LĂ€sa/skriva textfiler i Python

Kort repetition: Anropsstacken¶

  • En stack, datastruktur dĂ€r vi bara kan ta bort eller lĂ€gga till element "pĂ„ toppen", eller mer generellt, i ena Ă€nden.
    • Följer principen: First in, last out eller FILO.
    • (Vi kommer senare att stöta pĂ„ datastrukturen kö, eller queue som följer FIFO eller First in, first out.)
  • Anropsstacken, en stack med omgivningar, eller frames, som hĂ„ller koll pĂ„:
    1. Var olika namn och bindningar mellan namn och vÀrden existerar, dvs. variablers scope.
    2. Var i programflödet exekveringen av koden just nu befinner sig och
    3. vart exekveringen skall fortsÀtta nÀr den aktuella funktionen returnerar.

Kort repetition: Rekursion¶

  • Upprepning med hjĂ€lp av funktionsanrop och anropsstacken.
  • En funktion anropar sig sjĂ€lv med enklare och enklare delproblem tills vi nĂ„r ett trivialt basfall dĂ€r svaret Ă€r givet utan ytterligare anrop.
  • För varje anrop lĂ€ggs en ny frame pĂ„ stacken som hĂ„ller reda pĂ„ hur resultaten pĂ„ de lösta delproblemen skall returneras tillbaka genom alla anropen.

Rekursiv kontra iterativ processlösning¶

  • Rekursiv process: Kombinationen av lösningarna till alla delproblem utförs först nĂ€r vi nĂ„tt basfallet. Det rekursiva anropet mĂ„ste ske innan dess returvĂ€rde kan kombineras med lösningen pĂ„ det aktuella delproblemet.
  • Iterativ process: Lösningarna pĂ„ delproblemen kombineras i en ackumulator allt eftersom de berĂ€knas. Det rekursiva anropet Ă€r alltid det sista som utförs i funktionen.
  • Olika problem kan lĂ€mpa sig bĂ€ttre för ena eller andra approachen, i mĂ„nga fall gĂ„r bĂ„da lika bra och i vissa komplexa fall blandar vi bĂ„da teknikerna.
  • Ofta Ă€r returvĂ€rdet i basfallet i den rekursiva processlösningen samma vĂ€rde som startvĂ€rdet pĂ„ ackumulatorn i den motsvarande iterativa processlösningen.
def tallest(heights):
    if heights == []:
        return 0
    tallest_in_rest = tallest(heights[1:])
    if heights[0] > tallest_in_rest:
        return heights[0]
    else:
        return tallest_in_rest
print("tallest([10, 79, 41])", "->", tallest([10, 79, 41]))
  • Rekursiv processlösning.
  • Det rekursiva anropet sker innan returvĂ€rdet "kombineras" (hĂ€r Ă€r det inte sĂ„ mycket en kombination som en antingen-eller, men principiellt Ă€r det samma sak).
  • Resultatet byggs upp först nĂ€r de rekursiva anropen returnerar, "pĂ„ vĂ€gen upp" eller "pĂ„ vĂ€gen tillbaka", och alla de rekursiva anropen kommer alltsĂ„ inte nödvĂ€ndigtvis att returnera samma vĂ€rde.
def tallest_iter(heights, tallest_found):
    if heights == []:
        return tallest_found
    if heights[0] > tallest_found:
        return tallest_iter(heights[1:], heights[0])
    else:
        return tallest_iter(heights[1:], tallest_found)
print("tallest_iter([10, 79, 41])", "->", tallest_iter([10, 79, 41]))
  • Iterativ processlösning.
  • Resultatet byggs upp i en ackumulator, tallest_found, "pĂ„ vĂ€gen ner".
  • Det rekursiva anropet Ă€r det sista som sker i funktionen och alla anrop kommer alltsĂ„ alltid att returnera vĂ€rdet av nĂ€sta anrop, dvs. alltid det slutgiltiga vĂ€rdet pĂ„ tallest_found.
Prova att köra bÄde tallest och tallest_iter i Python Tutor.

Iteration - Upprepning utan nya anrop¶


I folkmun ofta kallat att loopa
No description has been provided for this image
  • AnvĂ€nds för att köra samma kod flera gĂ„nger
  • Exempel
    • skriva ut varje element i en lista
    • addera ett tal till en variabel tills det överstiger ett visst vĂ€rde
    • flytta fram figuren tills den nuddar en vĂ€gg

Rekursion kan lösa alla problem - i teorin¶

  • Alan Turing (kĂ€nd frĂ„n filmer som the Imitation Game etc.) beskrev en universell maskin som kunde berĂ€kna "det mesta", inklusive beskriva och implementera sin egen funktion, sendermera kallad den Universella Turingmaskinen.
  • Alonzo Church (mindre kĂ€nd men sedermera Turings handledare nĂ€r Turing doktorerade) hade beskrivit $\lambda$-kalkylen, en matematisk formalism som ocksĂ„ kunde berĂ€kna "det mesta".
  • Church-Turing-satsen sĂ€ger att $\lambda$-kalkylen och den Universella Turing-maskinen Ă€r ekvivalenta.
  • En följd av detta Ă€r att rekursion Ă€r Turingkomplett, dvs. alla problem som kan lösas av en dator kan, i teorin, lösas med rekursion.
  • Beviset lĂ€mnas som övning (efter att ha lĂ€st t.ex. TDDD85 Formella sprĂ„k och automatateori och ytterligare nĂ„gra kurser i berĂ€kningsteori).

TvÄ iterationssatser i Python¶

  • for-satsen
    • Utför ett stycke kod för varje element i en sekvens.
  • while-satsen
    • Som en if-sats, men blocket upprepas sĂ„ lĂ€nge som villkoret Ă€r uppfyllt.

for-loopen¶

for element in a_sequence:
    # Code to run for every element in a_sequence
  • Vi sĂ€ger att for-loopen loopar över en sekvens.
  • I varje upprepning, varje iteration, innehĂ„ller loopvariabeln element det aktuella vĂ€rdet i sekvensen.
    • OBS! Namnet element Ă€r helt godtyckligt och kan sĂ€ttas till vad som helst. LĂ€mpligen vĂ€ljer vi oftast ett mindre abstrakt namn som tydligt beskriver vad de enskilda elementen faktiskt representerar.
  • Ingen manuell kontrollvariabel.
  • Loopen slutar nĂ€r vi gĂ„tt igenom hela a_sequence
  • OBS! variablerna behöver inte heta element och a_sequence

GÄ igenom en lista med for¶

In [3]:
names = ["Ada", "Beata", "Cecilia", "Diana"]

for name in names:
    print(name)
    
Ada
Beata
Cecilia
Diana
  • Notera att loopvariabeln hĂ€r heter name, som vi har valt eftersom vi loopar över en lista med namn.
  • GĂ„ igenom alla element i names.
  • Det aktuella elementet refereras till via variabeln name i loopen.
  • Notera hur mycket enklare Ă€n motsvarande while-loop.

LÀngsta lÀngden med for¶

In [41]:
lengths = [120, 100, 160, 124, 112, 150, 188, 134]
tallest_found = 0

for length in lengths:
    print("LĂ€ngsta hittills:", tallest_found)
    if length > tallest_found:
        tallest_found = length
 
print("LĂ€ngsta i hela listan:", tallest_found)
LĂ€ngsta hittills: 0
LĂ€ngsta hittills: 120
LĂ€ngsta hittills: 120
LĂ€ngsta hittills: 160
LĂ€ngsta hittills: 160
LĂ€ngsta hittills: 160
LĂ€ngsta hittills: 160
LĂ€ngsta hittills: 188
LĂ€ngsta i hela listan: 188
for length in lengths:
    if length > max_length:
        max_length = length
  • Notera att vi inte behövde varken funktion eller funktionsanrop

GÄ igenom en strÀng med for¶

  • HĂ€r har vi valt namnet char för loopvariabeln eftersom vi loopar över delstrĂ€ngar bestĂ„ende av enskilda tecken, characters, i strĂ€ngen.
In [61]:
word = "fantastic"

for char in word:
    print(char)
f
a
n
t
a
s
t
i
c
In [62]:
# För bara halva word
for char in word[len(word)//2:]:
    print(char)
a
s
t
i
c
  • GĂ„ igenom alla tecken i word.
  • Det aktuella tecknet refereras till via variabeln char i loopen.

Byt ut bokstÀver¶

  • Variabeln new_string Ă€r vĂ„r ackumulatorvariabel, dvs. en variabel vars vĂ€rde stegvis byggs upp under iterationen.
    • PĂ„ samma sĂ€tt som vi anvĂ€nde en ackumulatorvariabel i en iterativ processlösning nĂ€r vi anvĂ€nde rekursion för upprepningen.
  • Kan vara nĂ€stan vilken klass som helst, strĂ€ng, lista, tal, osv.
  • I det hĂ€r fallet vill vi ta bort alla svenska tecken ur en strĂ€ng.
In [7]:
swe_string = "StrÀng med svenska tecken som Ä, À och ö."
new_string = ""

for char in swe_string:
    if char == "Ă„":
        new_string = new_string + "a"
    elif char == "Ă€":
        new_string = new_string + "a"
    elif char == "ö":
        new_string = new_string + "o"
    else:
        new_string = new_string + char
            
print(new_string)
Strang med svenska tecken som a, a och o.
if char == "Ă„":
        new_string = new_string + "a"
    elif char == "Ă€":
        new_string = new_string + "a"
    elif char == "ö":
        new_string = new_string + "o"
    else:
        new_string = new_string + char

Byt ut bokstÀver, nÄgot kortare¶

  • Vi kan anvĂ€nda operatorn in, inte att förvĂ€xla med nyckelordet in i en for-sats, för att kontrollera för bĂ„de Ă„ och Ă€ samtidigt, eftersom vi ska göra samma sak i bĂ„da de fallen, lĂ€gga till ett a:
In [54]:
swe_string = "StrÀng med svenska tecken som Ä, À och ö."
new_string = ""

for char in swe_string:
    if char in "ÄÀ":
        new_string = new_string + "a"
    elif char == "ö":
        new_string = new_string + "o"
    else:
        new_string = new_string + char
            
print(new_string)
Strang med svenska tecken som a, a och o.

PÄminnelse om strÀngar och deras element¶

  • Varje element i en strĂ€ng Ă€r i sig en strĂ€ng, Python har ingen separat datatyp för enskilda tecken.
  • Alla element i en strĂ€ng Ă€r alltsĂ„ en strĂ€ng med lĂ€ngden 1.
  • Litteralerna för tecken kan dock ibland röra till det för oss.
    • Kom ihĂ„g att en litteral bara Ă€r en textuell representation av det underliggande vĂ€rdet.
    • En litteral för vad som i Python rĂ€knas som ett enskilt tecken kan ibland behöva skrivas med mer Ă€n ett tecken pĂ„ skĂ€rmen. T.ex. för specialtecken som "\n", en radbrytning:
In [52]:
print("LĂ€ngden av en radbrytning:", len("\n"))
print("LĂ€ngden av tre radbrytningar:", len("\n\n\n"))
LĂ€ngden av en radbrytning: 1
LĂ€ngden av tre radbrytningar: 3
In [53]:
chars_in_string = []
for char in "\n\n\n":
    chars_in_string = chars_in_string + [char]
print('Tecken i strÀngen "\\n\\n\\n":', chars_in_string)
Tecken i strÀngen "\n\n\n": ['\n', '\n', '\n']

Funktionen range¶

  • Skapar en "virtuell lista med heltal" (tekniskt sett en sk generator) som man kan anvĂ€nda som en vanlig lista med heltal.
  • range(stop): Börja frĂ„n 0, öka med 1 och fortsĂ€tt generera heltal sĂ„ lĂ€nge som heltalet Ă€r mindre Ă€n stop
  • range(start, stop): Börja frĂ„n start, öka med 1 och fortsĂ€tt generera heltal sĂ„ lĂ€nge som heltalet Ă€r mindre Ă€n stop
  • range(start, stop, step): Börja frĂ„n start, öka med step och fortsĂ€tt att generera heltal sĂ„ lĂ€nge som heltalet Ă€r mindre Ă€n stop
  • För att fĂ„ en riktig lista kan vi skicka returvĂ€rdet frĂ„n range till funktionen list, t.ex. list(range(10))

Funktionen range¶

In [8]:
list(range(10))
Out[8]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [9]:
list(range(4,6))
Out[9]:
[4, 5]
In [13]:
list(range(10, 101, 10))
Out[13]:
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
In [12]:
list(range(10, 110, 10))
Out[12]:
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
In [15]:
list(range(10, 0, -2))
Out[15]:
[10, 8, 6, 4, 2]
In [16]:
list(range(10, 3, -2))
Out[16]:
[10, 8, 6, 4]

Reglerna för range motsvarar reglerna för slice¶

  • sequence[start:stop:step]
  • range(start, stop, step)
  • step kan vara positiv eller negativ men Ă€r 1 om inget annat anges.
  • Om start > stop sĂ„ mĂ„ste step vara negativ annars Ă€r resultatet tomt.
  • BĂ„de range(start, stop) och sequence[start:stop] indikerar halvöppna intervall $[start, stop)$.
    • Med andra ord, alla index $i$ sĂ„dana att $start \leq i \lt stop$.
In [59]:
# För att illustrera anvÀnder vi följande sekvens:
sequence = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list(range(10)), "==", sequence[:10])
print(list(range(4, 8)), "==", sequence[4:8])
print(list(range(6, 2, -2)), "==", sequence[6:2:-2])
# Men range behöver ingen fördefinierad sekvens att utgÄ ifrÄn utan genererar de vÀrden som efterfrÄgas:
print(list(range(8, 15)), "!=", sequence[8:15])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[4, 5, 6, 7] == [4, 5, 6, 7]
[6, 4] == [6, 4]
[8, 9, 10, 11, 12, 13, 14] != [8, 9]

for med range¶

In [60]:
# Skriv ut siffrorna 0 till 9 (10 siffror)

for i in range(0, 10, 1):
    print(i)
0
1
2
3
4
5
6
7
8
9

for med range över lÀngden pÄ en sekvens¶

Ofta överflödigt:

In [21]:
names = ["Adam", "Bethany", "Chris"]
# Skriv ut elementen i listan names
for i in range(len(names)):
    print(names[i])
Adam
Bethany
Chris
  • HĂ€r hade det varit lĂ€mpligare att loopa direkt över namnen.

for med range över lÀngden pÄ en sekvens¶

men ibland praktiskt:

In [22]:
names = ["Adam", "Bethany", "Chris"]
# Skriv ut elementen i listan names
for i in range(len(names)):
    print(names[i] + " likes " + names[(i+1) % len(names)])
Adam likes Bethany
Bethany likes Chris
Chris likes Adam

(För den som vill se en mer "pythonic" approach, kolla upp funktionen enumerate eller Ànnu hellre zip.)

while-loopen¶

  • Som en if-sats, men upprepad sĂ„ lĂ€nge villkoret Ă€r sant.
  • Satserna i blocket som tillhör while-loopen upprepas tills while-satsens villkor blir falskt.
  • Först kontrolleras om villkoret Ă€r sant.
    • Om villkoret Ă€r sant utförs koden i blocket en gĂ„ng, sedan kontrolleras villkoret igen, osv.
    • Om villkoret inte Ă€r sant "hoppar" exekveringen av koden till efter while-satsens kodblock precis som för en if-sats.
  • Varje upprepning eller "varv" loopen gör kallas för en iteration precis som för for.
while boolean:
    # Code to repeat

Exempel¶

  • RĂ€kna upp till 10.
  • Vi anvĂ€nder variabeln count som kontrollvariabel (eng. loop control variable), eller loopvariabel.
    • Till skillnad frĂ„n i for-loopen behöver vi initiera och rĂ€kna upp loopvariabeln manuellt.
In [23]:
count = 0
while count < 10:
    print(count)
    count += 1
    
0
1
2
3
4
5
6
7
8
9
  • Notera att vi anvĂ€nder <= för att fĂ„ med talet 10.
  • Vilket vĂ€rde har count efter att loopen Ă€r klar?

Exempel¶

# oÀndlig loop
x = 0
while x <= 10:
    print("En dator blir aldrig uttrÄkad.")
    print(x)

LÀngsta lÀngden med while¶

In [24]:
lengths = [120, 100, 160, 124, 112, 150, 188, 134]
tallest_found = 0

index = 0
while index < len(lengths):
    if lengths[index] > tallest_found:
        tallest_found = lengths[index]
    index = index + 1

print(tallest_found)
188
index = 0
while index < len(lengths):
    if lengths[index] > max_length:
        max_length = lengths[index]
    index = index + 1

Varför while nÀr for finns?¶

  • För att kunna anvĂ€nda for krĂ€vs att vi har eller kan fördefiniera en sekvens.
  • Exempel: Vi vill slĂ„ en 6-sidig tĂ€rning tills summan Ă€r minst 20.
    • Vi anvĂ€nder modulen random för att slumpa ett tal mellan 1 och 6 som representerar ett tĂ€rningsslag.
    • Variabeln total utgör bĂ„de kontrollvariabel/loopvariabel och ackumulatorvariabel.
In [77]:
import random
total = 0
rolls = []

while total < 20:
    roll = random.randint(1, 6)
    rolls = rolls + [roll]
    total = total + roll
 
print(total, rolls)
22 [1, 1, 6, 6, 5, 3]

while total < 20: roll = random.randint(1, 6) total += roll print("Roll is:", roll, "( total is:", total, ")\n")

Avbryta en loop, eller gÄ till nÀsta iteration i en loop¶

No description has been provided for this image

Avbryta en loop, eller gÄ till nÀsta iteration i en loop¶

  • Nyckelordet break anvĂ€nds för att avbryta en loop (hela loopen).
  • Nyckelordet continue anvĂ€nds för att hoppa över resten av loop-blocket och börja om igen frĂ„n början (nĂ€sta iteration)
In [27]:
for count in range(10):
    if count == 3 or count == 5:
        continue
    if count > 7:
        print("Nu orkar jag inte mer." + "(count: " + str(count) + ")")
        break
    elif count > 3:
        print("Är vi framme snart?" + "(count: " + str(count) + ")")
    elif count > 4:
        print("Har vi kört vilse?" + "(count: " + str(count) + ")")
    else:
        print("Det hÀr Àr roligt!" + "(count: " + str(count) + ")")    
Det hÀr Àr roligt!(count: 0)
Det hÀr Àr roligt!(count: 1)
Det hÀr Àr roligt!(count: 2)
Är vi framme snart?(count: 4)
Är vi framme snart?(count: 6)
Är vi framme snart?(count: 7)
Nu orkar jag inte mer.(count: 8)
  • Variabeln count kallas kontrollvariabel eller loop control variable pĂ„ eng.
  • Notera att vi inte kan skriva if count == 3 or 5

NÀr ska man vÀlja for resp. while?¶

  • for
    • NĂ€r vi har en fördefinierad sekvens (t.ex. en lista eller en strĂ€ng), eller
    • NĂ€r vi kan generera sekvensen (t.ex. med range eller enumerate).
  • while
    • NĂ€r vi inte i förvĂ€g kan definiera en sekvens att loopa över.
    • T.ex. för att antalet iterationer beror pĂ„ nĂ„gon form av slumpning, en mer komplicerad berĂ€kning eller, vĂ€rst av allt, input frĂ„n en mĂ€nsklig anvĂ€ndare đŸ˜±
  • Heurestik att anvĂ€nda tills ni börjar fĂ„ en intuition:
    1. AnvÀnd for om det gÄr.
    2. Annars, fundera pÄ om for + range fungerar.
    3. I sista hand, anvÀnd while.

Varför introducerade vi enkelrekursion nÀr for och while finns?¶

  • KrĂ€vde ingen ny syntax, bara funktionsanrop och villkorssatser som vi redan avhandlat.
  • Inte alla programmeringssprĂ„k har loopar, men nĂ€stan alla tillĂ„ter rekursion.
  • Rekursion Ă€r mest generellt och kan hantera situationer dĂ€r bara en loop inte rĂ€cker, men Ă€r slött och begrĂ€nsas i praktiken av anropsstackens maximala storlek.
    • Dubbelrekursion, som bygger pĂ„ enkelrekursion, Ă€r det mest naturliga sĂ€ttet att bearbeta godtyckligt nĂ€stlade data och trĂ€dstrukturer, som vi kommer till lĂ€ngre fram i kursen.

Finns det fler sÀtt att Ästadkomma upprepning?¶

  • Mest Pythonic: Generatoruttryck och listbyggare (list comprehensions).
    • Överkurs och skall förklaras om ni vĂ€ljer att anvĂ€nda det i en labb.
  • Mer generellt: Funktionerna map, filter, reduce
    • Mer om dessa lĂ€ngre fram i kursen.

Felsökning¶

No description has been provided for this image

Olika typer av fel¶

  • Syntaxfel:
    • Koden bryter mot programsprĂ„kets grammatiska regler.
  • Runtime-fel:
    • Fel som uppstĂ„r vid körning och resulterar i en "krasch".
  • Logiska fel:
    • Programmet kan köra utan att "krascha" men gör inte det man hade tĂ€nkt sig.
  • Det Ă€r inte alltid tydligt var grĂ€nsen mellan de sistnĂ€mnda gĂ„r, dĂ„ mĂ„nga logiska fel kan leda till runtime-fel i vissa men inte alla fall.

Syntaxfel¶

  • Koden bryter mot programsprĂ„kets grammatiska regler. Exempelvis:
In [63]:
2+ # Oavslutat uttryck
  Cell In[63], line 1
    2+
      ^
SyntaxError: invalid syntax
In [65]:
for i of range(3): # AnvÀnt `of` istÀllet för `in`
    print(i)
  Cell In[65], line 1
    for i of range(3):
          ^
SyntaxError: invalid syntax
In [64]:
def fun(args) # Saknas kolon
    do_something()
  Cell In[64], line 1
    def fun(args)
                 ^
SyntaxError: expected ':'
In [66]:
return = 4 # Försöker tilldela ett vÀrde till ett nyckelord
  Cell In[66], line 1
    return = 4
           ^
SyntaxError: invalid syntax
In [70]:
def if(cond, expr, alt): # Försöker skapa en funktion med samma namn som ett nyckelord
    return expr if cond else alt # Denna rad Àr dock giltig python, men fÄr betraktas som överkurs
  Cell In[70], line 1
    def if(cond, expr, alt): # Försöker skapa en funktion med samma namn som ett nyckelord
        ^
SyntaxError: invalid syntax
  • Exempel:
    • AnvĂ€nda reserverade ord pĂ„ fel sĂ€tt, t.ex. att döpa en variabel till return eller en funktion till if.
    • Saknat kolon i slutet av if-sats.

Runtime-fel¶

  • Fel som uppstĂ„r vid körning och resulterar i en "krasch". Exempelvis:
In [37]:
1+'1'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[37], line 1
----> 1 1+'1'

TypeError: unsupported operand type(s) for +: 'int' and 'str'
In [71]:
1/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[71], line 1
----> 1 1/0

ZeroDivisionError: division by zero
In [72]:
"abcd"[4]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[72], line 1
----> 1 "abcd"[4]

IndexError: string index out of range
In [73]:
len(4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[73], line 1
----> 1 len(4)

TypeError: object of type 'int' has no len()
  • Exempel:
    • Division med 0. a = 2

b = 1 a/b - Kombinera en strÀng och ett heltal med +-operatorn.a = '2' b = 1 a + b

Logiska fel¶

  • Programmet kan köra utan att "krascha" men gör inte det man hade tĂ€nkt sig. Exempelvis:
In [74]:
2+3 * 5 # Vi förvÀntade oss resultatet 25, men operatorn * har högre prioritet Àn + oavsett var man placerar mellanslag, vi fÄr anvÀnda parenteser precis som inom matematiken.
Out[74]:
17
In [40]:
'abcd'[1] # Vi ville ha första elementet men glömde att första elementet ligger pÄ index 0, inte index 1.
Out[40]:
'b'
  • Exempel:
    • Felaktig operatorprioritet, t.ex. rĂ„ka skriva 3 + 2 * 5 nĂ€r man menade (3 + 2) * 5.
    • Off-by-one-fel, t.ex. glömma bort att indexering börjar pĂ„ 0, inte 1.
      • a = ['1', '2', '3']
      • a[1]
      • Eller överkompensera och anta att nĂ„got ska vara 0 nĂ€r det borde vara 1.
      • Eller anvĂ€nda strikt olikhet dĂ€r det borde vara <= eller vice versa.

SpÄrutskrifter¶

  • Utskrift av variabelvĂ€rden vid olika tillfĂ€llen i koden
    • T.ex. print(my_var) före och efter ett anrop till en funktion för att kontrollera ifall vĂ€rdet förĂ€ndrats pga anropet.
  • Utskrift av text för att visa var i koden pythontolken befinner sig
    • T.ex. print("Entering my_fun") pĂ„ första raden i funktionen my_fun.

Felsökning av olika typer av fel¶

  • Syntaxfel: kontrollera syntax, antal blanksteg, parenteser, kolon, etc.
    • Felmeddelande: Oftast vĂ€ldigt tydligt exakt var felet Ă€r och vad som var fel (dock inte sjĂ€lvklart för nybörjaren vad som Ă€r rĂ€tt).
    • SpĂ„rutskrifter Ă€r inte till nĂ„gon hjĂ€lp.
  • Runtime-fel: tolka felmeddelandet, lĂ€gg till spĂ„rutskrifter
    • Felmeddelande: Oftast tydligt vad felet Ă€r och var det orsakar problem, dock inte alltid vad som orsakar felet eller var det intrĂ€ffar.
    • SpĂ„rutskrifter Ă€r ibland nödvĂ€ndiga för att hitta felet.
  • Logiska fel: försök att isolera felet, pĂ„ vilka stĂ€llen i koden anvĂ€nds variabeln/funktionen som har med felet att göra? LĂ€gg till spĂ„rutskrifter.
    • Felmeddelande: OsĂ€kert om man ens fĂ„r nĂ„got.
    • SpĂ„rutskrifter Ă€r nĂ€stan alltid nödvĂ€ndiga om man inte direkt inser var man tĂ€nkt fel.

  • NĂ€r det Ă€r svĂ„rt att hitta felet: Det Ă€r alltid bĂ€ttre att vara noggrann och dubbelkolla med spĂ„rutskrifter Ă€n att göra antaganden om att man gjort rĂ€tt, om alla vĂ„ra antaganden alltid vore korrekta hade det ju inte blivit fel frĂ„n första början.

SidospÄr: Begreppet "krasch"¶

  • Vi sĂ€ger ofta lite slarvigt att "Python kraschade" nĂ€r vĂ„r kod inte fungerar och vi fĂ„r ett felmeddelande.
  • Tekniskt sett Ă€r detta inte korrekt. SjĂ€lva Python, dvs. Pythontolken, har inte kraschat utan har upptĂ€ckt ett fel och stoppat exekveringen under kontrollerade former.
    • Eventuellt kan vi sĂ€ga att "vĂ„rt skript kraschade", men inte att "Python kraschade".
  • En verklig "krasch" Ă€r okontrollerad frĂ„n programmets (i det hĂ€r fallet Pythontolkens) sida.
    • Antingen kliver operativsystemet in och stoppar Pythontolken utan vidare konsekvenser för datorn, eller sĂ„ Ă€r felet sĂ„ allvarligt att hela operativsystemet utför ett kontrollerat nödstopp (t.ex. klassiska "Bluescreen of Death" i Windows). I vĂ€rsta fall Ă€r problemet sĂ„ allvarligt att Ă€ven operativsystemet avslutas okontrollerat, om sjĂ€lva datorn har upptĂ€ckt ett sĂ„ allvarligt fel att den omgĂ„ende stoppar allt för att undvika att maskinvaran tar skada.
    • Typiskt för att Python kraschar Ă€r att vi inte fĂ„r nĂ„got felmeddelande frĂ„n Python, i bĂ€sta fall fĂ„r vi ett felmeddelande av operativsystemet.
  • Det Ă€r vĂ€ldigt svĂ„rt att fĂ„ sjĂ€lva Python att krascha.
    • (Jag kan rĂ€kna pĂ„ ena handens fingrar antalet gĂ„nger jag sett operativsystemet behöva nödstoppa Python, jag har aldrig sett hela systemet pĂ„verkas av ett trasigt Pythonskript. //Johan)

Tips för parprogrammering¶


No description has been provided for this image
(https://www.wikihow.com/Pair-Program)

Förare och observatör¶


VÀxla ofta (minst var 15e minut)¶

  • Byt person som sitter vid tangentbordet med jĂ€mna mellanrum
    • Men INTE för att ni kört fast. "NĂ€, men assĂ„ sĂ„hĂ€r! ___KNYCK___"
    • Viktigt att skriva sjĂ€lv, inte bara sitta bredvid. Risken Ă€r att ni inte mĂ€rker fundamentala kunskapshĂ„l förrĂ€n pĂ„ tentan.
    • Är ni ett ojĂ€mnt par dĂ€r den ena har mer erfarenhet sĂ„ bör den med minst erfarenhet spendera mest tid vid tangentbordet.
  • BĂ„da Ă€ger koden.

Hur Àr man en bra förare?¶

  • TĂ€nk högt medan du skriver och förklara varför du skriver som du gör.
  • Lyssna pĂ„ observatören.

Hur Àr man en bra observatör?¶

  • Var aktiv
    • Se till att ni jobbar med det ni ska.
    • Granska koden löpande, och be föraren förklara om du tror att nĂ„got Ă€r fel eller Ă€r osĂ€ker pĂ„ varför föraren skrev en specifik sak.
    • Fundera pĂ„ större designfrĂ„gor Ă€n raden som skrivs just nu (mer aktuellt ju lĂ€ngre fram i kursen vi kommer).
  • Var öppna för input frĂ„n varandra, och kommunicera produktivt
  • Misstag/problem i koden Ă€r egenskaper hos koden, inte personliga egenskaper hos den som skrivit koden

Fira nÀr ni har löst ett problem


celebrate.gif

Program- och dataabstraktion¶

  • Programabstraktion
    • Uppdelning av större program i flera moduler, funktioner och processer som ansvarar för olika saker.
    • Döljer onödig komplexitet bakom anrop till funktioner med beskrivande namn.
    • Vi abstraherar bort detaljerna för hur nĂ„got skall utföras sĂ„ att vi inte behöver hĂ„lla det i huvudet hela tiden.
  • Dataabstraktion
    • Döljer detaljer kring hur data faktiskt lagras bakom ett grĂ€nssnitt, en uppsĂ€ttning funktioner som anvĂ€nds för att arbeta med datan utan att behöva veta detaljerna.

Programmeringsparadigm¶




(https://en.wikipedia.org/wiki/Programming_paradigm)


(LÀstips för den nyfikne: Sebesta, Robert W. Concepts of programming languages (11th Edition). Pearson Education, Inc, 2016 .)

Programmeringsstilar¶

  • Olika sĂ€tt att strukturera ett program, dvs. olika sĂ€tt att skapa abstraktioner och principer att följa kring var abstraktioner skapas och hur de interagerar med varandra.
  • Olika programmeringssprĂ„k har olika sprĂ„kliga konstruktioner med olika semantik (hur konstruktionerna faktiskt fungerar) som gör olika stilar lĂ€ttare eller svĂ„rare att applicera.
  • Stilarna Ă€r ofta mer av tendenser Ă€n strikta definitioner, och vissa stilar rĂ„der det debatt om hur de faktiskt ska definieras.
  • MĂ„nga stilar Ă€r ortogonala och kan anvĂ€ndas samtidigt.
  • NĂ€r en viss stil efterföljs utan undantag, eller med sĂ„ fĂ„ undantag som möjligt, kallar vi det för ett programmeringsparadigm.
  • Historiskt sett var programmeringssprĂ„k mer paradigmatiska, dvs. mer begrĂ€nsade till en viss programmeringsstil. De flesta populĂ€ra och mer moderna sprĂ„k, som Python, Ă€r multiparadigmatiska.

Strukturerad programmering, ett paraplyparadigm¶

  • All programmering i den hĂ€r kursen och nĂ€stan all annan modern programmering gĂ„r under paraplyet strukturerad programmering.
  • Det kan vara enklare att förklara vad som INTE Ă€r strukturerad programmering.

Ostrukturerad programmering (eller go to-programmering)

No description has been provided for this image
  • Program körs rad för rad, uppifrĂ„n och ned, och programmets flöde styrs genom go to-operationer som hoppar till en viss rad eller, senare, en viss label, och fortsĂ€tter dĂ€rifrĂ„n.
  • MĂ„nga moderna sprĂ„k som Python har ingen go to-operation (annat Ă€n som ett aprilskĂ€mt 2004).
  • Assembly-programmering och maskinkod Ă€r fortfarande oftast "ostrukturerad".
    • Y:arna fĂ„r lĂ€ra sig mer om detta i TSEA28 Datorteknik Y. (En av fĂ„ kurskoder som finns kvar Ă€nda sedan min tid. //Johan)

Ostrukturerad programmering i Python (enligt april-skÀmt 2004)

  • Edsger W. Dijkstra. 1968. Letters to the editor: Go To Statement Considered Harmful. Commun. ACM 11, 3 (March 1968), 147–148. https://doi.org/10.1145/362929.362947
    • En lite mer sentida analys av Dijkstras resonemang, med moderniserat (nĂ„ja) sprĂ„kbruk, av David Tribble finns hĂ€r.
  • VĂ€rt att pĂ„peka Ă€r att Dijkstra inte hĂ€vdade att en enskild go to-operation i sig alltid var problematisk (den starka titeln var det tidskriftens redaktör som hittade pĂ„) utan att överanvĂ€ndning av go to-konstruktionen ofta ledde till extremt svĂ„rtolkad kod. Vad som senare kom att kallas "Spaghettikod".
    • Omöjligt att följa programflödet eftersom det bara Ă€r en röra mellan olika rader/labels och goto-operationer, som att försöka följa en enskild spaghetti i en skĂ„l full med spaghetti.
  • IstĂ€llet skulle man anvĂ€nda konstruktioner som funktionsanrop, if, while, osv. för att strukturera koden.
  • Vi har alltsĂ„ hela tiden arbetat inom ramarna för strukturerad programmering.
goto .start;
label .fun;
print("Fun output 1")
print("Fun output 2")
goto .continue;
label .start;
print("Output 1")
print("Output 2")
goto .fun;
label .continue;
print("Output 3")
print("The End")
label .end;

Exemplet Àr inte körbart, eftersom riktig Python inte har goto eller label i verkligheten.

  • Inte körbart eftersom Python tack-och-lov inte har goto i verkligheten

Strukturerad programmering i riktig Python¶

In [ ]:
def fun():
    print("Fun output 1")
    print("Fun output 2")
print("Output 1")
print("Output 2")
fun()
print("Output 3")
print("The End")

Ytterligare tvÄ dimensioner¶

  • Imperativ programmering
    • Beskriver hur datorn skall utföra nĂ„got.
    • Beskriv steg för steg de operationer som tillsammans löser ett problem.
    • Ostrukturerad pĂ„ lĂ„g nivĂ„ och historiskt sett, idag finns strukturerade paradigm som ocksĂ„ Ă€r imperativa.
  • Deklarativ programmering
    • Beskriver vad datorn skall göra.
    • Beskriv en uppsĂ€ttning regler och relationer som programmet ska följa.
    • I princip alltid strukturerad per definition.

Tre mer praktiskt tillÀmpbara kategorier¶

  • Procedurell programmering (imperativ)
    • Procedurer (i Python, funktioner) och moduler. Enkla och förĂ€nderliga data. Programmets tillstĂ„nd, dvs. vad som just nu Ă€r lagrat i minnet, Ă€r centralt. Satser som Ă€ndrar programmets tillstĂ„nd.
    • (AnvĂ€nds ibland synonymt med imperativ programmering men lĂ€mpligare Ă€r kanske att sĂ€ga att procedurell programmering Ă€r en strukturerad form av imperativ programmering.)
  • Funktionell programmering (deklarativ)
    • Rena funktioner, dvs. funktioner utan sidoeffekter eller beroende av delat tillstĂ„nd. OförĂ€nderliga data. Eventuellt nödvĂ€ndigt tillstĂ„nd isoleras och hanteras separat. Uttryck som beskriver vĂ€rden och relationer.
  • Objektorienterad programmering (imperativ, oftast)
    • Objekt och, oftast, klasser. Programmets tillstĂ„nd distribuerat mellan objekt som kapslar in sina egna tillstĂ„nd. Meddelanden skickas mellan objekt för att indirekt manipulera objektens tillstĂ„nd.

Programmeringsparadigm som stöds i Python¶


Objektorienterad, Procedurell och Funktionell programmering (nÄja)
too_mainstream.gif

Python Àr multiparadigmatiskt¶

  • Den programmeringsstil som kallas Pythonic Ă€r influerad av flera stilar.
    • Allting Ă€r objekt som i OOP, men vi mĂ„ste inte programmera objektorienterat.
    • Pythons subrutiner kallas "funktioner" men kan vara
      • Rena procedurer, utan returvĂ€rde, som i procedurell programmering.
      • Rena funktioner, utan sidoeffekter, som i funktionell programmering.
      • NĂ„got mittemellan.
    • Plockar russinen ur mĂ„nga kakor.
  • Vi kan dock skriva kod som Ă€r mer typisk för ett visst paradigm, vi kan anvĂ€nda Python paradigmatiskt.

Bygga upp ett program med hjÀlp av funktioner¶

  • IstĂ€llet för att ha en monsterfunktion som gör allt
    • identifiera mönster
    • deluppgifter som förekommer pĂ„ flera olika stĂ€llen i ett program
  • Skriv en funktion för varje deluppgift
  • Uppdelningen kan vara hierarkisk:
    • Problem $A$ bestĂ„r av delproblemen $B$ och $C$
    • Problem $B$ bestĂ„r av delproblemen $E$, $F$ och $G$
    • Problem $C$ bestĂ„r av delproblemen $H$, $E$ och $I$

VÀlj beskrivande variabelnamn...¶

In [ ]:
i = ""
while i != 'q':
    i = input("Skriv nÄgot: ")
    p = None
    h = False
    for c in i:
        if p and p == c:
            h = True
        p = c
    if h:
        print("Det du skrev har upprepade bokstÀver i sig!")
    else:
        print("Det finns inga upprepade bokstÀver dÀr.")
print("Hej dÄ.")

VÀlj beskrivande variabelnamn...¶

In [ ]:
user_input = ""
while user_input != 'q':
    user_input = input("Skriv nÄgot: ")
    prev_char = None
    has_repeated = False
    for curr_char in user_input:
        if prev_char and prev_char == curr_char:
            has_repeated = True
        prev_char = curr_char
    if has_repeated:
        print("Det du skrev har upprepade bokstÀver i sig!")
    else:
        print("Det finns inga upprepade bokstÀver dÀr.")
print("Hej dÄ.")

Vi kan skapa egna abstraktioner med t.ex. funktioner¶

In [ ]:
def has_repeated_char(s):
    prev_char = None
    for curr_char in s:
        if prev_char and prev_char == curr_char:
            return True
        prev_char = curr_char
    return False

user_input = ""
while user_input != 'q':
    user_input = input("Skriv nÄgot: ")
    if has_repeated_char(user_input):
        print("Det du skrev har upprepade bokstÀver i sig!")
    else:
        print("Det finns inga upprepade bokstÀver dÀr.")
print("Hej dÄ.")

Kommentarer kan göra det lÀttare att förstÄ koden, men bara om de stÀmmer!¶

In [ ]:
def has_repeated_char(s):
    """Returnera True om upprepade tecken finns i s, annars False."""
    prev_char = None
    for curr_char in s:
        # Check whether previous charcter is the same as the current character.
        if prev_char and prev_char == curr_char:
            return True
        prev_char = curr_char
    return False

# Ask user for a word and answer whether it contains repeated characters or not.
user_input = ""
while user_input != 'q':
    user_input = input("Skriv nÄgot: ")
    if has_repeated_char(user_input):
        print("Det du skrev har upprepade bokstÀver i sig!")
    else:
        print("Det finns inga upprepade bokstÀver dÀr.")
print("Hej dÄ.")

Skiss för interaktivt program¶

def ta_emot_kommando():
    satser
    return kommando

def kommando1():
    satser

def kommando2():
    satser

def utför_kommando(kommando):
    satser

def main():
    while True:
        kommando = ta_emot_kommando()
        utför_kommando(kommando)

Skiss för autocomplete-uppgift¶

def ladda_ordlistor():
    satser
def ladda_ordlista(ordlista):
    satser
def autocomplete_strategi1():
    satser
def autocomplete_strategi2():
    satser
def autocomplete(strategi, ord, ordlistor)
    satser
def vÀlj_strategi():
    satser
def main():
    ordlistor = ladda_ordlistor()
    strategi = vÀlj_strategi()
    while True:
        ord = input("Skriv ett ord: ")
        if ord != "q":
            kompletterat_ord = autocomplete(strategi, ord, ordlistor)
            print(kompletterat_ord)

LÀsa frÄn textfil¶

LÀsa data frÄn textfil¶

  • LĂ€sning sker sekventiellt
  • Olika strategier
    • hela filen som en textstrĂ€ng
    • en rad i taget, en rad → en strĂ€ng
    • hela filen som en lista, varje rad blir ett element i en lista

Förberedelser innan lÀsning¶

  • Filen mĂ„ste öppnas för lĂ€sning
  • Liknelse: undersöka innehĂ„ll i en lĂ„da frĂ„n en lagerlokal
    • ange vilken lĂ„da - ange sökvĂ€g till filen
    • öppna lĂ„da för att plocka ut saker - öppna filen i "lĂ€slĂ€ge"
    • plocka fram innehĂ„ll - lĂ€s frĂ„n fil

LÀsa in information frÄn en fil¶

  • Funktionen open tar in en strĂ€ng (namn pĂ„ fil), och öppnar filen och returnerar den öppna filen (speciell datatyp)
file = open(filnamn)
file = open(filnamn, 'r') # read - endast lÀsning
file = open(filnamn, 'w') # write - skriva över
file = open(filnamn, 'a') # append - lÀgga till

LÀsa in information frÄn en fil¶

  • Metoden .readlines lĂ€ser innehĂ„llet i filen och returnerar det som en lista av strĂ€ngar.
    • (metod: funktion som sitter ihop med ett vĂ€rde)
file = open("hemligt.txt")
contents = file.readlines()

StÀnga fil¶

  • Vi stĂ€nger en fil nĂ€r vi Ă€r klara med den (praxis, för att undvika diverse problem)
file = open("data.csv")
contents = file.readlines()
file.close()

Funktionen repr¶

  • repr returnerar strĂ€ng inklusive citattecken för att indikera att det Ă€r en strĂ€ng
In [ ]:
print(True)
print("True")
print(repr(True))
print(repr("True"))