tisdag 2 oktober 2012

Backslash på Mac i IDLE

Se instruktion av Malin Christersson:

Hur man skriver backslash "\" i IDLE med ett icke-amerikanskt Mac-tangentbord

Den vanliga knapp-kombinationen Alt+Shift/ resulterar troligen inte i någonting om man skriver den i IDLE på ett icke-amerikanskt Mac-tangentbord. För att få backslash att fungera måste man göra följande:
Välj IDLE->Preferences... och markera Keys-fliken. Gå ner till "expand-word"-raden och klicka på "Get New Keys for Selection"-knappen.
image
Välj några nya tangenttryckningar för "expand-word" och klicka Ok.
image
Skriv in ett namn för en ny "Custom Key Set".
image

Strängar

Variabeltypen string används för att lagra och manipulera text och textliknande teckenföljder.

En sträng anges omgiven av "-tecken eller '-tecken:
a="Hej!"
b='Hej!'

Speciella tecken

Tecknet \ ("backslash") är en så kallad escape character som används för att skriva tecken som annars skulle vålla problem då de krockar med hur koden normalt läses av programmet ex:
\"     ett "-tecken inuti en sträng omgiven av "-tecken
\'      ett '-tecken inuti en sträng omgiven av '-tecken
\n    ny rad
\t     tabulering

Hur skrivs då \  i en sträng? Jo som \\

Övning 1.23
Använd det interaktiva skalet och testa följande:
a) print("C:\\kalle\\Python")
b) print("Lars kallas \"Lasse\"")
c) print("Kalle\t200\nLisa\t1200")

Långa rader i koden

Det är  rekommenderat att ha radlängder på max 79 tecken i koden för att förenkla för den som ska läsa och klara alla editorer. Därför kan det finnas behov av att dela upp en sats eller en sträng i flera rader.
  • För uttryck som inte är strängar och som omges av (), [] eller {} görs radbyte genom ENTER
  • För att byta rad i strängar eller uttryck utan omgivande parenteser används \
Övning 1.24
Testa exemplet:

lang_text="Detta är en låg text så jag byter \
rad i koden trots att det är en rad utskriven."
print(lang_text)
print()
print( "Och nu blev den ännu längre: " +
       lang_text + " Så den inte får plats på en rad")

Manipulera strängar

Strängar (eng. strings) kan ses som en rad tecken efter varandra. I Python är tecknen numrerade från noll och uppåt till sista tecknet. Positionen för ett tecken kallas index. Sista tecknet i en sträng har index som är 1 mindre än strängens längd.

Funktionen len(s) ger längden på en sträng.

 För att komma åt delar av en sträng används:
Uttryck Beskrivning
s[i] Returnerar tecknet med index i
s[i:j] Returnerar strängen från och med i till (men ej lika med!) j
s[i:] Returnerar strängen från och med i
s[:i] Returnerar strängen till i
s[-j] Motsvarar s[len(s)-j]. Alltså tecknet j från slutet
s[-j:] Returnerar resten av strängen från och med tecknet j från slutet
s[:-j] Returnerar strängen fram till tecknet j från slutet


Obs! Att använda negativa index som i de tre sista varianterna är en indexering som många språk saknar. Det finns tex. varken i C, C++ eller Java.

Övning 1.25
Skriv och testa nedanstående program.

text="ABCDEFGHIJK"
print(text+"\tLängd="+str(len(text)))
print(text[2])
print(text[len(text)-1])
print(text[2:6])
print(text[2:])
print(text[:6])
print(text[-2])
print(text[-2:])
print(text[:-6])

Concatenering, att lägga ihop strängar

Att lägga strängar efter varandra till en ny längre sträng kallas concatenring (eng. concatenate) och görs med + så som du sett. Det går också att skapa en upprepad sträng med *-tecken.

Övning 1.26
Använd interaktiva skalet och testa följande:
a) print("husvagn"[:3]+"lastbåt"[-3:])
b) print("Ha"*5)

Du kan inte ändra i en sträng

Det går inte att ändra i en sträng, lägga till eller ta bort tecken efter att den skapats. Utan du måste skapa en ny sträng för detta  och tilldela den till den ursprungliga. Ex:

Lägga till sträng t först till s
s=t+s


Lägga till sträng t sist till s
s=s+t

