729G04 PYTHON 4 JODY FOO Department of Computer and Information Science (IDA) Linköping University, Sweden
TURTLE GRAPHICS
LOGO LOGO: programmeringsspråk för användning inom undervisning 1967 av Papert och Feurzeig Språket är släkt med LISP, men som "utenhet" hade man bl.a. möjlighet att styra en sköldpadda. Sköldpaddan hade även en "penna" som den kunde höja och sänka.
The robot
Trådlös variant
Modern variant Valiant Technology Ltd.
Turtle Graphics Att rita med hjälp av en sköldpadda som man styr med hjälp av kommandon brukar man kalla för Turtle Graphics. Man ser sköldpaddan uppifrån och kan ge den kommandon Python har en modul som låter oss hålla på med Turtle Graphics.
Kommandon man kan ge till sköldpaddan Gå frammåt Backa Sväng vänster Sväng höger Dra upp pennan Sätt ner pennan
http://el.media.mit.edu/logo-foundation/logo/turtle.html
PYTHONS TURTLE-MODUL
Förflyttning av sköldpaddan fd(avstånd): flytta sköldpaddan frammåt ett visst avstånd bk(avstånd): flytta sköldpaddan bakåt ett visst avstånd lt(grader): sväng vänster ett visst antal grader rt(grader): sväng höger ett visst antal grader
En fyrkant import turtle screen = turtle.screen() screen.setup(width=600, height=600) turtle.mode("logo") turtle.showturtle() turtle.fd(100) turtle.rt(90) turtle.fd(100) turtle.rt(90) turtle.fd(100) turtle.rt(90) turtle.fd(100) importera modulen skapa en rityta sätt storleken på ritytan Använd LOGO-standard för riktning visa sköldpaddan gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg
En fyrkant framåt
En fyrkant vänster
En fyrkant framåt
En fyrkant vänster
En fyrkant framåt
En fyrkant vänster
En fyrkant framåt
En fyrkant
DEMO AV PENNAN, FÖRFLYTTNING, POSITION OCH RIKTNING
En fyrkant import turtle screen = turtle.screen() turtle.showturtle() turtle.fd(100) turtle.rt(90) turtle.fd(100) turtle.rt(90) turtle.fd(100) turtle.rt(90) turtle.fd(100) importera modulen skapa en rityta visa sköldpaddan gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg
En fyrkant from turtle import * screen = Screen() showturtle() fd(100) rt(90) fd(100) rt(90) fd(100) rt(90) fd(100) importera modulen skapa en rityta visa sköldpaddan gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg sväng höger 90 grader gå framåt 100 steg
KAN VI STOPPA IN DETTA I EN LOOP?
KAN VI FÅ SKÖLDPADDAN ATT RÖRA SIG SLUMPMÄSSIGT?
Sköldpaddan ritar på ritytan med en penna Sköldpaddan har en penna som kan höjas och sänkas. Den är nere från början. pd(): sätt ner pennan pu(): dra upp pennan
KOORDINATSYSTEM OCH RIKTNING
(-, +) (+, +) (0, 0) (-, -) (+, -)
Sköldpaddan och koordinater pos() returnerar en tupel (x, y) där x och y är flyttal setpos(x, y) flyttar sköldpaddan till positionen (x, y) där x och y är flyttal. OBS! setpos() teleporterar inte sköldpaddan, dvs att om pennan är nere kommer det att bli ett streck.
Ja, tupler finns i Python också... Lista: [1, 2, 3] Tupel: (1, 2, 3) Den stora skillnaden mellan listor och tupler i Python är att man inte kan ändra på en tupel. Datatyper som man inte kan ändra värdet på kallas "immutable" Datatyper man kan ändra värde på kallas "mutable"
TUPEL-EXEMPEL
Exempel >>> a = [1, 2, 3] >>> b = (1, 2, 3) >>> a[0] = 4 >>> b[0] = 4 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment >>>
Sköldpaddan och koordinater pos() returnerar en tupel (x, y) där x och y är flyttal setpos(x, y) flyttar sköldpaddan till positionen (x, y) där x och y är flyttal. OBS! setpos() teleporterar inte sköldpaddan, dvs att om pennan är nere kommer det att bli ett streck.
RIKTNING
Riktning i världen mode("standard") 90 mode("logo") 0 0 90
Sköldpaddan vet vilken riktning den tittar åt mode("logo") 0 är norrut 90 grader är österut gradantalet ökar medurs heading() returnerar ett flyttal som berättar gradantalet hos den riktning man står åt seth(d) där d är ett flyttal, ställer in sköldpaddans riktning
CIRKELRÖRELSE
Exempel på anrop till circle() circle(40) circle(-40) circle(40, 90) circle(40, 180, 4)
Cirkelrörelse circle() kan ta ett till tre argument circle(40) flyttar sköldpaddan moturs i en cirkel med radien 40 circle(-40) ger en rörelse i medurs riktning circle(40, 90) avbryter rörelsen efter 90 grader circle(40, 90, 2) säger att sköldpaddan kommer dela upp rörelsen i två raksträckor.
REFAKTORISERING ATT SKRIVA OM KOD FÖR ATT GÖRA DEN BÄTTRE UTAN ATT ÄNDRA PÅ DESS BETEENDE
Exempel och refaktorisering Några exempel på vad man kan göra med turtle-modulen samt genomgång av refaktorisering genom generalisering och inkapsling Inkapsling: Att kapsla in en sekvens av uttryck och satser i en funktion Generalisering: Att göra en funktion mer generell genom att specificera beteenden med hjälp av parametrar/argument
Refaktorisering (refactoring) Förbättring av kod dock med bibehållen funktionalitet. Förbättringen kan avse underhåll lättlästhet Inte att förväxla med optimering som har med förbättring av prestanda att göra. Båda kan dock inträffa samtigt. Optimering kommer på nästa föreläsning.
Exempel på refaktorisering def get_number_of_children(name): if name == "Ada": return 1 elif name == "Bean": return 3 elif name == "Cecil": return 1 elif name == "Dan": return 2 elif name == "Eliza": return 2 elif name == "Frodo": return 0 elif name == "Garfield": return 0 def get_number_of_children(name): if name in ["Frodo", "Garfield"]: return 0 elif name in ["Ada", "Cecil"]: return 1 elif name in ["Dan", "Eliza"]: return 2 elif name in ["Bean"]: return 3
När ska man refaktorisera? När koden luktar illa - exempel på dålig lukt: redundant kod, t.ex. kod som upprepas eller kod som inte gör något långa funktioner väldigt många parametrar skickas till en funktion en funktion som gör för lite variabelnamn eller funktionsnamn som har missvisande namn komplexa vilkor i koden
Sick sack Vi vill producera ovanstående mönster. Kom ihåg: programmering är en iterativ aktivitet! Vi gör ett steg i taget.
Sick sack from turtle import * screen = Screen() mode("logo") fd(100) rt(90) fd(10) rt(90) fd(100) lt(90) fd(10) lt(90) fd(100) rt(90) fd(10) rt(90) fd(100) lt(90) fd(10) lt(90) fd(100) rt(90)
Sick sack - vi ser en loop from turtle import * screen = Screen() mode("logo") for i in range(3): fd(100) rt(90) fd(10) rt(90) fd(100) lt(90) fd(10) lt(90) fd(100) rt(90) fd(10) rt(90) fd(100) lt(90) fd(10) lt(90) fd(100) rt(90) fd(10) rt(90) fd(100) lt(90) fd(10) lt(90) fd(100) rt(90) fd(10) rt(90) fd(100) lt(90) fd(10) lt(90)
Inkapsling (en. encapsulation) print "Hi Ben!" print "I am happy!" def greet_ben(): print "Hi Ben!" print "I am happy!" Inkapsling: Att kapsla in en sekvens av uttryck och satser i en funktion.
Varför är detta bra? Återanvändning! def greet_ben(): print "Hi Ben!" print "I am happy!" greet_ben() greet_ben()
Sick sack - Inkapsling for i in range(3): fd(100) rt(90) fd(10) rt(90) fd(100) lt(90) fd(10) lt(90) def make_right_l(): fd(100) rt(90) fd(10) rt(90) def make_left_l(): fd(100) lt(90) fd(10) lt(90) for i in range(3): make_right_l() make_left_l()
Generalisering Att gå från en funktion som utför en specifik uppgift, till en funktion som är mer flexibel med avseende på beteende genom att införa parametrar
Generalisering (en. generalization) def greet_ben(): print "Hi Ben!" print "I am happy!" def greet(name): print "Hi " + name + "!" print "I am happy!" Att göra en funktion mer generell genom att specificera beteenden med hjälp av parametrar/argument
Generalisering def make_right_l(): fd(100) rt(90) fd(10) rt(90) def make_left_l(): fd(100) lt(90) fd(10) lt(90) def make_l(dir): angle = 0 if dir == "standard": angle = -90 elif dir == "mirror": angle = 90 fd(100) rt(angle) fd(10) rt(angle) for i in range(3): make_l("standard") make_l("mirror")
Inkapsling igen def make_l(dir): angle = 0 if dir == "standard": angle = -90 elif dir == "mirror": angle = 90 fd(100) rt(angle) fd(10) rt(angle) for i in range(3): make_l("standard") make_l("mirror") def make_l(dir): angle = 0 if dir == "standard": angle = -90 elif dir == "mirror": angle = 90 fd(100) rt(angle) fd(10) rt(angle) def zigzag(): for i in range(3): make_l("standard") make_l("mirror") zigzag()
Generalisering igen def make_l(dir): angle = 0 if dir == "standard": angle = -90 elif dir == "mirror": angle = 90 fd(100) rt(angle) fd(10) rt(angle) def zigzag(): for i in range(3): make_l("standard") make_l("mirror") zigzag() def make_l(dir): angle = 0 if dir == "standard": angle = -90 elif dir == "mirror": angle = 90 fd(100) rt(angle) fd(10) rt(angle) def zigzag(n): for i in range(n): make_l("standard") make_l("mirror") zigzag(3)
Stjärnan Vi vill rita en sån här figur.
Hur ska vi göra? En ansats till att rita en "streck-stjärna" är att utgå från just streck. Vi ser också att vi kan låta strecken utgå från mitten av stjärnan. Vi kapslar in detta i en funktion.
Streck - Tur och retur Pythonstandard för funktionsdokumentation def draw_line_and_return(): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(50) bk(50) Vi gör en funktion som ritar ett streck och ser till att sköldpaddan slutar där den började.
Rita fem streck # rita fem streck # 1. rita ett streck # 2. vänd på dig så mycket som behövs för att t.ex. rita 5 streck per varv # 3. fortsätt med ovanstående tills vi ritat 5 streck
FEM STRECK FRÅN MITTEN!
Rita fem streck def draw_line_and_return(): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(50) bk(50) fd(50) bk(50) draw_line_and_return() rt(360.0/5) draw_line_and_return() rt(360.0/5) draw_line_and_return() rt(360.0/5) draw_line_and_return() rt(360.0/5) draw_line_and_return() rt(360.0/5)
Rita fem streck - lägg till loop def draw_line_and_return(): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(50) bk(50) fd(50) bk(50) draw_line_and_return() rt(360.0/5) draw_line_and_return() rt(360.0/5) draw_line_and_return() rt(360.0/5) draw_line_and_return() rt(360.0/5) draw_line_and_return() rt(360.0/5) def draw_line_and_return(): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(50) bk(50) for line in range(5): draw_line_and_return() rt(360.0/5)
Rita fem streck - kapsla in def draw_line_and_return(): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(50) bk(50) for line in range(5): draw_line_and_return() rt(360.0/5) def draw_line_and_return(): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(50) bk(50) def draw_star(): """Rita ut en stjärna med fem armar""" for line in range(5): draw_line_and_return() rt(360.0/5) draw_star()
Rita fem streck - generalisera def draw_line_and_return(): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(50) bk(50) def draw_star(): """Rita ut en stjärna med fem armar""" for line in range(5): draw_line_and_return() rt(360.0/5) draw_star() def draw_line_and_return(length): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(length) bk(length) def draw_star(): """Rita ut en stjärna med fem armar""" for line in range(5) draw_line_and_return(50) rt(360.0/5) draw_star()
Rita fem streck - generalisera def draw_line_and_return(length): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(length) bk(length) def draw_star(): """Rita ut en stjärna med fem armar""" for line in range(5) draw_line_and_return(50) rt(360.0/5) draw_star() def draw_line_and_return(length): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(length) bk(length) def draw_star(points, length): """Rita ut en stjärna med fem armar""" for line in range(points): draw_line_and_return(length) rt(360.0/points) draw_star(5, 50)
DEBUGGA MED SPÅRUTSKRIFTER
Att hitta vad som går fel skriv ut värden på variabler på lämpligt ställe i koden print("x: ", x)
Exempel på funktion med spårutskrifter def draw_line_and_return(length): """Rita ett streck och återvänd till startpositionen.""" # Språrutskrift print("drawing line, length:", length) fd(length) bk(length) def draw_star(points, length): """Rita ut en stjärna med fem armar""" for line in range(points): # Skriv ut vilket streck vi ligger på print("line:", line) draw_line_and_return(length) rt(360.0/points)
VISUALISERING AV NÄSTLAD LOOP
Stjärna med stjärnor # Ursprunglig stjärnkod def draw_line_and_return(length): """Rita ett streck på 50 enheter och återvänd till startpositionen.""" fd(length) bk(length) def draw_star(points, length): """Rita ut en stjärna med fem armar""" for line in range(points): draw_line_and_return(length) rt(360.0/points) draw_star(5, 50) def draw_starstar(points, length): """Rita ut en stjärna med fem armar""" for line in range(points): # stoppar tillbaka utritning av streck fd(length) bk(length) rt(360.0/points) draw_star(5, 50)
Stjärna med stjärnor def draw_starstar(points, length): """Rita ut en stjärna med fem armar""" for line in range(points): # stoppar tillbaka utritning av streck fd(length) bk(length) rt(360.0/points) draw_star(5, 50) def draw_starstar(points, length): """Rita ut en stjärna med fem armar""" for line in range(points): fd(length) # ritar en mindre stjärna efter varje streck for line2 in range(points): fd(length/2) bk(length/2) rt(360.0/points) bk(length) rt(360.0/points) draw_starstar(5, 50)
REKURSION
Man kan skriva rekursiva funktioner i Python Minns att rekursiva funktioner anropar sig själva. Man kan loopa med hjälp av rekursion.
Rekursiva funktioner kontra iterativa loopar Rekursiva funktioner ligger nära matematiska funktioner. Loopar som använder for eller while, kallas för iterativa loopar. Rekursiva funktioner kan ta både längre tid och kan kräva mer minne än iterativa lösningar. Vissa problem kan dock ibland utryckas "enklare" med en rekursiv funktion.
Att skriva en rekursiv funktion Funktionen måste returnera ett värde Funktionsdefinitionen har minst ett argument Ett slutvillkor måste finnas
Skapa en lista med siffror def generate_number_list(i): if i == 0: return [0] return [i] + generate_number_list(i-1) Givet 5 som input generera listan [5, 4, 3, 2, 1, 0] Vi generera den genom ett rekursivt anrop som gör något med det aktuella värdet och ropar på sig själv med nästa värde.
Struktur på rekursiv funktion med returvärde def sum(number): """Denna funktion tar in en siffra och returnerar summan av sig själv och föregående siffror.""" # Första vilkoret berättar alltid när vi ska sluta if number == 0: return 0 else: return number + sum(number - 1)
Struktur på rekursiv funktion med returvärde def value_in_list(n, values): """Returnera True om värdet n finns med i listan values""" # Första vilkoret berättar alltid när vi ska sluta if len(values) = 0: return False elif n == values[0]: return True else: return value_in_list(n, values[1:])
VISUALISERING AV REKURSION
Nästlad loop jmf med rekursion Nästlad loop: loop i loop Rekursiv funktion: funktionsanrop i funktionsanrop
Stjärna med stjärnor def draw_starstar(points, length): """Rita ut en stjärna med fem armar""" for line in range(points): fd(length) # ritar en mindre stjärna efter varje streck for line2 in range(points): fd(length/2) bk(length/2) rt(360.0/points) bk(length) rt(360.0/points) draw_starstar(5, 50) def draw_starstar(points, length): """Rita ut en stjärna med fem armar""" for line in range(points): fd(length) # ritar en mindre stjärna efter varje streck om strecket är längre än 5 if length > 5: draw_starstar(points, length/2) bk(length) rt(360.0/points) draw_starstar(5, 50)
Exempel: Helt rekursiv (enkel) stjärna # Denna funktion ritar ut en stjärna, men den är skriven rekursivt # snarare än med en iterativ loop. Fler argument måste skickas vid # funktionsanropet som används som "minne" mellan anropen. def recursive_star(total_points, current_point, length): """Rita ut en stjärna""" if current_point == 0: pass else: fd(length) bk(length) rt(360.0/total_points) recursive_star(total_points, current_point-1, length)
Exempel: dubbelrekursiv stjärna def recursive_starstar(total_points, current_point, length): """Rita ut en stjärna med stjärnor i spetsarna""" # sluta rita när vi har ritat alla streck if current_point == 0: pass else: fd(length) # om stjärnstrecket är tillräckligt långt, kan vi rita en till stjärna # i spetsen if length > 5: recursive_starstar(total_points, total_points, length / 2) bk(length) rt(360.0 / total_points) recursive_starstar(total_points, current_point - 1, length)
Labb 4 Tillämpning av funktioner, loopar och sköldpaddor Labb 4-8 görs i par Anmäl er till nya pargrupper https://www.ida.liu.se/webreg/729g04-2014/parlabb
ANGRY TURTLES
Angry Turtles Fåglar har stulit sköldpaddornas ägg och sköldpaddorna hämnas genom att skjuta iväg sig själva på en måltavla. Användaren ger styrka och vinkel Spelet ritar ut hur sköldpaddan flyger iväg och kollar om sköldpaddan träffar målet.
Angry Turtles - innan spelet kan börja Innan vi spelet kan börja måste vi göra följande Ställa in ritytan Bestämma var måltavlan ska vara Rita ut måltavlan Placera sköldpaddan i startposition
Angry Turtles - själva spelet Spelaren börjar med t.ex. 1000 poäng Spelloop Fråga användaren parametrar: styrka och ev vinkel - använd input() Förflytta sköldpaddan lite i taget (loop) förflyttningen baseras på angiven styrka (och ev vinkel) avbryt loopen (break) när sköldpaddan nuddat marken (eller är under marken), dvs kolla om y <= 0. Kolla avståndet till måltavlan Om sköldpaddan är tillräckligt nära måltavlan - berätta det, visa poäng och fråga om spelaren vill spela igen. Om sköldpaddan var för långt ifrån måltavlan - berätta det och dra ifrån poäng