Flöde och State
Att programmera är att ge instruktioner till en dator, eller snarare datorns processor.
En processor kan bara göra en sak åt gången, och instruktionerna utförs därför en efter en, i ett flöde.
Under flödets gång kommer variabler att skapas och deras värden uppdateras.
State är värdet på alla variabler i ett program, vid ett givet tillfälle.
Programmets state ändras alltså allteftersom programmet körs.
Att programmera handlar därför om att skapa ett flöde som bearbetar state och ser till att programmet har rätt state när det är klart.
Man kan alltså se flödet som en serie av transformationer av state.
För att konstruera programmets flöde och för att transformera state finns ett begränsat antal strategier som används av alla programmerare oavsett vilka program de skriver eller språk de använder.
Genom att kombinera dessa strategier på olika sätt, kan man skapa alla program.
Strategierna är oberoende av kod, men kan så klart implementeras i kod i valfritt programmeringsspråk.
Den här boken går igenom de strategier man behöver för att klara Programmering 1 (och antagligen bra mycket mer än så).
Flödesschema
För att illustrera strategier används flödesscheman.
Flödesscheman fungerar såhär …
Mål och Strategier
Här behövs text om hur programmerare sätter upp mål- och delmål och hur de använder strategier för att uppnå dessa mål- och delmål. Gärna med praktiska exempel.
Strategier
Ändring av state (transformation)
Tilldelning
Är tilldelning en strategi eller räknas det som programming "knowledge"?
För att kunna använda värden (t.ex tal) i program måste man först lagra dem i en variabel. Att lagra ett värde i en variabel gör man med hjälp av tilldelning.
En tilldelning består av tre komponenter: en variabel, ett tilldelningstecken och ett värde.
-
Ruby
-
Python
-
Javascript
-
C#
speed = 10
speed = 10
const speed = 10;
var speed = 10;
Initialisering
Initialisering används när man vet att man kommer behöva ett värde som man kan modifiera längre fram i flödet.
Exempel: Summera tal
När man vill räkna ut summan alla värden i en lista behöver man ha en variabel som innehåller summan. För varje värde i listan vill man sen addera värdet till summa-variabeln. Men för att man ska kunna addera det första värdet i listan till summa-variabeln måste summa-variabeln först vara initialiserad med ett värde, antagligen 0. |
Exempel: Bilspel
I ett bilspel vill man kunna gasa och bromsa. Både gas- och bromskoden kommer modifiera hastigheten på bilen, men för att hastigheten ska kunna modifieras måste det först skapas en variabel som innehåller hastigheten, och variabeln måste ha initialiserats med ett värde som kan modifieras. |
-
Ruby
-
Python
-
Javascript
-
C#
speed = 0 (1)
...
if pressed_button == 'w'
speed += 1 (2)
elsif pressed_button == 's'
speed -= 1 (2)
elsif pressed_button == 'esc'
speed = 0 (2)
end
1 | Initialisering av speed-variabeln |
2 | Modifiering av speed-variabeln |
speed = 0
const speed = 0;
var speed = 10;
Inkrementering
Inkrementering innebär att öka värdet på en variabel. Oftast är värdet ett heltal. Inkrementering används ofta inne i loop-strategierna för att hålla koll på räknar-variabeln, men det går även att använda tillsammans med de olika if-strategierna.
För att inkrementera värdet på en variabel måste variabeln först vara initialiserad
-
Ruby
-
Python
-
Javascript
-
C#
healthiness_score = 0 (1)
...
if favourite_drink == 'water'
healthiness_score += 1 (2)
end
if favourite_food == 'fruit'
healthiness_score += 1 (2)
elsif favourite_food == 'carrot'
healthiness_score += 2 (2)
elsif favourite_food == 'pizza'
healthiness_score -= 2 (3)
end
1 | Initialisering av healthiness-variabeln |
2 | Inkrementering av healthiness-variabeln |
3 | Dekrementering av healthiness-variabeln |
speed = 0
const speed = 0;
var speed = 10;
-
Ruby
-
Python
-
Javascript
-
C#
number_of_greeted_people = 0 (1)
...
while number_of_greeted_people < number_of_people
puts "Hello, nice to meet you!"
number_of_greeted_people += 1 (2)
end
1 | Initialisering av number_of_greeted_people-variabeln |
2 | Inkrementering av number_of_greeted_people-variabeln |
speed = 0
const speed = 0;
var speed = 10;
Dekrementering
Dekrementering innebär att minska värdet på en variabel. Oftast är värdet ett heltal. Dekrementering används ofta inne i loopar för att hålla koll på räknar-variabeln, men det går även att använda tillsammans med if-strategierna.
För att dekrementera värdet på en variabel måste variabeln först vara initialiserad
-
Ruby
-
Python
-
Javascript
-
C#
healthiness_score = 0 (1)
...
if favourite_drink == 'water'
healthiness_score += 1 (2)
end
if favourite_food == 'fruit'
healthiness_score += 1 (2)
elsif favourite_food == 'carrot'
healthiness_score += 2 (2)
elsif favourite_food == 'pizza'
healthiness_score -= 2 (3)
end
1 | Initialisering av healthiness-variabeln |
2 | Inkrementering av healthiness-variabeln |
3 | Dekrementering av healthiness-variabeln |
speed = 0
const speed = 0;
var speed = 10;
-
Ruby
-
Python
-
Javascript
-
C#
number_of_laps_left = 5 (1)
...
while number_of_laps_left > 0
puts "Running a lap!"
number_of_laps_left -= 1 (2)
end
1 | Initialisering av number_of_laps_left-variabeln |
2 | Dekrementering av number_of_laps_left-variabeln |
speed = 0
const speed = 0;
var speed = 10;
Mellanlagring
Mellanlagring används när man vill återanvända värdet från en uträkning eller ett funktionsanrop senare i flödet. Exempelvis för att funktionsanropet kräver mycket resurser, tar lång tid att genomföra, eller kräver input från en användare, eller för att det bara blir tydligare kod.
Exempel: Lagra användares initialer
Istället för att varje gång man vill använda en användares initialer extrahera dessa från variabeln som innehåller användarens namn, kan man extrahera dem en gång och mellanlagra resultatet i en variabel. |
Exempel: Hur många gånger förekommer ordet i boken?
Ett program ska jämföra vilket av två ord som förekommer flest antal gånger i en bok. Det tar relativt lång tid att söka igenom boken och räkna antalet förekomster av ordet. Därför kan man räkna anatalet förekomster en gång, och lagra resultatet i en variabel, som man sedan använder varje gång man vill använda värdet. |
-
Ruby
-
Python
-
Javascript
-
C#
initials = "#{first_name[0].capitalize}.#{last_name[0].capitalize}." (1)
...
puts "Your initials are: #{initials}" (2)
1 | Mellanlagring av värdet. |
2 | Användning av värdet. |
Python
Javascript
C#
Konkatenering
Är det här en strategi?
Konfigurationslagring
Konfigurationslagring används när man vill använda ett värde längre fram i flödet, men man vill inte skriva ut värdet direkt, utan vill kunna använda en variabel med ett beskrivande namn istället för att skriva värdet direkt.
Exempel: Priser med och utan moms
Ett program som ska visa priser med och utan moms. Istället för att skriva pris * 1.25 kan man skriva pris * moms, förutsatt att man i moms-variabeln lagrat värdet 1.25. |
Exempel: Kastbanor med gravitationskonstant
I ett program som ska räkna ut kastbanor kan man lagra gravitationskonstanten (9.807) i variabeln |
Exempel: Spara återkommande textfraser
I Bröderna Lejonhjärta-spelet ska alla vakter avsluta sina meningar med frasen "All makt åt Tengil vår befriare!". Istället för att skriva frasen på alla ställen kan man lagra frasen i en variabel och använda variabeln vid varje tillfälle. |
Konfigurationslagring gör koden tydligare att förstå för oss människor (datorn bryr sig inte), och gör det enklare att ändra - om man behöver ändra det man lagrat behöver man bara ändra på ett enda ställe.
-
Ruby
-
Python
-
Javascript
-
C#
vat = 1.25 (1)
...
puts "#{price} exkl moms (#{price * vat} inkl. moms)" (2)
1 | Konfigurationslagring av momssatsen (vat står för Value Added Tax, (moms på engelska)), |
2 | Användning av momssatsen. |
gravitation = 9.807
const gravitation = 9.807;
var gravitation = 9.807;
Type inference räknar ut vilken datatyp som lagras i variabeln. Även om det inte står att variabeln innehåller en specifik datatyp kommer variabeln fortfarande vara "låst" till den datatyp som initialt lagrades i variabeln (i det här fallet en int ).
|
Swap
Swap används när du behöver byta plats på två värden i ett program.
Exempel: Byta plats på värden i en array
Som en del av en |
Exempel: Byta plats på tecken i en sträng.
Om man vill skapa anagram kan man byta plats på tecken i sen sträng, t.ex kan man ändra |
Exempel: Byta plats på namn.
I vissa delar av världen presenterar man sig med förnamn, och sen efternamn, i andra delar av världen med efternamn, och sen förnamn. Om man har två variabler för namn, kan man därför vilja byta plats på vad som lagras i variablerna. |
För att genomföra swap-strategin behöver du först ha referenser till de två värden du vill byta plats på. Referenserna kan vara variabler som innehåller själva värdena eller ett index i en lista (där värdet finns).
Svårigheten uppstår när man försöker byta plats. Tänk följande pseudokod
Problemet med att byta plats
|
Man kan göra en jämförelse till copy/paste på en dator. Om du kopierar något, och sen klistrar in det på ett annat ställe, skriver du över det som fanns på det andra stället tidigare.
För att kunna spara värdet på a
behöver vi använda mellanlagring för att temporärt lagra värdet på a
, innan vi skriver över a
med värdet på b
.
Swap med mellanlagring
|
Uppdelning av flöde (selektion)
If
Om du vill att ditt program ska göra en sak enbart om ett villkor är sant, kan du använda if.
If-strategin använder ett villkor (ett påstående som kan vara sant eller falskt), och om villkoret utvärderas till sant kommer något ske. Om villkoret inte utvärderas till sant kommer programflödet fortsätta efter villkoret, som om ingenting hänt.
Exempel: Pris med eller utan moms.
På en hemsida som framförallt säljer till företag visas priserna utan moms. Men om någon klickat i att de vill se priserna med moms, ska priserna modifieras med momsen |
-
Ruby
-
Python
-
Javascript
-
C#
if show_price_with_vat?
price = price * vat
end
if show_price_with_vat:
price = price * vat
end
null
Null
Exempel: Ett till exempel
Exempeltext |
If-else
If-elsif
If-elsif-else
Lorem
-
Ruby
-
Python
-
Javascript
-
C#
Ruby
Python
Javascript
C#
Ipsum
-
Ruby
-
Python
-
Javascript
-
C#
Ruby
Python
Javascript
C#
Upprepning av flöde (iteration)
Räknande Loop
En räknande loop används när …
Inkrementerande Loop
En inkrementerande loop används när …
-
Ruby
-
Python
-
Javascript
-
C#
Ruby
Python
Javascript
C#
Dekrementerande Loop
En inkrementerande loop används när …
-
Ruby
-
Python
-
Javascript
-
C#
Ruby
Python
Javascript
C#
Loop Med Brytvärde
Behöver utvecklas (alla loopar är väl egentligen loopar med brytvärde, men själva intentionen är väl det som skiljer).
Avbrott av flöde
Är detta en grupp av strategier?
Tidigt avbrott
Är detta en strategi? (och ska den i så fall ligga här eller under upprepning av flöde?)
Sammansatta strategier
Iterativt uppbyggd output
Iterativt uppbyggd output används när man har en mängd data (ofta en lista) att behandla, och output är beroende av datan.
I iterativt uppbyggd output initialiserar man först output-variabeln med ett lämpligt värde som vi sedan kan modifiera.
Sedan itererar man över datan man behöver behandla och modifierar i iterationen output-variabeln (t.ex genom att addera värden till den)
Slutligen, när iterationen är klar kommer output-variabeln innehålla rätt värde, och man kan returnera den.
Exempel: Skapa en lista av alla ord som börjar med en given bokstav i en text
Först initialiserar vi output-variabeln med lämpligt värde, i det här fallet antagligen en tom lista (som kan innehålla alla ord vi hittar). Sen itererar vi (med lämplig strategi för uppreping av flödet) över orden och i varje varv i iterationen använder vi if-strategin för att modifiera output-variabel-listan om om ordet börjar med den givna bokstaven. Slutligen, när iterationen är klar kommer output-variabel-listan innehålla alla ord som efterfrågades, och vi är klara |
Lämpliga värden att initialisera output-variabeln med är ofta:
-
0 (om output ska vara ett tal)
-
en tom sträng (om output ska vara en sträng)
-
en tom lista (om output ska vara en lista)
Men det finns undantag, t.ex kan man ibland initialisera output-variabeln till ett av värdena som finns i mängden man ska behandla.
Switch Loop
Är detta en strategi?