Lägga till t vid index j
s=s[:j]+t+ s[j:]

Ta bort en del av s från och med index i till j
s=s[:i]+s[j:]

Uppgift 8
Skriv ett program som testar ovanstående strängmanipulationer. Välj själv lämpliga strängar för t och s.

Jämförelse av strängar och några bra strängmetoder

Att jämföra strängar i Python påminner om jämförelser mellan tal. Här följer de mest användbara jämförelserna:
Uttryck Beskrivning
s == t Sant om s är exakt lika som t
s != t Sant om något skiljer mellan s och t
s in t Sant om strängen s återfinns någonstans i t
s not in t Sant om strängen s inte återfinns någonstans i t


Notera: Det är skillnad på stora och små bokstäver när jämförelser sker.

Strängar är lite av både fågel och fisk i Python, och flera andra språk. De fungerar som vanliga variabler men de är också objekt. (Vi kommer att tala mycket mer om objekt senare i kursen.)

Så som objekt har strängar metoder som kan användas för att utföra olika åtgärder. För att använda en metod skrivs strängen eller strängvariabeln följt av . och sedan metoden. Ex:
"Hej".lower()
s.lower()
Den övre raden ändrar strängen "Hej" till "hej" den nedre gör motsvarande för innehållet i strängen s.

Följande strängmetoder är ofta användbara:
metod Beskrivning
lower() Returnerar en sträng där alla bokstäver ändrats till gemener
upper() Returnerar en sträng där alla bokstäver ändrats till versaler
find(t)     Returnerar första index då strängen t återfinns. Om ingen hittas returneras -1.
count(t)     Returnera hur många gånger strängen t återfinns.
replace(s, t) Returnerar en sträng där alla förekomster av av strängen s bytts ut mot strängen t
strip() Returnerar en sträng där eventuella mellanslag först eller sist är borttagna
zfill(x); Returnerar en sträng där nollor fyller ut  till vänster om texten tills längden är x. Ex. "23".zfill(4) ger "0023"
format(a, ka) Strängformatering. a och ka är argument som används vid formateringen.

split(t)
Skapar en lista av strängar från den ursprungliga strängen. Uppdelade där strängen t förkommer. Mer om detta senare i kursen.


Överkurs: Detta är endast ett litet urval metoder och varianter på metoder. Läs mer här.

Övning 1.27
Testa följande program

s="IKEA"
t=s.lower()
print("s är: "+ s+"\nt är: "+t)

Notera att s behåller sitt värde "IKEA" .
Ändra programmet så att det byter "IKEA" i s till "ikea" istället för att skapa den nya variabeln t.


Övning 1.28
Ett program som summerar inskrivna tal och avslutas när man skriver n eller N. Skriv och testa.

sum=0

while True :
        try: 
            s=input("Skriv in ett tal. (n för att sluta) ")
            if s == "n" or s == "N": break
            sum=sum+float(s)
        except ValueError:
            print("Felaktig inmatning. Försök igen") 

print("Summan blir:" + str(sum))

Övning 1.29
Skriv ett program som returnerar första ordet ur en inskriven text.

Övning 1.30
Skriv ett program som skriver ut en inmatad sträng enligt nedanstående exempel:
Inmatad text: Hej alla glada
Utskrift:
H
He
Hej
Hej 
Hej a
... osv ...
Hej alla glad
Hej alla glada

Notera: Kontrollera så att programmet klarar strängar med ett tecken och att du inte får tomrad först i utskriften.

Övning 1.31
Skriv ett program som byter ut alla förekomster av hon och han till hen i en inmatad text.

Uppgift 1.8
Skriv ett program som skriver ut varje ord i en inmatad sträng på en egen rad.

Strängformatering

Det finns en mängd möjliga formateringar med hjälp av format-metoden. Vi ska titta på några. Syntaxen för strängformatering visas i följande exempel:

"Här är ett tal {0:0.2f} och här är ett tal {1:0.5e}".format(3454564, 3555564)

Resultatet blir (vilket du kan kontrollera med interaktiva skalet):

Här är ett tal 3454564.00 och här är ett tal 3.55556e+06

Klamrarna {} är platshållare som fylls i med innehållet från parentesen efter format.
Texten i klamrarna är en kod som talar om hur formateringen ska ske. Se nedanstående bild som visar vad de olika delarna betyder:

