Rád experimentuju se zdrojovým kódem. Náhodou jsem narazil na pár jednoduchých vztahů či pravidel. Pokouším se je určitým způsobem zobecnit a udělat v tom jakýsi pořádek. Prozatímní výsledky vám tu předkládám k pobavení, posouzení a možnému praktickému využití.
Programy jsou psány v určitém jazyce – programovacím jazyce. Je to umělý jazyk a byl stvořen/vyvinut jako přesný jazyk, aby to, co zápis programovacím jazyce říká, nešlo vyložit jinak. Program funguje jednoznačně tak, jak zápis ve zdrojovém kódu říká. Zároveň je ale programovacímu jazyku dána velká flexibilita, určitou věc lze zapsat mnoha více či méně odlišnými způsoby.
Obdobný jazyk, který má přesnost a zároveň flexibilitu, je jazyk matematiky.
Vlastnosti těchto jazyků podmiňují existenci jednoduchých pravidel pro přesnou změnu tvaru zápisu v daném jazyce. Změnu tvaru beze změny smyslu, správnosti, podstaty toho, co zápis říká, např. pravidla pro úpravu rovnic v matematice.
Myslím si, že tak, jak zacházíme s jazykem matematiky, můžeme analogicky tomu zacházet i s jazykem programovacím. Zdrojový kód je i v mnohém podobný matematickému zápisu. Máme konkrétní programovací jazyk, známe jeho syntaxi a význam symbolů, ze kterých se zápis skládá. Mohli bychom najít, sepsat pravidla, která umožňují přesnou změnu formy určitého zápisu – zdrojového kódu.
Kdyby existovala nějaká ucelená soustava jednoznačných pravidel, jak provádět přesné změny ve zdrojovém kódu, možná bychom mohli doslova „řešit“ zdrojový kód analogicky, jak řešíme rovnice v matematice.
Analogie s matematikou se mi zdají trefné, občas se jich na některých místech přidržím.
Protože jiný programovací jazyk moc neumím (a nejen proto), budou ukázky kódu v jazyce C/C++ (stačí základy).
Poznámka:
Výborné na jazyce C a jazycích z něj odvozených je obecnost a úspornost syntaxe – to přesně se mi hodí, např. geniálně obecně vymyšlený cyklus for. V jazycích odlišných od C by některé úpravy byly složitější. Nicméně s určitými omezeními platí dále uvedené věci i pro odlišné jazyky.
O čem budu psát, jsou až překvapivě jednoduché věci. Možná, že více programátorů ve své kuchyni podobné triky dávno používá, ale veřejně to není známo; žádná knížka o tom nepíše, na Internetu jsem nic nenašel. Třeba mě někdo vyvede z omylu. Nebo je to taková trivialita, že nestojí za to o tom něco psát? Proč? Myslím si, že zvláště pro začátečníky v programování, kteří ještě v programovacím jazyce neumí „myslet“, může být představovaný přístup nejvíce přínosný.
Nahrazení identifikátoru proměnné
O co jde, ukážu na jednoduchém kódu:
// Vypíše čísla od 0 do 9
for (int x = 0; x < 10; x++) {
printf("%d\n", x);
}
Můžu klidně přejmenovat identifikátor x ve všech výskytech třeba za y a smysl programu a jeho funkčnost se touto změnou nezmění. Na tom nic zajímavého není.
Co mi připadá přínosnější…
Můžu taky identifikátor x „přejmenovat“ (nahradit) ve všech výskytech třeba za (x+3) a smysl programu se ani touto změnou nezmění.
Udělám to, doslova nahradím identifikátor x za (x+3) a dostanu:
for (int (x+3) = 0; (x+3) < 10; (x+3)++) {
printf("%d\n", (x+3));
}
Ve dvou místech tím vznikla syntaktická chyba, nevadí – ještě jsem neskončil. Nyní po tomto nahrazení udělám to, čemu budu dále říkat: „Obnovit správnou syntaxi po nahrazení“
Podrobně rozepíšu jednotlivé případy.
Syntaktická chyba 1:
int (x+3) = 0;
Překladači jazyka vadí, že (x+3) není L-hodnota. I když ten zápis logiku má, syntaxe tohle nedovoluje.
Obnovit správnou syntaxi po nahrazení můžu:
a) Úvahou:
- Ten kód slovy znamená: „(x+3) je celé číslo, jehož výchozí hodnota je 0“. Jestliže (x+3) je celé číslo, pak také x musí být celé číslo, a má-li být hodnota (x+3) rovna 0, musí být hodnota x rovna -3
b) Mechanicky:
- Je to jednoduchá rovnice, kterou vyřeším odečtením 3 od obou stran. Oběma způsoby vychází, že zápis:
int (x+3) = 0;
je to samé, jako už syntakticky správný zápis:
int x = -3;
Poznámka:
V průběhu úprav neřeším, že má určitý číselný typ v programovacím jazyce omezený rozsah. Pro potřeby podobných úprav prostě a naivně předpokládám, že všechny celočíselné typy pojmou jakékoli celé číslo, jaké potřebuju. Teprve v okamžiku, kdy mám upravený a syntakticky správný program a chci ho spustit, zkontroluju, zdali mi někde něco nepřeteklo apod. Obecně, jde tu spíše o význam toho, co svým zápisem programovací jazyk říká, než o omezení média.
Vypomáhám si touto analogií:
V matematice také v průběhu úprav neřeším, že výsledek nějakého výpočtu bude nakonec napsán na papír, kam se vejde jen omezený počet cifer…
Syntaktická chyba 2:
(x+3)++
Překladač opět hlásí chybu, (x+3) není L-hodnota.
Obnovit správnou syntaxi po nahrazení můžu:
a) Úvahou:
- Ten kód slovy znamená: „Zvyš hodnotu (x+3) o 1“ protože +3 ve výrazu (x+3) je konstanta, lze příkaz splnit jedině tak, že se o 1 zvýší proměnná x
b) Mechanicky:
- Kód si rozepíšu jako (x+3) = (x+3) + 1 a mám rovnici, kterou zjednoduším odečtením 3 od obou stran a dostanu výsledek: x = x + 1, což je zkráceně ono x++
Oběma způsoby vychází, že:
(x+3)++
je totéž jako už syntakticky správný zápis:
x++
Poznámka:
Pokud je někde x++ součástí jiného výrazu, je třeba to ošetřit a myslet na rozdíl mezi pre-inkrement a post-inkrement.
Kód je v tuto chvíli už funkční, pro přehlednost ještě upravím nerovnici:
(x+3) < 10
Odečtením 3 od obou stran dostanu:
x < 7
A po odstranění zbylé zbytečné závorky konečně dostanu upravený program:
// Vypíše čísla od 0 do 9
for (int x = -3; x < 7; x++) {
printf("%d\n", x+3);
}
Trošku odlišný kód, který ale funguje úplně stejně, jako kód výchozí. Můžu v tomto kódu na zkoušku provést opačnou úpravu, kdy identifikátor x nahradím za (x-3) a dostanu zase tu výchozí formu kódu.
Myslím, že je vidět, že to bude fungovat nejen pro trojku. Není důležité, kterou konstantu přičítám nebo odečítám, princip je stejný u libovolné celočíselné konstanty.
Je to jen pár mechanických pravidel, která si člověk rychle osvojí a zautomatizuje. Když mám daleko složitější kód, kde se daná proměnná vyskytuje na mnoha místech, tento přístup k věcem pak velmi usnadňuje úpravy; jsou přímočařejší.
Závěr
V článku jsem se pokusil ukázat, že lze mechanicky vzít identifikátor proměnné a nahradit ho za výraz (identifikátor + konstanta) nebo (identifikátor - konstanta) (kde tu konstantu zvolím třeba tak, aby se mi někde něco vyrušilo, zjednodušilo). Po provedení úprav obnovujících správnost syntaxe programovacího jazyka dostanu správný kód, který funguje stejně jako kód výchozí, ale má trochu jiný tvar. Může být třeba lepší, výstižnější, jednodušší, rychlejší než kód výchozí. To, v jakém smyslu je lepší, si řídím tím, jaké provádím nahrazení.
To, co píšu, neberte prosím jako danou věc a hotovo. Píšu jen svůj osobní pohled na věc. Věřím, že se ty myšlenky a jejich forma mohou nějakým způsobem rozvíjet dál. Někdo, kdo to bude číst, může mít třeba lepší nápad – co mi nedošlo – a myšlenky budou moci vykročit za hranice mých limitů, ku prospěchu všech. Proto to vlastně dělám. Budu rád za dotazy a připomínky.
Máte-li k věci co říct, pište, prosím. :-)