Obsah
1. Test, jaké hodnoty jsou uloženy ve dvojici s jejich vzájemným porovnáním
2. Vzorek odpovídající libovolnému řetězci
3. Výsledek transpřekladu se vzorkem odpovídajícím libovolnému řetězci
4. Vzorek obsahující řetězec, zástupný symbol a další řetězec
5. Výsledek transpřekladu vzorku s textovým literálem následovaným zástupným symbolem
6. Několik vzorků obsahujících řetězec, zástupný symbol a další řetězec
7. Jediná větev case s několika vzorky spojenými operátorem |
8. Test, zda je hodnota instancí nějaké třídy
9. Vzorek s testem atributů objektu, včetně podrobnější podmínky
10. Test atributů objektu s dodatečnou podmínkou jejich existence
11. Strukturální pattern matching se slovníky
12. Vzorky založené na seznamu
13. Vzorek se seznamem se známou hlavou (head)
14. Výsledek transpřekladu se vzorkem obsahujícím seznam se známou hlavou
15. Vzorek se seznamem se známým koncem (tail)
17. Vzorek se známým začátkem i koncem seznamu
19. Repositář s demonstračními příklady
1. Test, jaké hodnoty jsou uloženy ve dvojici s jejich vzájemným porovnáním
V předchozím článku jsme si kromě dalších vzorků ukázali i skript, který po svém spuštění zjistí, kolik prvků obsahuje seznam, který byl předán nějaké funkci. Navíc skript zjistil i to, jaké jsou hodnoty těchto prvků a zda se vůbec jedná o seznam. Ovšem ve skutečnosti mohou být vzorky zapsány i složitějším způsobem. Podívejme se na následující příklad, který dokáže zjistit a následně porovnat hodnoty prvků ve dvojici. Prvky buď mohou být totožné (viz první vzorek, v němž se opakuje zástupný symbol x), nebo může být první prvek větší, než prvek druhý, popř. naopak. Realizace takového testu vypadá následovně:
def pair(p): match p: case [x,x]: return "same values!" case [x,y] if x>y: return "1st value is greater" case [x,y]: return "2nd value is greater" else: return "other" pair([1, 1]) |> print pair([1, 2]) |> print pair([2, 1]) |> print
Výsledky získané po spuštění tohoto skriptu dobře ukazují jeho chování:
same values! 2nd value is greater 1st value is greater
2. Vzorek odpovídající libovolnému řetězci
Strukturální pattern matching realizovaný v programovacím jazyce Coconut umožňuje, aby se ve vzorku nacházely i řetězce, popř. řetězce spojené se zástupnými symboly (placeholdery). To je v ekosystému Pythonu relativní novinka, protože samotný programovací jazyk Python zápis takových vzorků nepodporuje (což je škoda). Ukažme si ovšem nejprve velmi jednoduchý příklad se vzorkem, který odpovídá libovolné hodnotě (teprve později se přiblížíme ke komplikovanějším vzorkům):
def say_hello(name): match name: case x: print(f"Hello {x}!") say_hello("Marvin")
Po spuštění se vypíše očekávaná zpráva:
Hello Marvin!
3. Výsledek transpřekladu se vzorkem odpovídajícím libovolnému řetězci
Tento skript bude (trans)přeložen do Pythonu následujícím způsobem. Povšimněte si, že se nejprve připraví pomocné proměnné naplňované při testu jednotlivých vzorků a posléze se pouze provede test s hodnotou True, která je konstantně přiřazena do druhé proměnné. Takto mechanicky jsou překládány všechny vzorky, což vede k poměrně dlouhému výslednému kódu (někdy o délce několika kilobajtů):
# Compiled Coconut: ----------------------------------------------------------- def say_hello(name): #1 (line in Coconut source) _coconut_case_match_to_0 = name #2 (line in Coconut source) _coconut_case_match_check_0 = False #2 (line in Coconut source) _coconut_match_set_name_x = _coconut_sentinel #2 (line in Coconut source) _coconut_match_set_name_x = _coconut_case_match_to_0 #2 (line in Coconut source) _coconut_case_match_check_0 = True #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) if _coconut_match_set_name_x is not _coconut_sentinel: #2 (line in Coconut source) x = _coconut_match_set_name_x #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) print("Hello {_coconut_format_0}!".format(_coconut_format_0=(x))) #4 (line in Coconut source) say_hello("Marvin") #7 (line in Coconut source)
4. Vzorek obsahující řetězec, zástupný symbol a další řetězec
Skript ze druhé kapitoly byl pouze jakousi ochutnávkou mnohem sofistikovanějších vzorků. Programovací jazyk Coconut totiž umožňuje, aby vzorek vypadat i takto:
- řetězcový literál + zástupný symbol
- zástupný symbol + řetězcový literál
- řetězcový literál + zástupný symbol + řetězcový literál
V praxi to znamená, že v Coconutu není zapotřebí zneužívat regulární výrazy jen proto, abychom například zjistili, jaké jméno je použito ve větě „My name is Marvin.“ (včetně oné tečky na konci). Můžeme totiž zjištění jména ponechat na technologii pattern matchingu a program přepsat do podoby:
def say_hello(about_me): match about_me: case "My name is " + name + ".": print(f"Hello {name}!") say_hello("My name is Marvin.")
Podívejme se na výsledek získaný po spuštění tohoto skriptu, abychom zkontrolovali korektnost provedení pattern matchingu:
Hello Marvin!
5. Výsledek transpřekladu vzorku s textovým literálem následovaným zástupným symbolem
Zajímavé bude zjistit, jakým způsobem se vlastně předchozí skript (trans)přeloží do Pythonu, protože to naznačuje, jak bychom mohli tento problém řešit ručně. Povšimněte si, že Coconut detekoval zástupný symbol uprostřed řetězce a využil tedy metody startswith a endwith. Taktéž využil řezy řetězcem (tj. zápis řetězec[od:do]):
# Compiled Coconut: ----------------------------------------------------------- def say_hello(about_me): #1 (line in Coconut source) _coconut_case_match_to_0 = about_me #2 (line in Coconut source) _coconut_case_match_check_0 = False #2 (line in Coconut source) _coconut_match_set_name_name = _coconut_sentinel #2 (line in Coconut source) if (_coconut.isinstance(_coconut_case_match_to_0, _coconut.str)) and (_coconut.len(_coconut_case_match_to_0) >= 12) and (_coconut_case_match_to_0.startswith("My name is ")) and (_coconut_case_match_to_0.endswith(".")): #2 (line in Coconut source) _coconut_match_temp_0 = _coconut_case_match_to_0[11:-1] #2 (line in Coconut source) _coconut_match_set_name_name = _coconut_match_temp_0 #2 (line in Coconut source) _coconut_case_match_check_0 = True #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) if _coconut_match_set_name_name is not _coconut_sentinel: #2 (line in Coconut source) name = _coconut_match_set_name_name #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) print("Hello {_coconut_format_0}!".format(_coconut_format_0=(name))) #4 (line in Coconut source) say_hello("My name is Marvin.") #7 (line in Coconut source)
6. Několik vzorků obsahujících řetězec, zástupný symbol a další řetězec
Pochopitelně nám nic nebrání v přidání několika dalších vzorků založených na testování, jaké „pevné“ a „proměnné/zástupné“ části řetězec obsahuje. Například můžeme předchozí demonstrační příklad rozšířit o získání jmen z několika různých oznamovacích vět. Řešení založené na pattern matchingu je stále poměrně přímočaré a dobře čitelné:
def say_hello(about_me): match about_me: case "My name is " + name + ".": print(f"Hello {name}!") case "His name is " + name + ".": print(f"Hello {name}!") case "He is named " + name + ".": print(f"Hello {name}!") case _: print("Sorry, I don't know your name") say_hello("My name is Marvin.") say_hello("His name is Marvin.") say_hello("He is named Marvin.") say_hello("Its name is Marvin.")
Opět si vše otestujme:
Hello Marvin! Hello Marvin! Hello Marvin! Sorry, I don't know your name
7. Jediná větev case s několika vzorky spojenými operátorem |
Stejný algoritmus ovšem můžeme zapsat i odlišně, konkrétně s využitím znaku | ve vzorku. Takový vzorek je možné rozdělit na více řádků, ovšem musíme zde použít znak \ na konci, takže je otázkou, jestli se jedná o dobré řešení či nikoli (v tomto případě je asi vhodné, protože se nebude opakovat příkaz zapsaný přímo ve větvi):
def say_hello(about_me): match about_me: case "My name is " + name + "."| "His name is " + name + "." | "He is named " + name + ".": print(f"Hello {name}!") case _: print("Sorry, I don't know your name") say_hello("My name is Marvin.") say_hello("His name is Marvin.") say_hello("He is named Marvin.") say_hello("Its name is Marvin.")
Výsledek by měl být stejný, jako tomu bylo v předchozím demonstračním příkladu:
Hello Marvin! Hello Marvin! Hello Marvin! Sorry, I don't know your name
8. Test, zda je hodnota instancí nějaké třídy
V předchozím článku jsem si ukazovali i několik příkladů, v nichž byly použity n-tice nebo seznamy pro reprezentaci komplexních čísel. V těchto příkladech jsme rozhodovali o tom, zda je komplexní číslo nulové, zda obsahuje nulovou reálnou složku atd. Ovšem v praxi se spíše setkáme s tím, že bude komplexní číslo reprezentováno objektem, konkrétně instancí třídy Complex apod. (jedná se opět o školní příklad, protože Python obsahuje podporu pro komplexní čísla jako plnohodnotných základních datových typů).
Díky existenci strukturálního pattern matchingu však můžeme zapsat vzorky i pro objekty, konkrétně pro otestování, jaké jsou hodnoty atributů těchto objektů. Nebo se může jednat o jednoduchý test, jestli je vůbec hodnota typu Complex, přesněji řečeno instance této třídy (bez ohledu na hodnoty atributů tohoto objektu). Pokusme se tedy skripty z předchozího článku přepsat takovým způsobem, že se budou testovat i vlastnosti komplexního čísla typu Complex předaného do funkce test_number. Nejprve přidáme test, jestli je předaná hodnota typu Complex, v dalším příkladu pak přidáme i testy na atributy. Povšimněte si, že ve skriptu je obsažena podpora i pro původní reprezentaci komplexních čísel jako dvojice hodnot:
class Complex(): def __init__(self, real, imag): self.real = real self.imag = imag def __str__(self): return f"Complex number {self.real}+i{self.imag} represented as object" def test_number(value): match value: case (0, 0): print("Zero") case (real, 0) if real>0: print(f"Positive real number {real}") case (real, 0): print(f"Negative real number {real}") case (0, imag) if imag<0: print(f"Negative imaginary number {imag}") case (0, imag): print(f"Negative imaginary number {imag}") case (real, imag): print(f"Complex number {real}+i{imag}") case Complex(): print(value) case _: raise ValueError("Not a complex number") test_number((0,0)) test_number((1,0)) test_number((-1,0)) test_number((0,1)) test_number((0,-1)) test_number((1,1)) test_number(Complex(0,0)) test_number(Complex(1,0)) test_number(Complex(-1,0)) test_number(Complex(0,1)) test_number(Complex(0,-1)) test_number(Complex(1,1))
Vše si otestujeme spuštěním skriptu:
Zero Positive real number 1 Negative real number -1 Negative imaginary number 1 Negative imaginary number -1 Complex number 1+i1 Complex number 0+i0 represented as object Complex number 1+i0 represented as object Complex number -1+i0 represented as object Complex number 0+i1 represented as object Complex number 0+i-1 represented as object Complex number 1+i1 represented as object
9. Vzorek s testem atributů objektu, včetně podrobnější podmínky
V předchozím skriptu jsme testovali, jestli je předávaná hodnota typu Complex (tj. instancí této třídy). Jednalo se o následující vzorek:
case Complex(): print(value)
Ovšem relativně snadno můžeme přidat i test nejenom na typ hodnoty, ale i na konkrétní vlastnosti atributů tohoto objektu. Příkladem může být test na nulové komplexní číslo (tj. na „komplexní nulu“). Tento zápis vypadá následovně a je v této podobě kompatibilní i s jazykem Python:
case Complex(real=0, imag=0): print("Zero complex represented as object")
Jednoduše tedy můžeme přidat tento další test, který musí být umístěn za obecnějším testem, do skriptu, jenž získá následující podobu:
class Complex(): def __init__(self, real, imag): self.real = real self.imag = imag def __str__(self): return f"Complex number {self.real}+i{self.imag} represented as object" def test_number(value): match value: case (0, 0): print("Zero") case (real, 0) if real>0: print(f"Positive real number {real}") case (real, 0): print(f"Negative real number {real}") case (0, imag) if imag<0: print(f"Negative imaginary number {imag}") case (0, imag): print(f"Negative imaginary number {imag}") case (real, imag): print(f"Complex number {real}+i{imag}") case Complex(real=0, imag=0): print("Zero complex represented as object") case Complex(): print(value) case _: raise ValueError("Not a complex number") test_number((0,0)) test_number((1,0)) test_number((-1,0)) test_number((0,1)) test_number((0,-1)) test_number((1,1)) test_number(Complex(0,0)) test_number(Complex(1,0)) test_number(Complex(-1,0)) test_number(Complex(0,1)) test_number(Complex(0,-1)) test_number(Complex(1,1))
Následují výsledky s otestováním vlastností nové větve se vzorkem:
Zero Positive real number 1 Negative real number -1 Negative imaginary number 1 Negative imaginary number -1 Complex number 1+i1 Zero complex represented as object Complex number 1+i0 represented as object Complex number -1+i0 represented as object Complex number 0+i1 represented as object Complex number 0+i-1 represented as object Complex number 1+i1 represented as object
10. Test atributů objektu s dodatečnou podmínkou jejich existence
V některých situacích je vhodné zjistit, jestli objekt skutečně obsahuje zvolené atributy (protože netřídní atributy jsou v Pythonu vytvářeny dynamicky a není zaručena jejich existence ve všech případech). Vzorek s testem existence (a hodnoty) atributu musí před názvem atributu obsahovat tečku. Takový vzorek je unikátní pro jazyk Coconut a nelze ho použít přímo v Pythonu:
case Complex(.real=0, .imag=0): print("Zero complex represented as object")
Doplňme si tedy náš skript pro rozpoznávání různých variant komplexních čísel ještě o výše uvedenou větev s novým typem vzorku. Finální varianta bude vypadat následovně:
class Complex(): def __init__(self, real, imag): self.real = real self.imag = imag def __str__(self): return f"Complex number {self.real}+i{self.imag} represented as object" def test_number(value): match value: case (0, 0): print("Zero") case (real, 0) if real>0: print(f"Positive real number {real}") case (real, 0): print(f"Negative real number {real}") case (0, imag) if imag<0: print(f"Negative imaginary number {imag}") case (0, imag): print(f"Negative imaginary number {imag}") case (real, imag): print(f"Complex number {real}+i{imag}") case Complex(.real=0, .imag=0): print("Zero complex represented as object") case Complex(): print(value) case _: raise ValueError("Not a complex number") test_number((0,0)) test_number((1,0)) test_number((-1,0)) test_number((0,1)) test_number((0,-1)) test_number((1,1)) test_number(Complex(0,0)) test_number(Complex(1,0)) test_number(Complex(-1,0)) test_number(Complex(0,1)) test_number(Complex(0,-1)) test_number(Complex(1,1))
Vše si otestujme:
Zero Positive real number 1 Negative real number -1 Negative imaginary number 1 Negative imaginary number -1 Complex number 1+i1 Zero complex represented as object Complex number 1+i0 represented as object Complex number -1+i0 represented as object Complex number 0+i1 represented as object Complex number 0+i-1 represented as object Complex number 1+i1 represented as object
11. Strukturální pattern matching se slovníky
Prozatím jsme si ukázali zhruba jen polovinu typů strukturálních vzorků. Víme již, jak zjistit (a popř. zachytit) hodnoty prvků seznamu. Ovšem prakticky stejným způsobem lze pracovat i s množinami a se slovníky. V následujícím skriptu jsou ukázány vzorky, které nám umožní zjistit, zda předaný slovník obsahuje hodnoty uložené pod zadanými klíči. Jedná se o (zjednodušenou) implementaci funkci pro login, které se předává buď slovník se jménem a příjmením uživatele, nebo slovník s jeho vygenerovaným ID. Na obě možnosti, které mohou nastat, je možné reagovat následujícím způsobem. Povšimněte si, že (podle očekávání) dokážeme zachytit jméno a příjmení, popř. ID do zástupných symbolů x a y, popř. pouze do symbolu x:
def login(value): match value: case {"name": x, "surname": y}: print(f"User identified by name {x} and surname {y}") case {"id": x}: print(f"User identified by ID {x}") else: print("Unable to identify user") login({"name": "John", "surname": "Joe"}) login({"id": 42}) login({"id": 42, "name": "John"}) login({"id": 42, "name": "John", "surname": "Joe"}) login({"foo": "bar"})
Ukázka, jak tento skript pracuje:
User identified by name John and surname Joe User identified by ID 42 User identified by ID 42 User identified by name John and surname Joe Unable to identify user
12. Vzorky založené na seznamu
Poměrně často se setkáme se situací, kdy je nutné zjistit, jestli nějaká n-tice nebo seznam začíná, popř. zda končí nějakou sekvencí hodnot. A pokud n-tice, resp. seznam má odpovídající začátek nebo konec, budeme chtít uložit zbytek sekvence do zástupného symbolu a dále ho nějakým způsobem zpracovat. I tohoto chování je pochopitelně možné v jazyku Coconut dosáhnout, na rozdíl od Pythonu, kde to alespoň prozatím možné není.
13. Vzorek se seznamem se známou hlavou (head)
V dalším demonstračním příkladu je ukázán vzorek, který odpovídá seznamu začínajícího třemi prvky se známou hodnotou 1, 2 a 3. V případě, že seznam obsahuje více hodnot, budou tyto hodnoty uloženy do zástupného symbolu x, což je vlastně chování velmi podobné vzorkům s řetězci:
def list_type(value): match value: case [1, 2, 3] + x: print(f"List starting with expected head, followed by item(s) = {x}") else: print(f"Unexpected value {value}") [] |> list_type [1] |> list_type [1, 2] |> list_type [1, 2, 3] |> list_type [1, 2, 3, 4] |> list_type [1, 2, 3, 4, 5] |> list_type [2, 2, 3, 4, 5] |> list_type
Pokusme se tento skript spustit a zjistit, jakým způsobem rozpozná či naopak nerozpozná vzor pro seznamy různé délky a obsahu:
Unexpected value [] Unexpected value [1] Unexpected value [1, 2] List starting with expected head, followed by item(s) = [] List starting with expected head, followed by item(s) = [4] List starting with expected head, followed by item(s) = [4, 5] Unexpected value [2, 2, 3, 4, 5]
Povšimněte si, že do zástupného symbolu x je vždy uložen zbytek seznamu, kromě známé a ve vzorku specifikované „hlavy“ seznamu.
14. Výsledek transpřekladu se vzorkem obsahujícím seznam se známou hlavou
Použití podobných vzorků, jakým je i vzorek z předchozí kapitoly, může být v praxi velmi užitečné, takže se podívejme na to, jak je vlastně celá funkce přeložena do Pythonu:
# Compiled Coconut: ----------------------------------------------------------- def list_type(value): #1 (line in Coconut source) _coconut_case_match_to_0 = value #2 (line in Coconut source) _coconut_case_match_check_0 = False #2 (line in Coconut source) _coconut_match_set_name_x = _coconut_sentinel #2 (line in Coconut source) if (_coconut.isinstance(_coconut_case_match_to_0, _coconut.abc.Sequence)) and (_coconut.len(_coconut_case_match_to_0) >= 3) and (_coconut_case_match_to_0[0] == 1) and (_coconut_case_match_to_0[1] == 2) and (_coconut_case_match_to_0[2] == 3): #2 (line in Coconut source) _coconut_match_temp_0 = _coconut.list(_coconut_case_match_to_0[3:]) #2 (line in Coconut source) _coconut_match_set_name_x = _coconut_match_temp_0 #2 (line in Coconut source) _coconut_case_match_check_0 = True #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) if _coconut_match_set_name_x is not _coconut_sentinel: #2 (line in Coconut source) x = _coconut_match_set_name_x #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) print("List starting with expected head, followed by item(s) = {_coconut_format_0}".format(_coconut_format_0=(x))) #4 (line in Coconut source) if not _coconut_case_match_check_0: #5 (line in Coconut source) print("Unexpected value {_coconut_format_0}".format(_coconut_format_0=(value))) #6 (line in Coconut source) (list_type)([]) #9 (line in Coconut source) (list_type)([1,]) #10 (line in Coconut source) (list_type)([1, 2]) #11 (line in Coconut source) (list_type)([1, 2, 3]) #12 (line in Coconut source) (list_type)([1, 2, 3, 4]) #13 (line in Coconut source) (list_type)([1, 2, 3, 4, 5]) #14 (line in Coconut source) (list_type)([2, 2, 3, 4, 5]) #15 (line in Coconut source)
15. Vzorek se seznamem se známým koncem (tail)
Podobným způsobem je možné napsat funkci se vzorkem, který rozpozná, jestli seznam končí trojicí prvků [7, 8, 9]. Začátek takového seznamu (obecně n prvků nebo i prázdný seznam) je v takovém případě vložen do zástupného symbolu nazvaného x:
def list_type(value): match value: case x + [7, 8, 9]: print(f"List ending with expected tail, preceded by item(s) = {x}") else: print(f"Unexpected value {value}") [8, 9] |> list_type [7, 8, 9] |> list_type [6, 7, 8, 9] |> list_type [5, 6, 7, 8, 9] |> list_type [4, 5, 6, 7, 8, 9] |> list_type [3, 4, 5, 6, 7, 8, 9] |> list_type [2, 3, 4, 5, 6, 7, 8, 9] |> list_type [1, 2, 3, 4, 5, 6, 7, 8, 9] |> list_type [1, 2, 3, 4, 5, 6, 7, 8, 1] |> list_type
Otestujme si tento skript, a to opět pro seznamy s různým obsahem:
Unexpected value [8, 9] List ending with expected tail, preceded by item(s) = [] List ending with expected tail, preceded by item(s) = [6] List ending with expected tail, preceded by item(s) = [5, 6] List ending with expected tail, preceded by item(s) = [4, 5, 6] List ending with expected tail, preceded by item(s) = [3, 4, 5, 6] List ending with expected tail, preceded by item(s) = [2, 3, 4, 5, 6] List ending with expected tail, preceded by item(s) = [1, 2, 3, 4, 5, 6] Unexpected value [1, 2, 3, 4, 5, 6, 7, 8, 1]
16. Výsledek transpřekladu
V (trans)přeloženém kódu opět můžeme vidět operace řezu (slice), tentokrát však se zápornými indexy, tj. s indexy, které se počítají od konce seznamu či jiné sekvence:
# Compiled Coconut: ----------------------------------------------------------- def list_type(value): #1 (line in Coconut source) _coconut_case_match_to_0 = value #2 (line in Coconut source) _coconut_case_match_check_0 = False #2 (line in Coconut source) _coconut_match_set_name_x = _coconut_sentinel #2 (line in Coconut source) if (_coconut.isinstance(_coconut_case_match_to_0, _coconut.abc.Sequence)) and (_coconut.len(_coconut_case_match_to_0) >= 3) and (_coconut_case_match_to_0[-3] == 7) and (_coconut_case_match_to_0[-2] == 8) and (_coconut_case_match_to_0[-1] == 9): #2 (line in Coconut source) _coconut_match_temp_0 = _coconut.list(_coconut_case_match_to_0[:-3]) #2 (line in Coconut source) _coconut_match_set_name_x = _coconut_match_temp_0 #2 (line in Coconut source) _coconut_case_match_check_0 = True #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) if _coconut_match_set_name_x is not _coconut_sentinel: #2 (line in Coconut source) x = _coconut_match_set_name_x #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) print("List ending with expected tail, preceded by item(s) = {_coconut_format_0}".format(_coconut_format_0=(x))) #4 (line in Coconut source) if not _coconut_case_match_check_0: #5 (line in Coconut source) print("Unexpected value {_coconut_format_0}".format(_coconut_format_0=(value))) #6 (line in Coconut source) (list_type)([8, 9]) #9 (line in Coconut source) (list_type)([7, 8, 9]) #10 (line in Coconut source) (list_type)([6, 7, 8, 9]) #11 (line in Coconut source) (list_type)([5, 6, 7, 8, 9]) #12 (line in Coconut source) (list_type)([4, 5, 6, 7, 8, 9]) #13 (line in Coconut source) (list_type)([3, 4, 5, 6, 7, 8, 9]) #14 (line in Coconut source) (list_type)([2, 3, 4, 5, 6, 7, 8, 9]) #15 (line in Coconut source) (list_type)([1, 2, 3, 4, 5, 6, 7, 8, 9]) #16 (line in Coconut source) (list_type)([1, 2, 3, 4, 5, 6, 7, 8, 1]) #17 (line in Coconut source)
17. Vzorek se známým začátkem i koncem seznamu
Dostáváme se k dnešnímu poslednímu příkladu. V něm je použit vzorek se seznamem, u kterého jsou známé prvky jak na jeho začátku, tak i na jeho konci. I takové vzorky nám Coconut umožňuje specifikovat, pochopitelně s tím, že „prostředek“ seznamu se opět uloží do zvoleného zástupného symbolu. A navíc je možné přidat i doplňkový test, pokud to bude zapotřebí:
def list_type(value): match value: case [1, 2, 3] + x + [7, 8, 9]: print(f"Items between head and tail: {x}") else: print(f"Unexpected value {value}") [1, 2, 3, 7, 8, 9] |> list_type [1, 2, 3, 6, 7, 8, 9] |> list_type [1, 2, 3, 5, 6, 7, 8, 9] |> list_type [1, 2, 3, 4, 5, 6, 7, 8, 9] |> list_type [9, 2, 3, 4, 5, 6, 7, 8, 9] |> list_type [1, 2, 3, 4, 5, 6, 7, 8, 1] |> list_type
Items between head and tail: [] Items between head and tail: [6] Items between head and tail: [5, 6] Items between head and tail: [4, 5, 6] Unexpected value [9, 2, 3, 4, 5, 6, 7, 8, 9] Unexpected value [1, 2, 3, 4, 5, 6, 7, 8, 1]
18. Výsledek transpřekladu
Již naposledy se podívejme na to, jak vypadá výsledek (trans)překladu skriptu ze sedmnácté kapitoly. Uvidíme zde poměrně otrockou kontrolu prvních a posledních prvků v seznamu. Tuto část by pravděpodobně bylo možné napsat lépe (opět s využitím řezů porovnávaných s konstantní sekvencí atd.):
# Compiled Coconut: ----------------------------------------------------------- def list_type(value): #1 (line in Coconut source) _coconut_case_match_to_0 = value #2 (line in Coconut source) _coconut_case_match_check_0 = False #2 (line in Coconut source) _coconut_match_set_name_x = _coconut_sentinel #2 (line in Coconut source) if (_coconut.isinstance(_coconut_case_match_to_0, _coconut.abc.Sequence)) and (_coconut.len(_coconut_case_match_to_0) >= 6) and (_coconut_case_match_to_0[0] == 1) and (_coconut_case_match_to_0[1] == 2) and (_coconut_case_match_to_0[2] == 3) and (_coconut_case_match_to_0[-3] == 7) and (_coconut_case_match_to_0[-2] == 8) and (_coconut_case_match_to_0[-1] == 9): #2 (line in Coconut source) _coconut_match_temp_0 = _coconut.list(_coconut_case_match_to_0[3:-3]) #2 (line in Coconut source) _coconut_match_set_name_x = _coconut_match_temp_0 #2 (line in Coconut source) _coconut_case_match_check_0 = True #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) if _coconut_match_set_name_x is not _coconut_sentinel: #2 (line in Coconut source) x = _coconut_match_set_name_x #2 (line in Coconut source) if _coconut_case_match_check_0: #2 (line in Coconut source) print("Items between head and tail: {_coconut_format_0}".format(_coconut_format_0=(x))) #4 (line in Coconut source) if not _coconut_case_match_check_0: #5 (line in Coconut source) print("Unexpected value {_coconut_format_0}".format(_coconut_format_0=(value))) #6 (line in Coconut source) (list_type)([1, 2, 3, 7, 8, 9]) #9 (line in Coconut source) (list_type)([1, 2, 3, 6, 7, 8, 9]) #10 (line in Coconut source) (list_type)([1, 2, 3, 5, 6, 7, 8, 9]) #11 (line in Coconut source) (list_type)([1, 2, 3, 4, 5, 6, 7, 8, 9]) #12 (line in Coconut source) (list_type)([9, 2, 3, 4, 5, 6, 7, 8, 9]) #13 (line in Coconut source) (list_type)([1, 2, 3, 4, 5, 6, 7, 8, 1]) #14 (line in Coconut source)
19. Repositář s demonstračními příklady
Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Coconut byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs.
20. Odkazy na Internetu
- Pattern matching (Wikipedia)
https://en.wikipedia.org/wiki/Pattern_matching - Coconut: funkcionální jazyk s pattern matchingem kompatibilní s Pythonem
https://www.root.cz/clanky/coconut-funkcionalni-jazyk-s-pattern-matchingem-kompatibilni-s-pythonem/ - Coconut aneb funkcionální nadstavba nad Pythonem (2.část)
https://www.root.cz/clanky/coconut-aneb-funkcionalni-nadstavba-nad-pythonem-2-cast/ - Python 3.10 and the Elegance of Pattern Matching
https://python.plainenglish.io/python-3–10-and-the-elegance-of-pattern-matching-2620a02b2379 - More Pattern Matching in Python 3.10
https://towardsdatascience.com/more-advanced-pattern-matching-in-python-3–10–2dbd8598302a - Pattern Matching in Python 3.10
https://towardsdatascience.com/pattern-matching-in-python-3–10–6124ff2079f0 - Python 3.10.0
https://www.python.org/downloads/release/python-3100/ - The fate of reduce() in Python 3000
http://lambda-the-ultimate.org/node/587 - PEP 634 – Structural Pattern Matching: Specification
https://peps.python.org/pep-0634/ - PEP 635 – Structural Pattern Matching: Motivation and Rationale
https://peps.python.org/pep-0635/ - PEP 636 – Structural Pattern Matching: Tutorial
https://peps.python.org/pep-0636/ - PEP 622 – Structural Pattern Matching
https://peps.python.org/pep-0622/ - Python 3.10 se strukturálním pattern matchingem
https://www.root.cz/zpravicky/python-3–10-se-strukturalnim-pattern-matchingem/ - Null coalescing operator
https://en.wikipedia.org/wiki/Null_coalescing_operator - Operátor koalescence
https://cs.wikipedia.org/wiki/Oper%C3%A1tor_koalescence - Clojure core.match
https://github.com/clojure/core.match - The Rust Programming Language: Patterns and Matching
https://doc.rust-lang.org/book/ch18–00-patterns.html#patterns-and-matching - The Rust Programming Language: Pattern Syntax
https://doc.rust-lang.org/book/ch18–03-pattern-syntax.html - Elvis operator
https://en.wikipedia.org/wiki/Elvis_operator - Safe navigation operator
https://en.wikipedia.org/wiki/Safe_navigation_operator - Setting stacksize in a python script
https://stackoverflow.com/questions/5061582/setting-stacksize-in-a-python-script - What is the maximum recursion depth in Python, and how to increase it?
https://stackoverflow.com/questions/3323001/what-is-the-maximum-recursion-depth-in-python-and-how-to-increase-it?rq=1 - Does Python optimize tail recursion?
https://stackoverflow.com/questions/13591970/does-python-optimize-tail-recursion - Programovací jazyk APL: programování bez smyček
https://www.root.cz/clanky/programovaci-jazyk-apl-programovani-bez-smycek/ - Programovací jazyk APL – dokončení
https://www.root.cz/clanky/programovaci-jazyk-apl-dokonceni/ - Tail call
https://en.wikipedia.org/wiki/Tail_call - Tail Call Optimization for Python
https://github.com/baruchel/tco - Tail Recursion Elimination
http://neopythonic.blogspot.cz/2009/04/tail-recursion-elimination.html - Origins of Python's „Functional“ Features
http://python-history.blogspot.cz/2009/04/origins-of-pythons-functional-features.html - Tail recursion decorator revisited
http://fiber-space.de/wordpress/2009/04/20/tail-recursion-decorator-revisited/ - Koncová rekurze
https://cs.wikipedia.org/wiki/Koncov%C3%A1_rekurze - Recursion (computer science)
https://en.wikipedia.org/wiki/Recursion_%28computer_science%29 - Coconut: Simple, elegant, Pythonic functional programming
http://coconut-lang.org/ - coconut 1.1.0 (Python package index)
https://pypi.python.org/pypi/coconut/1.1.0 - Coconut Tutorial
http://coconut.readthedocs.io/en/master/HELP.html - Coconut FAQ
http://coconut.readthedocs.io/en/master/FAQ.html - Coconut Documentation
http://coconut.readthedocs.io/en/master/DOCS.html - Python gains functional programming syntax via Coconut
https://www.infoworld.com/article/3088058/python-gains-functional-programming-syntax-via-coconut.html - Repositář projektu pyparsing
https://github.com/pyparsing/pyparsing - Repositář projektu cPyparsing
https://github.com/evhub/cpyparsing - Projekty vylepšující interaktivní režim Pythonu: bpython, ptpython, DreamPie a IPython
https://www.root.cz/clanky/projekty-vylepsujici-interaktivni-rezim-pythonu-bpython-ptpython-dreampie-a-ipython/ - Coconut na Redditu
https://www.reddit.com/r/Python/comments/4owzu7/coconut_functional_programming_in_python/ - Repositář na GitHubu
https://github.com/evhub/coconut - patterns
https://github.com/Suor/patterns - Source-to-source compiler
https://en.wikipedia.org/wiki/Source-to-source_compiler - The Lua VM, on the Web
https://kripken.github.io/lua.vm.js/lua.vm.js.html - Lua.vm.js REPL
https://kripken.github.io/lua.vm.js/repl.html - lua2js
https://www.npmjs.com/package/lua2js - Wisp na GitHubu
https://github.com/Gozala/wisp - Wisp playground
http://www.jeditoolkit.com/try-wisp/ - REPL v prohlížeči
http://www.jeditoolkit.com/interactivate-wisp/ - Minification (programming)
https://en.wikipedia.org/wiki/Minification_(programming) - JavaScript is Assembly Language for the Web: Sematic Markup is Dead! Clean vs. Machine-coded HTML
http://www.hanselman.com/blog/JavaScriptIsAssemblyLanguageForTheWebSematicMarkupIsDeadCleanVsMachinecodedHTML.aspx - JavaScript is Web Assembly Language and that's OK.
http://www.hanselman.com/blog/JavaScriptIsWebAssemblyLanguageAndThatsOK.aspx - Dart
https://www.dartlang.org/ - CoffeeScript
http://coffeescript.org/ - TypeScript
http://www.typescriptlang.org/ - JavaScript: The Web Assembly Language?
http://www.informit.com/articles/article.aspx?p=1856657 - asm.js
http://asmjs.org/ - List of languages that compile to JS
https://github.com/jashkenas/coffeescript/wiki/List-of-languages-that-compile-to-JS - Permutation
https://en.wikipedia.org/wiki/Permutation - Pattern matching (Wikipedia)
https://en.wikipedia.org/wiki/Pattern_matching - Programovací jazyky používané v SSSR (část 2 – SNOBOL)
https://www.root.cz/clanky/programovaci-jazyky-pouzivane-v-sssr-cast-2-ndash-snobol/ - Pattern matching v Rustu
https://www.root.cz/clanky/rust-funkce-lambda-vyrazy-a-rozhodovaci-konstrukce-match/#k13 - SNOBOL
https://en.wikipedia.org/wiki/SNOBOL - Podpůrný plugin pro Vim
https://github.com/manicmaniac/coconut.vim - Příkaz (programování)
https://cs.wikipedia.org/wiki/P%C5%99%C3%ADkaz_%28programov%C3%A1n%C3%AD%29 - Threading Macros Guide
https://clojure.org/guides/threading_macros - Nejdůležitější novinka v Pythonu 3.10: strukturální pattern matching
https://www.root.cz/clanky/nejdulezitejsi-novinka-v-pythonu-3–10-strukturalni-pattern-matching/ - Rosetta Code: Roman_numerals
http://rosettacode.org/wiki/Roman_numerals - Category:SNOBOL4
http://rosettacode.org/wiki/Category:SNOBOL4 - An introduction to SNOBOL by James Ford
http://drofmij.awardspace.com/snobol/ - AWK
https://en.wikipedia.org/wiki/AWK - Get started with GAWK: AWK language fundamentals
https://web.archive.org/web/20150427143548/https://www6.software.ibm.com/developerworks/education/au-gawk/au-gawk-a4.pdf - Pattern Matching
https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching - Parsing expression grammar
https://en.wikipedia.org/wiki/Parsing_expression_grammar - Abort, Retry, Fail?
https://en.wikipedia.org/wiki/Abort,_Retry,_Fail%3F - SNOBOL4 and SPITBOL Information
http://www.snobol4.com/ - Vanilla Snobol4 Reference Manual
http://burks.bton.ac.uk/burks/language/snobol/catspaw/manual/contents.htm - SNOBOL4.ORG – SNOBOL4 Resources
http://www.snobol4.org/ - Snobol3 – Snobol 3 Interpreter Implemented in Java
http://serl.cs.colorado.edu/~dennis/software/s3.html - Exploring Beautiful Languages – A guick look at SNOBOL
http://langexplr.blogspot.com/2007/12/quick-look-at-snobol.html - Rekurze a pattern matching v programovacím jazyku F#
https://www.root.cz/clanky/rekurze-a-pattern-matching-v-programovacim-jazyku-f/ - Programovací jazyk OCaml: rekurze, pattern matching a práce se seznamy
https://www.root.cz/clanky/programovaci-jazyk-ocaml-rekurze-pattern-matching-a-prace-se-seznamy/