Notera: koden ovan är inte fullständig. Det finns ytterligare positioner som kan användas. Den täcker dock det viktigaste.
  • Index i formatlistan: Anger vilken position i parentesen, räknat från noll, som innehållet ska hämtas ur. Kan utelämnas. Om det utelämnas hämtas innehållet i ordningen de står i format-parentesen så att första klamern fylls med första innehållet, andra med andra osv.
  • Minimal längd: Anger det minsta antal tecken som innehållet ska ta upp. Om det blir tecken över fylls de med mellanslag. Kan utelämnas.
  • Precision: Anger antal decimaler eller värdesiffror för tal. Kan utelämnas.
  • Formateringstyp: Ytterligare information om formateringen för tal. Se nedanstående tabell för exempel.
Tecken Beskrivning
b För heltal: Binär form
d Heltal: Basen 10
f Flyttal: Bestämt antal decimaler och utan tiopotens
g Flyttal: Automatiskt byte till grundpotes för stora  eller små tal. När g används anger siffran bakom punkten antalet värdesiffror.
e Flyttal: Grundpotensform
x För heltal: Hexadecimal form


Övning 1.32
Använd interaktiva skalet och gör följande:
a) Skriv och testa: "{:6.2f}".format(3.1415)
b) Skriv och testa: "{:10}{:6d}".format("Invånare", 123)
c) Ta reda på vad 2000 blir hexadecimalt respektive binärt.
d) Skriv och testa: "{1}={0:0.3g}".format(12.234, "a")

Uppgift 1.9
Skriv ut ett "kvitto" enligt nedanstående. Decimalpunkterna ska hamna under varandra.

Godis      21.40
Kött      121.30
 ===============
Summa     142.70   

onsdag 26 september 2012

Mer om logiska uttryck och elif-satsen

Vi ska nu titta på mer komplicerade logiska uttryck.

Minns att vi har en logisk datatyp som kallas bool som kan ha två olika värden. Sant, True, och falskt, False.
En jämförelse som x>5 resulterar i ett svar av typen bool; True om x är större än 5 och False annars.

And, or och not

För att kombinera olika jämförelser används de logiska operatorerna: and (och), or (eller) och not (icke).
Om A och B är bool fungerar de som i tabellerna nedan:

A B A and B
False False False
False True False
True False False
True True True
Både A och B måste vara sanna för att A and B skall bli sant


A B A or B
False False False
False True True
True False True
True True True
Minst en av A eller B måste vara sann för att A or B skall bli sant

A not A
False True
True False

Övning 1.18
Använd det interaktiva skalet för att pröva de logiska operatorerna. Pröva med följande:
a) True and False
b) True or False
c) not False
d) 3 > 2 or 2 > 3

Prioriteringsregler

Liksom i vanliga matematiken, där tex.  * görs före +, finns det prioriteringsregler för logiska operatorer. Se följande tabell som går från högsta till lägsta av de vi kommit i kontakt med:
Prioritet Operatorer
1 ()
2 vanlig matematik
enligt dess regler
3 jämförelser: >, <,
 ==, !=, >=, <=
4 not
5 and
6 or

I övning 1.18d görs alltså jämförelserna 3>2 som blir True och 2>3 som blir False först. Sedan True or False som blir True.

Notera:
or görs efter and
not görs före både and och or

Övning 1.19
Använd det interaktiva skalet för att pröva de logiska operatorerna. Pröva med följande:
a) not True and False
b) not (True and False)

Varför blir det skillnad mellan resultaten?

Övning 1.20
Skriv och testa följande program:

while True :
    try : 
        a=int(input("Skriv ett tal "))
        if a < 100 and a > 50 :
            print("Du skrev ett tal mellan 50 och 100")
    except ValueError:
        print("Tack för mig")
        break


Uppgift 1.7
 Ändra det logiska uttrycket i if-satsen i övning 1.20 så den talar om om man skriver tal som antingen är större än 100 eller mindre än 50.

elif-satsen

En if-sats kan följas av en eller flera elif-satser, förkortning för "else if". Detta är användbart i många sammanhang.

Övning 1.21
Skriv och testa:

