Introduktion till programmering Föreläsning 9: Tupler 1 1
Sammansatta datatyper Strängar Sekvenser av tecken Icke muterbara Syntax: "abcde" Listor Sekvenser av vad som helst Muterbara Syntax: [1, 2, 3] Nytt för idag: tupler Sekvenser av vad som helst (likt listor) Icke muterbara (likt strängar) Syntax: (1, 2, 3) 2 2
Tupel-operationer Grundidé: samma operationer som för listor >>> ('a', 0) * 3 ('a', 0, 'a', 0, 'a', 0) >>> x = (1, 2, 3) + ('a', 'b') >>> x[:] (1, 2, 3, 'a', 'b') >>> x[1:3] (2, 3) >>> x[0] 1 Men: >>> x[0] = 8 TypeError: object doesn't support item assignment 3 3
En lite udda detalj Minns: listan med endast ett element x skrivs [x] ej samma sak som att skriva endast x Tupeln med endast ett element x kan dock inte skrivas (x) detta betyder ju enbart x (med onödiga parenteser) Pythons fix för att lösa problemet: ett extra komma! Alltså: tupeln med endast ett element x skrivs (x,) Varför är detta viktigt? Se exempel: >>> (1, 2, 3) + (4) TypeError: can only concatenate tuple (not "int") to tuple >>> (1, 2, 3) + (4,) (1, 2, 3, 4) 4 4
Tupler kontra listor Varför behövs tupler över huvud taget om det enda som skiljer gentemot listor är att man inte kan mutera tupler? Svar 1: Tupler kan av denna anledning användas på sätt som listor inte kan, inte minst fungera som nycklar i dictionaries Svar 2: Det finns en stark tradition (inom många språk) att skilja på homogena strukturer med variabel längd (listor) och heterogena strukturer med fix längd (tupler) Svar 3: Likheten mellan tupler och argumentlistor är elegant...! 5 5
Tupler som resultat En funktion som kastar om sina två argument: def swap(x, y): return (y, x) En funktion som hittar en listas extremvärden: def bounds(list): min = max = list[0] for x in list: if x < min: min = x if x > max: max = x return (min, max) 6 6
Tupeltilldelning Syntaktisk finess i Python: I stället för temp = a a=b b = temp kan man skriva (a, b) = (b, a) Här (liksom i många andra sammanhang) går det bra att utelämna parenteserna runt en tupel: a, b = b, a Observera: alla uttryck på högersidan evalueras före någon av tilldelningarna får effekt 7 7
Kontrollfråga Varför fungerar inte följande variant av swap? def swap2(x, y): x, y = y, x Svar: för att effekten av tilldelningen endast är lokal! Se följande stackdiagram för anropet swap2(a, b) -toplevel- swap2 a b 3 7 x 3 7 7 3 y 8 8
Listor och tupler Listor förväntas oftast innehålla samma typ av element i alla positioner, medan längden kan variera: for x in list:... Tupler däremot förutsätts ofta ha en låst längd, men kan ha olika typer av element i olika positioner: (k,v) = tuple: if k == 'SMD': return v+1 Notera: Python tillåter egentligen både heterogena listor och iteration över tupler, men mönstren ovan blir svåra att använda om uppdelningen mellan listor och tupler inte följs 9 9
Om slumpen Kan vi med det vi nu vet om programmering skriva en funktion vars resultat är fullständigt slumpmässigt? Svar: Nej! Ett Python-program (och en dator) beter sig alltid på exakt samma sätt, givet samma input Vad vi däremot kan göra är att skapa matematiska talserier som ser slumpmässiga ut (pröva t ex att upprepa a = (a * 32719 + 3) % 32749) ge ett sant slumpmässigt värde som input (t ex nanosekund-decimalerna på datorns klocka vid start) Ofta är en kombination av dessa tekniker den bästa lösningen (startvärde som input, därefter talserier) 10 10
Modulen random Innehåller ett flertal funktioner som beräknar pseudoslumpmässiga talserier enligt diverse statistiska fördelningar, med startvärde baserat på ett verkligt slumptal (datorns klocka) Användbar funktion: random.random() returnerar ett nytt pseudoslumptal varje gång den anropas, jämt fördelat i intervallet 0.0 x < 1.0: import random for i in range(10): x = random.random() print x Önskas annat intervall? Multiplicera x med maxvärdet! 11 11
Listor med slumptal Skapa först listan med enbart nollor, mutera sedan: def randomlist(n): s = [0.0] * n for i in range(n): s[i] = random.random() return s Körning: >>> randomlist(6) [0.62504370149889954, 0.56128788040723299, 0.7091414734252125, 0.1727060649300064, 0.61854565558054986, 0.98306629398279177] Hur pass jämt fördelar sig dessa tal? 12 12
Låt oss räkna Räknande kod som vi redan sett: count = 0 for char in fruit: if char == 'a': count = count + 1 print count Låt oss återanvända, med små förändringar: count = 0 for num in list: if low < num < high: count = count + 1 print count 13 13
Räkna slumptal Inkapslat som en funktion: def inbucket(list, low, high): count = 0 for num in list: if low <= num < high: count = count + 1 return count Grov koll om slumptalen är jämt fördelade: x = randomlist(50) lower = inbucket(x, 0.0, 0.5) upper = inbucket(x, 0.5, 1.0) print lower, upper 14 14
Räkna slumptal Mer finkorning kontroll: bucket1 = inbucket(x, 0.0, 0.25) bucket2 = inbucket(x, 0.25, 0.5) bucket3 = inbucket(x, 0.5, 0.75) bucket4 = inbucket(x, 0.75, 1.0) Observation: om vi vill dela in intervallet i n delar kommer varje del at ha bredden 1.0/n Vi generaliserar med hjälp av en snurra: bucketwidth = 1.0/numBuckets for i in range(numbuckets): low = i * bucketwidth high = low + bucketwidth... 15 15
Räkna slumptal Efter inkapsling: def histogram(list, numbuckets): buckets = [0] * numbuckets bucketwidth = 1.0 / numbuckets for i in range(numbuckets): low = i * bucketwidth high = low + bucketwidth buckets[i] = inbucket(list, low, high) return buckets Körning: >>> histogram( randomlist(1000), 8 ) [138, 124, 128, 118, 130, 117, 114, 131] 16 16
Om effektivitet Om vår lista har N slumptal, och hela listan ska testas gentemot M "hinkar"... det blir N*M*2 jämförelser! Detta är inte vidare effektivt det borde gå att undersöka slumptalens fördelning genom att gå igenom slumptalslistan endast en gång Låt oss i stället för varje slumptal 0 x < 1.0 försöka hitta index för motsvarande hink, givet att vi har numbuckets hinkar Detta index borde vara närmaste heltal mindre eller lika med x*numbuckets...... dvs int(x*numbuckets)! 17 17
En effektivare variant Ny algoritm: def histogram2(list, numbuckets): buckets = [0] * numbuckets for x in list: index = int(x*numbuckets) buckets[index] = buckets[index] + 1 return buckets Skillnaden mellan histogram och histogram2 syns inte i vad som beräknas, endast i hur lång tid beräkningarna tar histogram2: tiden är proportionell mot listans längd histogram: tiden är proportionell mot listans längd gånger antalet hinkar 18 18
Komplexitet Vi säger att histogram och histogram2 har olika tidskomplexitet Komplexitet är ett mått på hur väl en lösning kan appliceras på stora datamängder, dvs hur effektiv lösningen är "i allmänhet" Om vi låter både antalet slumptal och antalet hinkar växa mot oändligheten ser vi att histogram har kvadratisk tidskomplexitet, medan histogram2 är linjär Både tidskomplexitet och det nära besläktade begreppet minneskomplexitet kommer att återkomma i högre datatekniska kurser! 19 19