Post on 17-Apr-2020
transcript
ŘETĚZCE, SEZNAMY, NTICEIB111 ZÁKLADY PROGRAMOVÁNÍ
Nikola Beneš10. října 2019
ŘETĚZCE
ŘETĚZCE – SYNTAX (V PYTHONU)
• uvozovky: "Toto je text."• alternativně apostrofy: 'Toto je taky text.'• víceřádkové řetězce:
text = """Toto je text,který pokračuje na dalším řádku.A potom ještě na jednom."""
• speciální symboly – uvozené zpětným lomítkem \• konec řádku \n• tabulátor \t• uvozovka \", apostrof \'• zpětné lomítko \\• … a různé jiné
print("\N{grinning face}")
1
text = """Toto je text,který pokračuje na dalším řádku.A potom ještě na jednom."""
print(text)
print("\N{grinning face}")
ŘETĚZCE – ZÁKLADNÍ OPERACE
• zřetězení: "ahoj, " + "lidi"• „násobení“: "kolo" * 3 (specialita Pythonu)• porovnávání• typování konverze čísla na řetězec: str(3.14)• přiřazení do proměnné: text = "Dnes je pěkně."• délka textu (počet znaků): len(text)• indexování (výběr konkrétního znaku): text[0], text[2]• indexování od konce: text[-1]• pořadové číslo znaku (v Unicode): ord("b")• znak pro zadané pořadové číslo: chr(99)
2
INDEXOVÁNÍ
Indexování od nuly
• první znak řetězce je text[0]• částečně historicko-technické důvody• ale i dobré „matematické“ důvody• souvisí s oblibou polouzavřených intervalů ⟨𝑜𝑑, 𝑑𝑜)
Pro zajímavost:
• https://en.wikipedia.org/wiki/Zero-based_numbering• https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html
• https://softwareengineering.stackexchange.com/questions/110804/why-are-zero-based-arrays-the-norm
3
https://en.wikipedia.org/wiki/Zero-based_numberinghttps://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.htmlhttps://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.htmlhttps://softwareengineering.stackexchange.com/questions/110804/why-are-zero-based-arrays-the-normhttps://softwareengineering.stackexchange.com/questions/110804/why-are-zero-based-arrays-the-norm
POKROČILEJŠÍ INDEXOVÁNÍ
(specifické pro Python)
text[0:3] # první 3 znakytext[:3] # --- totéž ---text[3:] # od 4. znaku do koncetext[1:8:2] # od 2. znaku po 7., jen každý druhý znaktext[::3] # od začátku do konce, každý třetí
• všimněte si podobnosti s range() – není náhodná
4
REPREZENTACE ŘETĚZCŮ V POČÍTAČI
Jak jsou znaky reprezentovány v počítači?
• ASCII, ISO8859-2, Windows-1250, Unicode (UTF-8, UTF-16, UTF-32),…
• Python umí pracovat s různými kódováními• zdrojový soubor programu je v UTF-8 (dá se změnit) (Python 3)• interní reprezentace řetězců v Pythonu:
• složitější (PEP-393), ale pokrývá celý Unicode
• pro účely tohoto předmětu:• ord, chr – převod znaků na čísla a zpět• znaky anglické abecedy mají přiřazena po sobě jdoucí čísla
for i in range(26):print(chr(ord('A') + i))
5
for i in range(26): print(chr(ord('A') + i))
MODIFIKACE ŘETĚZCŮ
• řetězce v Pythonu jsou neměnné (immutable)• rozdíl proti řetězcům v některých jiných jazycích• rozdíl proti seznamům v Pythonu
• místo změny znaku musíme vytvořit nový řetězec
text = "holinky"# text[2] = "d" # error!text = text[:2] + "d" + text[3:]
6
text = "holinky"# text[2] = "d" # error!text = text[:2] + "d" + text[3:]
print(text)
ŘETĚZCE – DALŠÍ OPERACE
text = "hello, world!"print(text.upper())print(text.lower())print(text.capitalize())print(text.rjust(30))print(">" + text.center(30) + "" + text.center(30) + "
ŘETĚZCE – FORMÁTOVÁNÍ
Vkládání čísel (a jiných hodnot) do řetězců
• starý způsob:"Jmenuji se %s a je mi %d let." % ("Metuzalém", 900)
• nový způsob (od Pythonu 3.1): metoda format()• nejnovější způsob (od Pythonu 3.6): tzv. f-strings
print("Dnes je {}. {}. ({})".format(10, "října", "čtvrtek"))
print("Je právě {:d}.{:02d}".format(17, 7))print("X{:10}X".format("text"))print("X{:^10}X".format("text"))print("{:.2f}".format(math.pi))
• format toho umí spoustu: https://pyformat.info/8
https://pyformat.info/import math
print("Dnes je {}. {}. ({})" .format(10, "října", "čtvrtek"))print("Je právě {:d}.{:02d}".format(17, 7))print("X{:10}X".format("text"))print("X{:^10}X".format("text"))print("{:.2f}".format(math.pi))
print(f"{math.pi:.2f}")
PŘÍKLAD – CAESAROVA ŠIFRA
• vstup: text, posun• výstup: zašifrovaný text
def caesar_cipher(text, shift):result = ""text = text.upper()for char in text:
if char.isalpha():code = (ord(char) - ord('A') + shift) % 26result += chr(code + ord('A'))
else:result += char
return result
• jak dešifrovat? jak dešifrovat, když neznáme posun?
LBH YBFG GUR TNZR 9
def caesar_cipher(text, shift): result = "" text = text.upper() for char in text: if char.isalpha(): code = (ord(char) - ord('A') + shift) % 26 result += chr(code + ord('A')) else: result += char return result
print(caesar_cipher("This is a very secret message.", 3))
PŘÍKLAD – STRATEGIE PRO KÁMEN, NŮŽKY, PAPÍR
def strategy_uniform_1():choice = random.randint(1, 3)if choice == 1:
return "R"if choice == 2:
return "S"return "P"
def strategy_uniform_2():return "RSP"[random.randint(0, 2)]
def strategy_uniform_3():return random.choice("RSP")
10
import random
def strategy_uniform_1(): choice = random.randint(1, 3) if choice == 1: return "R" if choice == 2: return "S" return "P"
def strategy_uniform_2(): return "RSP"[random.randint(0, 2)]
def strategy_uniform_3(): return random.choice("RSP")
SEZNAMY, NTICE
MOTIVACE PRO SEZNAMY
• chceme zpracovávat větší množství položek• nechceme psát opakovaně stejný kód (DRY)• nemusíme předem znát počet položek
Příklady
• simulace preferencí politických stran (minule)• frekvence písmen v textu• seřazení studentů podle počtu bodů• reprezentace herního plánu (piškvorky, šachy)• …
11
PŘÍKLAD: FREKVENCE PÍSMEN (NEVHODNÉ ŘEŠENÍ)
def frequency_analysis(text):text = text.upper()freqA = 0freqB = 0freqC = 0for letter in text:
if letter == "A":freqA += 1
elif letter == "B":freqB += 1
elif letter == "C":freqC += 1
print("A:", freqA)print("B:", freqB)print("C:", freqC)
12
def frequency_analysis(text): text = text.upper() freqA = 0 freqB = 0 freqC = 0 for letter in text: if letter == "A": freqA += 1 elif letter == "B": freqB += 1 elif letter == "C": freqC += 1 print("A:", freqA) print("B:", freqB) print("C:", freqC)
jabberwocky = """Jabberwocky
'Twas brillig, and the slithy tovesDid gyre and gimble in the wabe;All mimsy were the borogoves,And the mome raths outgrabe."""
frequency_analysis(jabberwocky)
SEZNAMY V PYTHONU
0 1 2 3 4
• libovolný počet položek v pořadí za sebou• indexováno od nuly (jako řetězce)• běžně dostupné v jiných jazycích
• pole (array) – pevná délka, všechny položky stejného typu• dynamická pole, seznamy, …
• seznamy v Pythonu – obecnější než pole• umí měnit velikost• smí obsahovat položky různých typů• (ale práce se seznamy je pomalejší)
• pole v Pythonu (knihovna NumPy; nad rámec předmětu)
13
VYTVOŘENÍ SEZNAMU
• výčtem prvků
s = []s = [3, 1, 4, 1, 5]s = ["ABC", 3.14, -7]s = [[1, 2], [3, 4]]s = ["pes", "kočka", 0.01, ["velbloud", -13], []]
• tzv. list comprehension (intenzionální zápis seznamu)
s = [2 * x for x in range(10)]s = [x ** 2 for x in range(1, 10) if x % 2 == 0]s = [3 * x for x in [5, 17, 23, 40]]s = [[a, b, c] for a in range(1, 10)
for b in range(1, 10)for c in range(1, 10)if a ** 2 + b ** 2 == c ** 2] 14
s = []s = [3, 1, 4, 1, 5]s = ["ABC", 3.14, -7]s = [[1, 2], [3, 4]]s = ["pes", "kočka", 0.01, ["velbloud", -13], []]
s = [2 * x for x in range(10)]s = [x ** 2 for x in range(1, 10) if x % 2 == 0]s = [3 * x for x in [5, 17, 23, 40]]s = [[a, b, c] for a in range(1, 10) for b in range(1, 10) for c in range(1, 10) if a ** 2 + b ** 2 == c ** 2]
print(s)
OPERACE SE SEZNAMY
len(s) # délka seznamus.append(17) # přidání prvku na konec seznamus.pop() # odebrání prvku z konce seznamus + t # zřetězení seznamůs * 3 # opakování (pozor, může být zákeřné)
s[0] # přístup k prvnímu prvkus[2] = "ABC" # seznamy můžeme modifikovat!s[-1] # indexování od konces[1:4] # kopie části seznamus[1:5:2] # podobně jako u řetězcůs[:] # kopie celého seznamu (k čemu to je?)
list(x) # konverze na seznam (uvidíme později)
15
PROCHÁZENÍ SEZNAMU
• mám seznam my_list a chci vypsat všechny jeho prvky, jak?
• ne úplně vhodné řešení:
for i in range(len(my_list)):print(my_list[i])
• lepší řešení:
for element in my_list:print(element)
• podobnou notaci má většina moderních jazyků
• range(...) je něco jako seznam• (kvůli efektivitě to není seznam, ale tzv. generátor)• seznam můžeme vyrobit konvertováním list(range(...))
16
ODBOČKA – PROMĚNNÉ V PYTHONU
Proměnné v různých programovacích jazycích
• pojmenované místo v paměti• odkaz na místo v paměti (Python)• kombinace obou přístupů
Přiřazení v různých jazycích
• proměnné ve stylu C (Pascal): změna obsahu paměti• proměnné ve stylu Pythonu: změna (přesměrování) odkazu najiné místo v paměti
Poznámka: U neměnných (immutable) typů (čísla, řetězce)nepozorujeme žádný rozdíl.
17
ODBOČKA – PROMĚNNÉ V PYTHONU
Ilustrace přiřazení
Jazyk CProměnné jako místa v paměti
int a, b;a = 1;
a = 2;
b = a;
a 1
b
a 2
b
a 2
b 2
Jazyk PythonProměnné jako odkazy
a = 1
a = 2
b = a
a 1
a 1
2
a
b
1
2
18
SEZNAMY A PROMĚNNÉ V PYTHONU
s = [1, 2, 3]t = ss.append(4)print(t)
Co bude výstupem? [1, 2, 3, 4]
• proč?• protože proměnné v Pythonu jsou odkazy• http://www.pythontutor.com
Už víme, k čemu je dobré s[:]?
• pokud potřebujeme vytvořit novou kopii seznamu(nezávislou na původním seznamu)
19
http://www.pythontutor.coms = [1, 2, 3]t = ss.append(4)print(t)
SEZNAMY A VOLÁNÍ FUNKCÍ V PYTHONU
def fun(x):x = 17
y = 10fun(y)print(y)
Co bude výstupem? 10
• vypadá to, že funkce v Pythonu nemění hodnotu parametru(čísla jsou v Pythonu neměnná)
20
def fun(x): x = 17
y = 10fun(y)print(y)
SEZNAMY A VOLÁNÍ FUNKCÍ V PYTHONU
def fun(s):s.append(17)
t = [1, 2]fun(t)print(t)
Co bude výstupem? [1, 2, 17]
• vypadá to, že funkce v Pythonu mění hodnotu parametru(seznamy jsou v Pythonu měnitelné)
21
def fun(s): s.append(17)
t = [1, 2]fun(t)print(t)
SEZNAMY A VOLÁNÍ FUNKCÍ V PYTHONU
def fun(s):s.append(17)s = [4, 5]s.append(19)
t = [1, 2]fun(t)print(t)
Co bude výstupem? [1, 2, 17]
• proč?• přiřazení je změna odkazu• po provedení s = [4, 5] už s neukazuje na původní seznam• http://www.pythontutor.com
(vrátíme se k tomu v některé z dalších přednášek) 22
http://www.pythontutor.comdef fun(s): s.append(17) s = [4, 5] s.append(19)
t = [1, 2]fun(t)print(t)
SEZNAMY A PARAMETRY S IMPLICITNÍ HODNOTOU
• poněkud nepříjemná vlastnost Pythonu
def fun(my_list=[]):my_list.append(1)print(my_list)
• zkuste si opakovaně zavolat fun()
• pointa: nepoužívejte parametry s implicitními hodnotami, pokudjsou měnitelných typů (zatím známe jen seznamy)
23
def fun(my_list=[]): my_list.append(1) print(my_list)
fun()fun()fun([1, 2, 3])fun()
# try running pylint on this file
VZTAH ŘETĚZCŮ A SEZNAMŮ
• split – vytvoření seznamu z řetězce
vowels = "a, e, i, o, u"vowel_list = vowels.split(", ")
message = ".-|....|---|.---"letters = message.split("|")
• join – vytvoření řetězce ze seznamu
data = ["liberté", "égalité", "fraternité"]result = ", ".join(data)
24
data = ["liberté", "égalité", "fraternité"]result = ", ".join(data)
print(result)
vowels = "a, e, i, o, u"vowel_list = vowels.split(", ")
message = ".-|....|---|.---"letters = message.split("|")
print(vowel_list)print(letters)
NTICE (TUPLES)
• neměnná varianta seznamů• podobně jako řetězce
• v Pythonu fungují jako seznamy, ale nedají se měnit• zápis: kulaté závorky místo hranatých
• v jistých situacích se kulaté závorky smí vynechat
s = (1, "A", 3)print(s[0]) # OK# s[0] = "B" # error
• typická použití – jednoduchá strukturovaná data• souřadnice (x, y)• barva pixelu (red, green, blue)• vracení více hodnot z funkce, …
• ntice velikosti 1: (x,)
25
s = (1, "A", 3)print(s[0]) # OK# s[0] = "B" # error
data = ("Rick Sanchez", "C317", "Earth")name, dimension, planet = data
a = 10b = 17# we could also write: a, b = 10, 17
a, b = b, a # the same as: (a, b) = (b, a)
print(a, b)
def minmax(a, b): return min(a, b), max(a, b)
x, y = minmax(9.7, 3.14)print(x, y)
quot, rem = divmod(17, 5) # standard Python functionprint(quot, rem)
TYPICKÁ POUŽITÍ NTIC
• rozbalení ntice (funguje i se seznamy)
data = ("Rick Sanchez", "C317", "Earth")name, dimension, planet = data
• prohození hodnot proměnných (swap)
a, b = b, a # the same as: (a, b) = (b, a)
• vracení více hodnot z funkce
def minmax(a, b):return min(a, b), max(a, b)
x, y = minmax(9.7, 3.14)
quot, rem = divmod(17, 5) # standard Python function26
s = (1, "A", 3)print(s[0]) # OK# s[0] = "B" # error
data = ("Rick Sanchez", "C317", "Earth")name, dimension, planet = data
a = 10b = 17# we could also write: a, b = 10, 17
a, b = b, a # the same as: (a, b) = (b, a)
print(a, b)
def minmax(a, b): return min(a, b), max(a, b)
x, y = minmax(9.7, 3.14)print(x, y)
quot, rem = divmod(17, 5) # standard Python functionprint(quot, rem)
ŘEŠENÉ PŘÍKLADY
VÝPOČET PRŮMĚRU
def average1(data):total = 0for i in range(len(data)):
total += data[i]return total / len(data)
def average2(data):total = 0for element in data:
total += elementreturn total / len(data)
def average3(data):return sum(data) / len(data)
• pointa? není nutné vynalézat kolo(i když se samozřejmě hodí vědět, co se vlastně děje) 27
def average1(data): total = 0 for i in range(len(data)): total += data[i] return total / len(data)
def average2(data): total = 0 for element in data: total += element return total / len(data)
def average3(data): return sum(data) / len(data)
s = [29, 998, 559, 671, 349]
print(average1(s))print(average2(s))print(average3(s))
DĚLITELÉ ČÍSLA
def divisors(num):result = []for divisor in range(1, num + 1):
if num % divisor == 0:result.append(divisor)
return result
• dalo by se nějak vylepšit?• počítat jen do num // 2 (a přidat num na konec)• využít toho, že divisor a num // divisor jsou oba dělitelé• menší z těchto dvou dělitelů je ≤ odmocnině z num(zkuste si do příště naprogramovat)
• pro zajímavost (použití intenzionálních seznamů):
def divisors2(num):return [divisor for divisor in range(1, num + 1)
if num % divisor == 0] 28
def divisors(num): result = [] for divisor in range(1, num + 1): if num % divisor == 0: result.append(divisor) return result
def divisors2(num): return [divisor for divisor in range(1, num + 1) if num % divisor == 0]
print(divisors(72525))
PŘÍKLAD: SIMULACE PŘEDVOLEBNÍHO PRŮZKUMU
def survey(size, preferences):parties = len(preferences)count = [0] * partiesfor i in range(size):
r = random.randint(1, 100)threshold = 0for p in range(parties):
threshold += preferences[p]if r
PŘÍKLAD: FREKVENČNÍ ANALÝZA
def frequency_analysis(text):text = text.upper()freq = [0 for i in range(26)]for letter in text:
if "A"
PŘÍKLAD: PŘEVOD DO MORSEOVKY
MORSE = (".-", "-...", "-.-.", "-..") # etc.
def to_morse(text):text = text.upper()result = ""for letter in text:
if "A"
PŘÍKLAD: PRVOČÍSLA – ERATOSTHENOVO SÍTO
def sieve(limit):result = []is_prime = [True] * limitfor p in range(2, limit):
if is_prime[p]:result.append(p)for mult in range(p * p, limit, p):
is_prime[mult] = Falsereturn result
sum_primes = sum(sieve(1000000))
print("Součet všech prvočísel menších než milion je {}.".format(sum_primes))
32
def sieve(limit): result = [] is_prime = [True] * limit for p in range(2, limit): if is_prime[p]: result.append(p) for mult in range(p * p, limit, p): is_prime[mult] = False return result
sum_primes = sum(sieve(1000000))
print("Součet všech prvočísel menších než milion je {}." .format(sum_primes))
PŘÍKLAD: VÝŠKOVÝ PROFIL
33
PŘÍKLAD: VÝŠKOVÝ PROFIL
def height_profile(heights):for level in range(max(heights), 0, -1):
for height in heights:if height >= level:
print("# ", end="")else:
print(" ", end="")print()
34
def height_profile(heights): for level in range(max(heights), 0, -1): for height in heights: if height >= level: print("# ", end="") else: print(" ", end="") print()
def elevation(heights): ascent, descent = 0, 0 for i in range(len(heights) - 1): diff = heights[i + 1] - heights[i] if diff > 0: ascent += diff else: descent += -diff print("Total ascent:", ascent) print("Total descent:", descent)
trip = [10, 9, 7, 5, 3, 2, 2, 3, 4, 6, 7, 8, 11, 10, 8, 7, 6, 5, 5, 6]height_profile(trip)elevation(trip)
PŘÍKLAD: VÝŠKOVÝ PROFIL
def elevation(heights):ascent, descent = 0, 0for i in range(len(heights) - 1):
diff = heights[i + 1] - heights[i]if diff > 0:
ascent += diffelse:
descent += -diffprint("Total ascent:", ascent)print("Total descent:", descent)
35
def height_profile(heights): for level in range(max(heights), 0, -1): for height in heights: if height >= level: print("# ", end="") else: print(" ", end="") print()
def elevation(heights): ascent, descent = 0, 0 for i in range(len(heights) - 1): diff = heights[i + 1] - heights[i] if diff > 0: ascent += diff else: descent += -diff print("Total ascent:", ascent) print("Total descent:", descent)
trip = [10, 9, 7, 5, 3, 2, 2, 3, 4, 6, 7, 8, 11, 10, 8, 7, 6, 5, 5, 6]height_profile(trip)elevation(trip)
ŘetězceSeznamy, nticeŘešené příklady