while True :
    try : 
        a=int(input("Skriv ett tal "))
        if a > 100 : print("Du skrev ett tal större än 100")
        elif a > 50 :
            print("Du skrev ett tal mellan 50 och 100 (inkl.)")
        else : print("Du skrev ett tal mindre än 50")
    except ValueError:
        print("Tack för mig")
        break
 
Funktion:
  • Först utförs testet i if-satsen. Om det är sant görs följande print-sats sedan hoppar programmet över följande elif och else. Annars går programmet vidare till elif-satsen.
  • Om if-satsen svarade falskt utförs testet i elif-satsen. Om det är sant görs följande print-sats sedan hoppar programmet över följande else. Annars går programmet vidare till else-satsen.
  • Om inte if- eller elif-satsen var sann görs satserna efter else.
Övning 1.22
Skriv ett program som talar om om ett inmatat heltal är jämnt delbart med 2, 3 eller 5. Tips: Se övning 1.10.

onsdag 12 september 2012

Månlanda


Månlanda är ett enkelt och klassisk spel inspirerat av Apollo-programmet. Spelaren ska landa en månlandare på månen. När spelet startar är landaren på väg ner och spelaren kan reglera bromsraketernas gaspådrag en gång per sekund för att landa mjukt. Dock har landaren begränsad mängd bränsle så det gäller att inte slösa.

Spelfysiken

Spelet bygger på Newtons fysik och rörelseformlerna som du mött i fysikkurserna.

Spelets simulering tar tidssteg på en sekund och acceleration, hastighet och höjd beräknas för varje tidssteg.

Månens gravitation är inte lika stark som på jorden utan tyngdaccelerationen är ungefär en sjättedel av den på jorden. I spelet kan det vara bekvämt att sätta tyngdaccelerationen lika med 1,5 m/s2.

Bromsraketerna motverkar tyngdaccelerationen. Accelerationen under ett tidssteg kan skrivas som:

a =  fK - G                                     (Ekv. 1)

Där f är bränsleflödet i liter per sekund, K en konstant och G månens tyngdacceleration. Konstanten K är ett mått på kraften från motorn, vid pådraget 1 l/s, i förhållande till landarens massa. Notera att positiv acceleration är uppåt då höjden ökar åt det hållet.

Hastigheten ändras alltså med a under ett tidssteg:

vt = vt-1 + a                                 (Ekv. 2)

Där vt är den nya hastigheten och vt-1 är hastigheten förra tidssteget.

Höjden kommer att ändras med medelhastigheten under tidssteget:

ht = ht-1 + (vt + vt-1) / 2            (Ekv. 3)

vilket kombinerat med Ekv. 2 blir

ht = ht-1 + vt - a/2                     (Ekv. 4)


För varje nytt tidssteg beräknas alltså:

a =  fK - G

vt = vt-1 + a 

ht = ht-1 + vt - a/2


Gaspådrag och bränsle

Varje nytt tidssteg börjar med att spelaren anger det valda gaspådraget f. Gaspådraget har ett maximalt värde, full gas, som spelaren inte kan överskrida. Naturligtvis kan spelaren inte ange negativt gaspådrag eller använda mer än det kvarvarande bränslet.

Vid varje tidssteg minskas kvarvarande bränsle med pådraget f:

bt = bt-1 - f

Där bt är kvarvarande bränsle.

Landningen

När höjden är noll, eller mindre, sker landningen...eller kraschen. Om farten då är mindre än 2 m/s, alltså vt > - 2 , så anses landningen lyckad annars är det en kraschlandning.

Flödesschema

Nedanstående visar ett förslag på flödesschema för programmet:

Lämpliga startvärden

Följande värden är lämpliga för att få ett fungerande spel:
Tyngdacceleration G=1,5
Full gas MAX=60
Konstant K=0,1
Bränslemängd b=600
Starthöjd h=500
Starthastighet v=-10

Inlämningsuppgift 1.3

Skapa spelet Månlanda och skicka in. Tänk på att hantera eventuella undantag.

Tips: När du fått spelet att fungera så kan du testa olika värden på bränslemängd eller andra variabler för att göra spelet lagom svårt.

Tips: Som standard skrivs alla decimaler ut för höjd och hastighet och bränsle. Detta kan leda till svårlästa rader. Det går att få en snyggare formatering med tex två decimaler genom att använda formaterings-funktionen för texter:

print("Höjd= {0:.2f}".format(h))

Om h=2.248432 ger ovanstående texten: Höjd= 2.25


