Obsah
1. Coconut: jazyk naznačující směr budoucího vývoje Pythonu
2. Paralelní vývoj Pythonu i jazyka Coconut
3. Instalace interpretru a transpileru jazyka Coconut
4. Interaktivní konzole jazyka Coconut
5. Coconut ve funkci transpřekladače
6. Operátory zjednodušující zápis anonymních funkcí
7. Výpočet faktoriálu s využitím anonymních funkcí
8. Využití operátoru |> pro zřetězení operací a vytvoření kolony
9. Ukázky transpřekladu výpočtů faktoriálu z Coconutu do Pythonu
10. Lambda výraz s implicitním parametrem
11. Lambda výraz bez parametru
12. Od lambda výrazu k plnohodnotné anonymní funkci
13. Anonymní funkce s příkazem a výrazem
14. Anonymní funkce s typovými informacemi
15. Curryfikace a částečně vyhodnocené funkce
16. Deklarace částečně vyhodnocených funkcí v Coconutu
17. Příloha A: klávesové zkratky použité v konzoli Coconutu v režimu Emacs
18. Příloha B: klávesové zkratky použité v konzoli Coconutu v režimu Vi
19. Repositář s demonstračními příklady
1. Coconut: jazyk naznačující směr budoucího vývoje Pythonu
Už v roce 2017, což je z pohledu vývoje informatiky poměrně dlouhá doba, jsme se na stránkách Roota ve dvojici článků [1] [2] seznámili se zajímavým programovacím jazykem nazvaným Coconut. Připomeňme si, že se jedná o programovací jazyk, který byl původně navržen takovým způsobem, aby byl zpětně kompatibilní s Pythonem, což je důležité, protože Coconut můžeme považovat za sémantické i syntaktické rozšíření samotného Pythonu (program zapsaný v Pythonu je současně i korektním programem psaným v Coconutu; naopak to ovšem pochopitelně neplatí). Z pohledu programátora se Coconut používá jako klasický interpret, ovšem interně provádí transpilaci (či transkompilaci) zapsaného zdrojového kódu (zpět) do Pythonu a tento transpilovaný kód je možné si nechat v případě potřeby zobrazit a zjistit tak, jak jsou jednotlivé jazykové konstrukce Coconutu interně realizovány.
V jazyku Coconut jsme již v roce 2017 mohli najít takové jazykové konstrukce (tj. novou syntaxi se zcela novou sémantikou), které podporovaly funkcionální programování a vlastně i prakticky celé funkcionální paradigma, takže v Coconutu pocházejícím z té doby lze najít například funkce vyššího řádu (které ve skutečnosti podporuje i samotný Python, i když někdy poněkud nešikovně), podporu pro neměnitelné (immutable) hodnoty, podporu pro zápis kompozice funkcí či pro tvorbu takzvaných kolon (dnes navíc Coconut podporuje i zápis částečného vyhodnocení funkce). To ovšem nebylo zdaleka vše, protože již ve zmíněném roce 2017 jsme mohli v jazyce Coconut vidět a používat jazykovou konstrukci realizující pattern matching.
2. Paralelní vývoj Pythonu i jazyka Coconut
Od vydání obou výše zmíněných článků o programovacím jazyce Coconut již uběhla dlouhá doba, a to především v kontextu s postupným (a poměrně rychlým) vývojem samotného programovacího jazyka Python. Ostatně v roce 2017, kdy se začal Coconut postupně rozšiřovat, byla nejnovější verzí Pythonu (CPythonu) verze 3.6, viz tabulku se všemi verzemi Pythonu 3.x:
Verze | Datum vydání |
---|---|
Python 3.0 | 3. prosince 2008 |
Python 3.1 | 27. června 2009 |
Python 3.2 | 20. února 2011 |
Python 3.3 | 29. září 2012 |
Python 3.4 | 16. března 2014 |
Python 3.5 | 13. září 2015 |
Python 3.6 | 23. prosince 2016 |
Python 3.7 | 27. června 2018 |
Python 3.8 | 14. října 2019 |
Python 3.9 | 5. října 2020 |
Python 3.10 | 4. října 2021 |
Python 3.11 | 24. října 2022 |
Python 3.12 | 2. října 2023 |
Samotný programovací jazyk Python byl od výše zmíněné verze 3.6 upraven a rozšiřován v mnoha směrech, což je patrné při pohledu na další tabulku, tentokrát s největšími novinkami, které byly ve verzích následujících po verzi 3.6 do tohoto velmi populárního jazyka přidány:
Verze | Vybrané nové vlastnosti jazyka |
---|---|
Python 3.7 | rezervovaná klíčová slova async a await + jejich sémantika |
Python 3.8 | mroží operátor (walrus operator), poziční parametry funkcí |
Python 3.9 | generické datové typy |
Python 3.10 | pattern matching |
Python 3.10 | možnost použití operátoru | při definici typů |
Python 3.11 | skupiny výjimek |
Python 3.12 | klíčové slovo type + jeho sémantika |
Python 3.12 | nová syntaxe pro zápis generických typů |
Na tyto změny v Pythonu musel projekt Coconut nějakým způsobem reagovat, už jen z toho důvodu, že se v Pythonu 3.10 objevil pattern matching nekompatibilní s původním Coconutem (stručně řečeno – význam klíčových slov match a case byl prohozen). A právě na tyto změny (resp. přesněji řečeno na současný stav Coconutu) se zaměříme v dnešním článku i v článku navazujícím.
3. Instalace interpretru a transpileru jazyka Coconut
Programovací jazyk Coconut, což konkrétně znamená transpiler (transcompiler) upravený do takové podoby, že se chová jako běžný interpret s interaktivní smyčkou REPL (Read-Eval-Print Loop), je možné nainstalovat s využitím běžných správců balíčků ekosystému jazyka Python, tedy například pomocí nástroje pip či pdm. Samotná instalace je zdržena pouze překladem balíčku cPyparsing, který je překládán do nativního kódu (používá se zde technologie Cython, kterou již dobře známe, protože jsme se jí na stránkách Roota již několikrát zabývali).
V případě, že se vám nechce čekat přibližně minutu na dokončení překladu balíčku cPyparsing, lze namísto tohoto balíčku použít pyparsing, jenž je naprogramován v čistém Pythonu a tudíž nevyžaduje překlad. Nevýhodou bude pomalejší běž transpileru Coconutu, což ovšem na našich triviálních demonstračních příkladech vůbec nepocítíme:
$ pip install --user coconut Collecting coconut Downloading coconut-3.1.0-py2.py3-none-any.whl (316 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 316.5/316.5 kB 2.1 MB/s eta 0:00:00 Requirement already satisfied: setuptools>=44 in /usr/lib/python3.11/site-packages (from coconut) (65.5.1) Collecting cPyparsing<2.4.7.2.4,>=2.4.7.2.3.2 Downloading cpyparsing-2.4.7.2.3.3.tar.gz (1.2 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1.2 MB 3.6 MB/s eta 0:00:00 Installing build dependencies ... done Getting requirements to build wheel ... done Preparing metadata (pyproject.toml) ... done Requirement already satisfied: psutil>=5 in /home/ptisnovs/.local/lib/python3.11/site-packages (from coconut) (5.9.8) Requirement already satisfied: prompt-toolkit>=1 in /home/ptisnovs/.local/lib/python3.11/site-packages (from coconut) (3.0.39) Collecting async-generator>=1.10 Downloading async_generator-1.10-py3-none-any.whl (18 kB) Requirement already satisfied: anyio>=3 in /home/ptisnovs/.local/lib/python3.11/site-packages (from coconut) (3.7.1) Collecting typing-extensions>=4.9 Downloading typing_extensions-4.12.0-py3-none-any.whl (37 kB) Collecting pygments>=2.17 Downloading pygments-2.18.0-py3-none-any.whl (1.2 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.2/1.2 MB 3.9 MB/s eta 0:00:00 Requirement already satisfied: idna>=2.8 in /usr/lib/python3.11/site-packages (from anyio>=3->coconut) (3.4) Requirement already satisfied: sniffio>=1.1 in /home/ptisnovs/.local/lib/python3.11/site-packages (from anyio>=3->coconut) (1.3.0) Requirement already satisfied: wcwidth in /home/ptisnovs/.local/lib/python3.11/site-packages (from prompt-toolkit>=1->coconut) (0.2.8) Building wheels for collected packages: cPyparsing Building wheel for cPyparsing (pyproject.toml) ... done Created wheel for cPyparsing: filename=cPyparsing-2.4.7.2.3.3-cp311-cp311-linux_x86_64.whl size=5197614 sha256=8f1c9fabc0e1772db081d487c9eb6122c8a68c7ef0cb47b72312c72fd69e3527 Stored in directory: /home/ptisnovs/.cache/pip/wheels/ef/31/db/124f8f126b5196bd89cc0b2c4266f27e377efd4a544b90a94a Successfully built cPyparsing Installing collected packages: cPyparsing, typing-extensions, pygments, async-generator, coconut Attempting uninstall: pygments Found existing installation: Pygments 2.16.1 Uninstalling Pygments-2.16.1: Successfully uninstalled Pygments-2.16.1 Successfully installed async-generator-1.10 cPyparsing-2.4.7.2.3.3 coconut-3.1.0 pygments-2.18.0 typing-extensions-4.12.0
Po instalaci by mělo být možné spustit interpret Coconutu s interaktivní smyčkou REPL. Na systému s nainstalovaným Pythonem 3.11 (což je relativně nová verze Pythonu) bude spuštění vypadat následovně:
$ coconut Coconut Interpreter v3.1.0 (Python 3.11): (enter 'exit()' or press Ctrl-D to end) >>>
Ve skutečnosti však bude Coconut funkční i v případě, že je v systému nainstalována starší veze Pythonu, například dnes již notně zastaralý Python 3.8:
$ coconut Coconut Interpreter v3.1.0 (Python 3.8): (enter 'exit()' or press Ctrl-D to end)
Coconut taktéž podporuje poměrně velké množství přepínačů specifikovaných na příkazové řádce. Tyto přepínače si pochopitelně můžeme vypsat společně s krátkou nápovědou:
$ coconut --help usage: coconut [-h] [--and source [dest ...]] [-v] [-t version] [-i] [-p] [-a] [-l] [--no-line-numbers] [-k] [-w] [-r] [-n] [-d] [-q] [-s] [--no-tco] [--no-wrap-types] [-c code] [-j processes] [-f] [--minify] [--jupyter ...] [--mypy ...] [--argv ...] [--tutorial] [--docs] [--style name] [--vi-mode] [--recursion-limit limit] [--stack-size kbs] [--fail-fast] [--no-cache] [--site-install] [--site-uninstall] [--verbose] [source] [dest] docs: http://coconut.readthedocs.io/en/v3.1.0/DOCS.html positional arguments: source path to the Coconut file/folder to compile dest destination directory for compiled files (defaults to the source directory) options: ... ... ...
4. Interaktivní konzole jazyka Coconut
Uživatelské rozhraní interaktivní smyčky REPL je v Coconutu oproti klasickému Pythonu (přesněji řečeno CPythonu) vylepšené a do jisté míry připomíná IPython, bpython či PTPython (viz též článek na toto téma). Pravděpodobně nejviditelnějším vylepšením je, že se po stisku klávesy Tab zobrazí kontextové menu se seznamem symbolů, které se k Coconutu vztahují (včetně jeho specifických klíčových slov, funkcí použitelných bez nutnosti importu nějakého balíčku atd.). V interaktivním prostředí Coconutu se navíc barevně zvýrazňují klíčová slova, operátory a některé literály (číselné konstanty a řetězce).
Obrázek 1: Barevné zvýraznění literálů, klíčových slov atd.
Taktéž je možné se vracet a vyhledávat v historii příkazů (Ctrl+R a funkční budou i další známé klávesové zkratky, například pro skoky na začátek a konec řádku (Ctrl+A, Ctrl+E), mazání části zapsaného textu Ctrl+W, Ctrl+K, vložení značky pro výběr (Ctrl+Space) atd. Podporované klávesové zkratky jsou vypsány v příloze A.
Obrázek 2: Vyhledávání v historii zapsaných příkazů.
Obrázek 3: Kontextové menu s výpisem symbolů obsahujících uživatelem zapsané znaky. Vypisují se pouze symboly vztažené k samotnému Coconutu, nikoli k Pythonu.
5. Coconut ve funkci transpřekladače
Příkaz coconut spustí buď interaktivní smyčku REPL, což jsme si již ostatně ukázali v předchozím textu, nebo ho je možné alternativně použít pro transpřeklad zdrojových kódů z Coconutu do „čistého“ Pythonu. Ukažme si tedy ve stručnosti ještě i tento druhý způsob použití, a to na velmi jednoduchém příkladu, který má na standardní výstup vypsat řetězec „Hello world!“. S využitím nového operátoru pro kolonu (pipeline, ten si popíšeme v dalším textu) je možné takový prográmek napsat například následujícím způsobem:
"Hello, world!" |> print
Překlad, resp. přesněji řečeno transpřeklad, se provede příkazem:
$ coconut hello-world.coco
Průběh transpřekladu:
Compiling hello-world.coco ... CoconutWarning: Populating initial parsing cache (compilation may take longer than usual)... Compiled to hello-world.py .
Ve výsledném souboru hello-world.py nalezneme výsledek transpřekladu našeho jediného řádku:
# Compiled Coconut: ----------------------------------------------------------- (print)("Hello, world!") #1 (line in Coconut source)
Ve skutečnosti je ovšem soubor hello-world-py mnohem větší; jeho velikost dosahuje přibližně 144 kB. Je tomu tak z toho důvodu, že obsahuje všechny standardní funkce Coconutu (k nim se ještě vrátíme) i pomocné funkce použité v transpilovaném kódu. Přepínačem –minify se můžeme pokusit o zmenšení výsledného souboru, což v tomto případě povede k vygenerování zdrojového souboru Pythonu o velikosti přibližně 126 kB.
6. Operátory zjednodušující zápis anonymních funkcí
Velká část změn a vylepšení, které v jazyku Coconut nalezneme, se týká práce s funkcemi, které – jak již název napovídá – tvoří kostru funkcionálního programování. Na některé možnosti Coconutu v této oblasti se nyní zaměříme. Coconut programátorům nabízí i možnost zkráceného zápisu lambda výrazů (což jsou v Pythonu anonymní funkce tvořené jediným výrazem). Následující dva zápisy jsou ekvivalentní, přičemž výraz druhý je kratší a více se podobá „klasické“ lambdě:
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" #1 (line in Coconut source) words = message.split() filtered = filter(lambda word: len(word) > 4, words) for word in filtered: print(word)
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() filtered = filter(word -> len(word) > 4, words) for word in filtered: print(word)
Zápis anonymních operací s využitím šipky → je sice stále podporován, ale doporučuje se používat nový styl zápisu s odlišnou šipkou =>. Je tomu tak z toho důvodu, že → se dnes používá i při zápisu typových informací. V dalším textu tedy budeme používat tento zápis anonymní funkce:
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() filtered = filter(word => len(word) > 4, words) for word in filtered: print(word)
7. Výpočet faktoriálu s využitím anonymních funkcí
Způsob použití nového zápisu anonymních funkcí si ukážeme na výpočtu faktoriálu, což je úloha pro tyto účely téměř ideální. V „čistém“ jazyce Python může být tento výpočet realizován s využitím funkce vyššího řádu reduce, a to následovně:
from functools import reduce def factorial(n): return reduce(lambda a, b: a*b, range(1, n+1), 1) for n in range(0, 11): print(n, factorial(n))
Pokusme se tedy o přepis do jazyka Coconut. První varianta by mohla vypadat následovně:
def factorial(n): return reduce(a, b => a*b, range(1, n+1), 1) for n in range(0, 11): print(n, factorial(n))
Problém ovšem spočívá v tom, že se ze syntaktického hlediska nejedná o korektní zápis, protože jazyk nedokáže odvodit, že a není parametrem funkce reduce, ale že patří k definici anonymní funkce. Korektní zápis je nutné provést se závorkami, tedy takto:
def factorial(n): return reduce((a, b) => a*b, range(1, n+1), 1) for n in range(0, 11): print(n, factorial(n))
Tento skript je již možné transpilovat a výsledek spustit běžným způsobem.
Ovšem v Pythonu můžeme i samotnou funkci factorial zapsat přes lambda výraz, což vede k tomu, že v těle jednoho lambda výrazu bude další lambda výraz:
from functools import reduce factorial = lambda n: reduce(lambda a, b: a*b, range(1, n+1), 1) for n in range(0, 11): print(n, factorial(n))
Přepis do jazyka Coconut bude v tomto případě snadný, protože již známe podmínku, kterou je nutno splnit (uzávorkování většího množství parametrů):
factorial = n => reduce((a, b) => a*b, range(1, n+1), 1) for n in range(0, 11): print(n, factorial(n))
A konečně můžeme odstranit i poslední programovou smyčku, protože funkcí vyššího řádu map lze zajistit výpočet celé tabulky faktoriálů. Opět si nejdříve ukažme řešení realizované přímo v Pythonu:
from functools import reduce n = range(0, 11) factorials = map(lambda n: reduce(lambda a, b: a*b, range(1, n+1), 1), n) print(list(factorials))
Přepis do jazyka Coconut s využitím operátoru ⇒ je opět velmi snadný:
n = range(0, 11) factorials = map(n => reduce((a, b) => a*b, range(1, n+1), 1), n) print(list(factorials))
8. Využití operátoru |> pro zřetězení operací a vytvoření kolony
Trošku sice předbíháme, ale ukažme si, jak můžeme nahradit nepěkný zápis s mnoha závorkami (a čtením zprava doleva):
print(list(factorials))
Využijeme zde operátory |>, díky nimž se vytvoří „kolona“ funkcí, které si předávají svoje výstupní hodnoty. Prozatím je vše snadné, protože funkce factorials a list mají jedinou výstupní hodnotu a list a print akceptují jeden vstupní parametr:
n = range(0, 11) factorials = map(n => reduce((a, b) => a*b, range(1, n+1), 1), n) factorials |> list |> print
9. Ukázky transpřekladu výpočtů faktoriálu z Coconutu do Pythonu
Pro zajímavost se ještě podívejme na transpřeklad všech pěti výše uvedených výpočtů faktoriálu z jazyka Coconut do Pythonu. V levém sloupci je uveden zdrojový kód napsaný v Coconutu, ve sloupci pravém pak výsledek tranpřekladu (čistý Python):
def factorial(n): @_coconut_tco return reduce(a, b => a*b, range(1, n+1), 1) def factorial(n): return _coconut_tail_call(reduce, a, lambda b: a * b, range(1, n + 1), 1) for n in range(0, 11): print(n, factorial(n)) for n in range(0, 11): print(n, factorial(n))
def factorial(n): @_coconut_tco return reduce((a, b) => a*b, range(1, n+1), 1) def factorial(n): return _coconut_tail_call(reduce, lambda a, b: a * b, range(1, n + 1), 1) for n in range(0, 11): print(n, factorial(n)) for n in range(0, 11): print(n, factorial(n))
factorial = n => reduce((a, b) => a*b, range(1, n+1), 1) factorial = lambda n: reduce(lambda a, b: a * b, range(1, n + 1), 1) for n in range(0, 11): for n in range(0, 11): print(n, factorial(n)) print(n, factorial(n))
n = range(0, 11) n = range(0, 11) factorials = map(n => reduce((a, b) => a*b, range(1, n+1), 1), n) factorials = map(lambda n: reduce(lambda a, b: a * b, range(1, n + 1), 1), n) print(list(factorials)) print(list(factorials))
n = range(0, 11) n = range(0, 11) factorials = map(n => reduce((a, b) => a*b, range(1, n+1), 1), n) factorials = map(lambda n: reduce(lambda a, b: a * b, range(1, n + 1), 1), n) factorials |> list |> print (print)((list)(factorials))
10. Lambda výraz s implicitním parametrem
V jazyce Coconut je možné v případě, že anonymní funkce (lambda výraz) akceptuje pouze jediný parametr, jméno tohoto parametru před šipkou ⇒ vynechat. V těle takto zapsané anonymní funkce se namísto jména parametru v takovém případě použije pouze podtržítko. To znamená, že zápis mnoha lambda výrazů může být zkrácen, protože mnoho výrazů akceptuje právě jediný parametr. Opět si to ukažme na příkladu filtrace slov ze vstupního řetězce na základě jejich délky:
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() filtered = filter(=> len(_) > 4, words) for word in filtered: print(word)
Transpřeklad tohoto skriptu do Pythonu bude vypadat následovně:
# Compiled Coconut: ----------------------------------------------------------- message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" #1 (line in Coconut source) words = message.split() #2 (line in Coconut source) filtered = filter(lambda _=None: len(_) > 4, words) #4 (line in Coconut source) for word in filtered: #6 (line in Coconut source) print(word) #7 (line in Coconut source)
Výsledky běhu skriptu:
Lorem ipsum dolor amet, consectetur adipiscing elit, eiusmod tempor incididunt labore dolore magna aliqua
11. Lambda výraz bez parametru
Pokud se pozorněji podíváme na způsob transpilace anonymní funkce s jediným parametrem do Pythonu, zjistíme, že se vlastně jedná o lambda výraz s nepovinným parametrem. Z toho vyplývá, že v jazyku Coconut můžeme deklarovat i anonymní funkce bez parametrů. Nejedná se sice v naprosté většině případů o čisté funkce, ale to neznamená, že by se nejednalo o užitečné konstrukce. Můžeme se například pokusit o náhodný výběr zhruba poloviny slov ze zadaného řetězce:
import random message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() filtered = filter(=> random.random() > 0.5, words) for word in filtered: print(word)
Způsob transpilace tohoto skriptu do Pythonu:
# Compiled Coconut: ----------------------------------------------------------- import random #1 (line in Coconut source) message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" #3 (line in Coconut source) words = message.split() #4 (line in Coconut source) filtered = filter(lambda _=None: random.random() > 0.5, words) #6 (line in Coconut source) for word in filtered: #8 (line in Coconut source) print(word) #9 (line in Coconut source)
Opět si ukažme výsledek běhu tohoto skriptu (pouze příklad – výsledek bude pseudonáhodný):
Lorem ipsum dolor amet, adipiscing elit, sed do eiusmod incididunt et dolore magna aliqua
12. Od lambda výrazu k plnohodnotné anonymní funkci
Standardní programovací jazyk Python podporuje zápis lambda výrazů, což již ostatně víme. A navíc známe taktéž způsob přepisu do jazyka Coconut. Ovšem lambda výrazy mají jedno velké omezení, které je skryto už v jejich názvu – jejich tělo je totiž skutečně tvořeno jedním výrazem (s určitými triky). Ovšem jak lze zapsat plnohodnotnou anonymní funkci se smyčkou, více výrazy atd.? Ve standardním Pythonu to možné není, ale v jazyku Coconut naopak ano. Pro tento účel se používají takzvané „statement lambdas“, což je nepřesný název pro plnohodnotné anonymní funkce.
Anonymní funkce s potenciálně větším množstvím výrazů přitom nemusí obsahovat příkaz return, protože je vrácena hodnota posledního zapsaného výrazu (musí jít o výraz). Podívejme se na jednoduchý příklad, v němž je anonymní funkce podtržena:
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() filtered = filter((def (word) => len(word) > 4), words) for word in filtered: print(word)
Překlad do Pythonu dopadne následovně:
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" #1 (line in Coconut source) words = message.split() def _coconut_lambda_0(word): return len(word) > 4 filtered = filter((_coconut_lambda_0), words) for word in filtered: #6 (line in Coconut source) print(word)
13. Anonymní funkce s příkazem a výrazem
Předchozí demonstrační příklad je možné upravit do takové podoby, ve které se v anonymní funkci skutečně použije nikoli pouze jediný výraz, ale příkaz následovaný výrazem. Onen příkaz slouží k natavení lokální proměnné length; tato proměnná je následně použita ve výrazu, který vypočítá výsledek anonymní funkce:
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() filtered = filter((def (word) => length = len(word); length > 4), words) for word in filtered: print(word)
A takto vypadá transpřeklad původně anonymní funkce do čistého Pythonu:
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" #1 (line in Coconut source) words = message.split() def _coconut_lambda_0(word): length = len(word) return length > 4 filtered = filter((_coconut_lambda_0), words) for word in filtered: #6 (line in Coconut source) print(word)
14. Anonymní funkce s typovými informacemi
Zápis anonymních funkcí začínajících klíčovým slovem def nám navíc umožňuje specifikaci typových informací, tj. typů parametrů i typu návratové hodnoty (či hodnot). To u běžných lambda výrazů není možné. A navíc si při zápisu typových informací uvědomíme, proč se původní operátor → změnil na => – nedojde k nedorozumění, zda se zapisuje typ návratové hodnoty nebo tělo lambda výrazu či anonymní funkce:
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() filtered = filter((def (word: int) -> int => length = len(word); length > 4), words) for word in filtered: print(word)
Takový kód se překládá pro Python 2 bez generování typových informací (použijte parametr –target):
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() def _coconut_lambda_0(word ): #4 (line in Coconut source) # type: (...) -> int length = len(word) return length > 4 filtered = filter((_coconut_lambda_0), words) for word in filtered: print(word)
Naopak pro Python 3 se překlad (transpilace) provede s uvedením všech typových informací (opět použijte přepínač –target):
message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" words = message.split() def _coconut_lambda_0(word: int) -> int: length = len(word) return length > 4 filtered = filter((_coconut_lambda_0), words) for word in filtered: print(word)
15. Curryfikace a částečně vyhodnocené funkce
„Typically, developers learn new languages by applying what they know about existing languages. But learning a new paradigm is difficult – you must learn to see different solutions to familiar problems.“
V závěru dnešního článku si ukážeme, jakým způsobem se v programovacím jazyku Python provádí takzvaná curryfikace (anglicky currying). Pod tímto termínem se v teorii programovacích jazyků (ovšem i obecně v matematice) označuje proces, jímž se transformuje funkce, která má více než jeden parametr, do řady vložených funkcí, přičemž každá z nich má jen jediný parametr (jen na okraj – čistou funkci bez parametrů lze nahradit konstantou). Curryfikaci si můžeme představit jako postupnou transformaci funkce s n parametry na jinak zkonstruovanou funkci s n-1 parametry atd. až rekurzivně dojdeme k funkci s jediným parametrem:
x = f(a,b,c) → h = g(a) i = h(b) x = i(c)
Nebo na jediném řádku:
x = f(a,b,c) → g(a)(b)(c)
Částečné vyhodnocení funkce je realizováno například funkcí partial, kterou použijeme v příkladu s funkcí mul, která akceptuje dva parametry. Tyto parametry jsou vynásobeny a výsledek tohoto součinu je současně i návratovou hodnotou funkce mul. S využitím partial se tato funkce se dvěma parametry transformuje na novou funkci pojmenovanou doubler, která ovšem již akceptuje pouze jediný parametr y, neboť původní první parametr x byl nahrazen za dvojku. Následně se již funkce doubler volá s jediným parametrem:
from functools import partial def mul(x, y): return x * y print(mul(6, 7)) print() doubler = partial(mul, 2) for i in range(11): print(i, doubler(i))
16. Deklarace částečně vyhodnocených funkcí v Coconutu
V jazyce Coconut existuje velmi elegantní způsob zápisu konstrukce částečně vyhodnocené funkce. Tento způsob je založen na zápisu znaku $ za jméno jakékoli funkce, které se předají nějaké parametry (typicky méně parametrů, než funkce očekává). Výsledkem bude částečně vyhodnocená funkce, kterou lze zavolat později.
Skript z předchozí kapitoly lze do Coconutu přepsat tímto způsobem:
def mul(x, y): return x * y print(mul(6, 7)) print() doubler = mul$(2) for i in range(11): print(i, doubler(i))
def mul(x, y): return x * y print(mul(6, 7)) print() doubler = _coconut_partial(mul, 2) for i in range(11): print(i, doubler(i))
Další příklad, tentokrát s funkcí se třemi parametry:
def mul(x, y, z): return x * y * z print(mul(2, 3, 7)) print() doubler = mul$(2) for i in range(11): print(i, doubler(i, 10))
Popř. pro funkci se čtyřmi parametry – částečné vyhodnocení částečně vyhodnocených funkcí:
def mul(x, y, z, w): return x * y * z * w f1 = mul print(f1) f2 = f1$(2) print(f2) f3 = f2$(3) print(f3) f4 = f3$(4) print(f4) f5 = f4$(5) print(f5) f6 = f5$(6) print(f6) print() print(f1(2, 3, 4, 5)) print(f2(3, 4, 5)) print(f3(4, 5)) print(f4(5)) print(f5()) print(f6())
17. Příloha A: klávesové zkratky použité v konzoli Coconutu v režimu Emacs
V této příloze jsou vypsány vybrané klávesové zkratky, které jsou ve výchozím nastavení použity v interaktivní konzoli programovacího jazyka Coconut v režimu emulace Emacsu. V tomto režimu není zapotřebí přepínání režimů a většina příkazů je realizována klávesovou kombinací Ctrl+znak nebo Alt+znak. Současně se (pokud si neprovedete rekonfiguraci) jedná o stejné příkazy, jakými se ovládá příkazový řádek samotného shellu nebo standardního Pythonu (resp. přesněji řečeno jeho novějších verzí).
Příkazy pro přesuny kurzoru
Základní příkazy pro přesun kurzoru používají kombinaci Ctrl+znak, Alt+znak, popř. alternativně Esc, znak v případě, že zkratky Alt+znak kolidují s emulátorem terminálu (například vyvolávají příkazy z menu). Pokud je terminál správně nakonfigurován, měly by fungovat i kurzorové šipky a navíc i klávesy Home a End (se zřejmou funkcí):
Klávesa | Význam |
---|---|
Ctrl+B | přesun kurzoru na předchozí znak |
Ctrl+F | přesun kurzoru na další znak |
Alt+B | přesun kurzoru na předchozí slovo |
Alt+F | přesun kurzoru na další slovo |
Esc, B | shodné s klávesovou zkratkou Alt+B |
Esc, F | shodné s klávesovou zkratkou Alt+F |
Ctrl+A | přesun kurzoru na začátek řádku |
Ctrl+E | přesun kurzoru na konec řádku |
Mazání textu, práce s kill ringem
Pro přesun části textu v rámci editovaného řádku se používá takzvaný kill ring, do něhož se smazaný text uloží. Pro vložení takto smazaného textu do jiné oblasti se používá operace nazvaná yank (odpovídá paste). Některé dále uvedené příkazy dokážou s kill ringem pracovat:
Klávesa | Význam |
---|---|
Ctrl+K | smaže text od kurzoru do konce řádku a uloží ho do kill ringu |
Ctrl+U | smaže text od začátku řádku do pozice kurzoru a uloží ho do kill ringu |
Ctrl+W | smaže předchozí slovo a uloží ho do kill ringu |
Alt+D | smaže následující slovo a uloží ho do kill ringu |
Ctrl+Y | vloží text z kill ringu na místo, na němž se nachází kurzor (yank) |
Alt+Y | po operaci Ctrl+Y dokáže rotovat historií kill ringu a obnovit tak (před)předchozí smazaný text |
Ctrl+D | smaže jeden znak (pokud je ovšem na řádku nějaký obsah, jinak typicky ukončí aplikaci, resp. interaktivní smyčku Coconutu) |
Práce s historií dříve zadaných příkazů
Klávesa | Význam |
---|---|
Ctrl+P | průchod historií – předchozí text |
Ctrl+N | průchod historií – následující text |
Ctrl+R | zpětné (interaktivní) vyhledávání v historii |
Ctrl+G | ukončení režimu vyhledávání zapnutého předchozí zkratkou |
Některé další dostupné příkazy
Klávesa | Význam |
---|---|
Tab | implicitní klávesa pro zavolání completeru jazyka Coconut (kontextové menu) |
Ctrl+T | prohození dvou znaků (před kurzorem a na pozici kurzoru) |
Alt+U | text od pozice kurzoru do konce slova se změní NA VERZÁLKY |
Alt+L | text od pozice kurzoru do konce slova se změní na mínusky |
Alt+C | text od pozice kurzoru do konce slova se změní Tak, Že Slova Začínají Velkým Písmenem |
18. Příloha B: klávesové zkratky použité v konzoli Coconutu v režimu Vi
Pokud je Coconut spuštěn s volbou –vimode, bude se příkazová řádka ovládat příkazy napodobující editory Vi/Vim.
V režimu emulace editorů Vi/Vim je možné mj. použít i tyto klávesové zkratky:
Příkazy pro přesuny kurzoru
Tyto příkazy jsou platné pro normální režim a lze je zkombinovat s operátory (delete, change, yank atd.):
Klávesa | Význam |
---|---|
h | přechod na předchozí znak |
l | přechod na další znak |
b | skok (zpět) na první znak slova |
e | skok na poslední znak slova |
w | skok na první znak následujícího slova |
0 | skok na začátek řádku |
$ | skok na konec řádku |
% | doskok na párovou závorku |
f | skok na specifikovaný znak (find) |
Editace textu
Klávesa | Význam |
---|---|
x | smazání jednoho znaku (odpovídá klávese Delete) |
d | operace smazání textu (musí být následována příkazem pro pohyb kurzoru) |
D | smazání textu od pozice kurzoru do konce řádku |
y | přenos textu do registru _ (musí být následována příkazem pro pohyb kurzoru) |
c | změna textu (musí být následována příkazem pro pohyb kurzoru) |
r | změna jediného znaku |
s | změna jediného znaku a přechod do vkládacího režimu |
p | operace put/paste, vloží smazaný text od pozice kurzoru |
P | operace put/paste, vloží smazaný text před pozicí kurzoru |
Příkazy ve vkládacím režimu
Některé příkazy ve vkládacím režimu odpovídají režimu Emacsu (viz předchozí kapitolu):
Klávesa | Význam |
---|---|
Ctrl+H | smazání znaku nalevo od kurzoru (odpovídá Backspace) |
Ctrl+R | zpětné (interaktivní) vyhledávání v historii |
Ctrl+W | smaže předchozí slovo a uloží ho do kill ringu |
Další příkazy
Klávesa | Význam |
---|---|
Esc | přepnutí do normálního režimu |
1–9 | prefix pro opakování další operace |
u | vrácení poslední operace (lze opakovat) |
a | append – přechod do režimu vkládání |
i | insert – přechod do režimu vkládání |
~ | změna jednoho znaku: verzálky/kapitálky a zpět |
k | průchod historií – předchozí text |
j | průchod historií – následující text |
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
- 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/ - 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 - 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 - 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