Överkurs: Läs mer om textformatering här och se om du ytterligare kan snygga upp utskrifterna.

tisdag 11 september 2012

Repetitionsuppgifter 1.1

Dessa uppgifter repeterar tidigare kunskaper. Göres i mån av tid på lektionen eller som repetition hemma.

Rep 1.1
Skriv ett program som simulerar kast med två vanliga tärningar och visar ögonsumman.

Rep 1.2
Skriv ett program som beräknar medelvärdet av inmatade positiva tal. Efter varje nytt tal användaren skriver in ska medelvärdet av de inskrivna talen skrivas ut. Avslutas genom att användaren skriver in -1.

Rep 1.3
Skriv ett program som i repetitionsuppgift 1.2 men som efter avslutat inskrivande skriver ut det största av de inmatade talen.

söndag 9 september 2012

Fel och undantag

Det finns tre grundtyper av fel i programmeringssammanhang:
  • Syntax-fel: Fel i källkoden pga stavfel eller felaktig satsbyggnad
  • Buggar: Fel i källkoden så att programmet inte uppför sig som förväntat
  • Undantag (eng. Exceptions) Fel av typen division med noll, inmatning av text när en siffra förväntas mm.
Syntaxfelen brukar upptäckas av skalet du använder för att skriva ditt program. Buggar är upp till dig som programmerare att leta efter och rätta. För små program är det ofta enkelt att finna dem men för större kan det vara mycket svårt.

Undantag kan även beskrivas som förväntade problem. Sådant som du när du programmerat räknar med kan hända. Till exempel att någon råkar trycka ENTER istället för att skriva in ett tal. Det är att hantera undantag vi ska titta på här.

try-satsen

Satsen try används för att hantera förväntade problem. De består av två delar try och except.  Se nedanstående exempel:

Övning 1.15
Skriv följande program som hanterar division med noll och testa funktionen

a=float(input("nämnaren="))
try :
    print(100/a)
except ZeroDivisionError :
    print("Division med noll")
print("Tack för mig")

Hur det fungerar:
  • Först utförs de indragna satserna mellan try och except.
  • Om inget undantag inträffar hoppas de indragna satserna efter except över och programmet går vidare vid print("Tack för mig") .
  • Om ett undantag inträffar vid någon av satserna mellan try och except så hoppar programmet direkt till except. Där kontrolleras om undantaget är av den klass (typ) som beskrivs av nyckelordet ZeroDivisionError. Om så är fallet utförs de indragna satserna efter except. Texten "Division med noll" skrivs ut. Sedan fortsätter programmet till sitt slut.
  • Om ett undantag inträffar av en annan klass än ZeroDivisionError kommer programmet att hantera detta på standardsätt. Alltså programmet avslutas med ett felmeddelande.


Övning 1.16
Kör programmet i övning 1.15 och skriv in en bokstav istället för ett tal.

Notera att programmet nu avslutas med ett felmeddelande och sista raden inte körs. Du har nu skapat ett icke hanterat undantag (eng. unhandled exception).

Några undantagsklasser

Det finns en stor mängd färdigdefinierade undantag i Python. De är också ordnade i en hierarki med vissa undantag som är generella och täcker in flera underklasser.

Klass Beskrivning
Exception Huvudklassen för alla undantag. Används med försiktighet för att fånga upp undantag. Använd i första hand mer specifika klasser i stället.
IOError Undantag vid tex. läsning av och skrivning till filer.
IndexError Undantag vid hantering av listor och dylikt. Mer om detta senare i kursen
TypeError Undantag då typen är fel för operationen/fuktionen.  Tex: print("a"+2).
ValueError Undantag vid ogiltigt värde. Tex. math.sqrt(-2)
Även försök att omvandla en sträng bokstäver till tal med float() och int(). Se nedan övning 17
ZeroDivisionError Undantag vid division med noll.

Det går att ha flera olika klasser av undantag listade efter except. Man sätter de då inom parentes och med komma mellan dem. Till exempel kan det ibland vara bra att hantera matematiska fel med:

except (ValueError, ZeroDivisionError) :


Överkurs: Läs mer om undantag och hierarkin. Följ länkarna i texten ovanför tabellen.

Överkurs: Det går att ha flera except i en och samma try-sats för att hantera olika undantag. Läs om detta här.

Övning 1.17
Denna övning visar hur man skriver ett program som hanterar inmatning så att frågan upprepas tills användaren matar in korrekt datatyp. Skriv och testa.

while True :
    try :
        a=float(input("Skriv en siffra "))
        break
    except ValueError :
        print("Ooops! Ej ett tal")
print("Du skrev " + str(a))

Programmet snurrar i while-loopen om det blir ett undantag i tilldelningen av a. Om tilldelningen sker korrekt når programmet break-satsen och hoppar ur loopen  till sista raden.

Uppgift 1.5
Skriv om inlämningsuppgift 1.2, andragradsekvationsprogrammet, så att det hanterar felaktiga värden vid inmatning (så som text eller bara ENTER) samt använder undantag istället för if-sats för att hantera negativa rötter.

Uppgift 1.6
Då man ska mata in många värden är det extra viktigt att använda undantagshantering. Ingen vill mata in 143 värden och sedan kraschar programmet pga att man råkar trycka fel vid det 144...
Skriv om uppgift 1.4 så att felaktiga inmatningar hanteras. Tips: det går att utnyttja den oändliga loopen som redan finns för att effektivt genomföra detta.

tisdag 4 september 2012

While-loopar

I en loop upprepas samma satser flera gånger. Vi ska här se på en av två vanliga loopar nämligen while-loopen

While-loopen

While-loopen upprepar satserna i loopen tills logiska uttrycket i while-satsen inte är sant. Precis som för if-satser används indrag för att markera vilka satser som ska loopas:

while logiskt uttryck:
   sats(er) 

Övning 1.12
Skriv och testa följande program.
#Gissa tal

import random #modulen random gör så att jag kan generera slumptal

a = random.randint(1, 10) #Genererar ett slumpmäsigt heltal 1 till 10
b = 0                     #Initierar gissat tal utanför intervallet
gissningar = 0            #Antal gjorda gissningar 
while a != b :
    b = int(input("gissa talet:"))
    gissningar = gissningar + 1 #Ökar antalet gissningar med 1
print("Du gissade rätt på " + str(gissningar) + " försök")

Det finns några saker att notera i detta program:
  • Användningen av random.randint(1, 10) som generar ett slumpat heltal från och med 1 till och med 10. Läs gärna mer om generering av slumptal här.
  • Initieringen av b före loopen. Variabeln b sätts till 0 som är utanför intervallet för slumptalet. Vad kunde ha hänt annars?
  • Initieringen av gissningar till 0 då den kommer att ökas med 1 efter den första gissningen.
  • Tilldelningen gissningar=gissningar+1 gör att gissningar ökas med 1 efter varje gissning. Lägg märket till att denna typ av =-användning är felaktig matematiskt men vanlig inom programmering.
Överkurs: Tilldelningen a = a + 1 kan skrivas mer kortfattat med operatorn += så som a += 1 . Ändra i programmet så att denna operator används.

Övning 1.13
Ändra programmet i övning 1.12 så att ett slumpat heltal mellan 1 och 100 genereras samt att användaren får reda på om gissningen var större eller mindre än rätt svar efter varje gissning.

Uppgift 1.3
Skriv ett program som för varje varv i en loop genererar ett slumpat heltal 1 till 6 och adderar det med föregående tal samt skriver ut summan. Programmet ska loopa tills summan blivit minst 100.


Oändlig while-loop och break

Nedanstående är en oändlig loop.

while True :
    print("loop the loop")

OBS! Ovanstående köres på egen risk. Prova ctrl+C för att bryta loopen men inga garantier ges.

Dessa kan vara användbara ibland men vanligen vill man ha något sätt att bryta loopandet. Då kommer kommandot break till hjälp. Med break bryts loopen och programmet fortsätter nedanför loopens satser.

Kommandot break kan användas i alla loopar för att hoppa ur loopen.

Övning 1.14
Skriv programmet nedan och testa funktionen

while True :
    a=int(input("Skriv in ett tal. (0 för att sluta) "))
    if a == 0 : break
print("Du skrev noll. Avslutar")

Uppgift 1.4
Genom att  använda en oändlig loop samt break  i stället för att göra testet om gissningen är korrekt i while-satsen kan programmet i övning 1.13 skrivas kortare och med färre variabler. Gör det.