Hm, hele co mi tu leží za zády :) http://imgur.com/N7HDulb
Na Sinclair jsem ani neměla hry, programovat na něm byla jediná možná zábava :)
Ale nechápu co to mění na faktu, že to goto nejen lze abstrahovat pomocí switch, ale ještě k tomu se tím dá odbourat to porovnání na začátku a přesunout do default. Jinak pokud není záměrem té ukázky vytvořit konstrukci která vždy propadne všemi řádkami až dolů (ekvivalent switch bez break), ale bude se z jednotlivých větví zase vracet, je lepší použíť gosub a return.
Proboha, o čem pořád mluvíš? Už několikrát od několika lidí zmíněný switch to abstrahuje na podstatně lepší konstrukci včetně kontroly chybného vstupu (pokud jde o zadání jiného čísla než je v menu). Až si budu muset na takový primitivní kód kreslit vývojový diagram, seknu s prací a půjdu do důchodu.
Jen podotýkám, že ve většině situací by se spíš vyplatilo použít na tohle strukturovaný příkaz GO SUB, který umožňuje návrat na místo odskoku příkazem RETURN a maže tím nutnost konkretizovat číslo řádku. Pokud bys pak chtěl opakovat menu po doběhnutí podprogramu, vyžaduje to jen drobnou úpravu.
100 REM menu 1..9
...
200 INPUT "Zadej volbu: "; v
210 v=INT v
220 IF v<1 OR v>9 THEN GO TO 200
230 GO SUB v*1000: GO TO 100
1000 REM podprogram 1
…
1500 RETURN
2000 REM podprogram 2
…
2500 RETURN
Tak zrovna tu, podla mojho nazoru, je goto pouzite zbytocne.
Switch spravi to iste (pravdepodobne, zalezi od implementacie), akurat skoci cez tabulku caseov na dany podprogram a nebude zbytocne nasobit.
priklad:
nieco ako:
int a;
scanf(&a, "%d");
switch(a)
{
case 1:
putc('a');
break;
case 2:
putc('b');
break;
case 3:
putc('c');
break;
case 4:
putc('d');
break;
case 5:
putc('e');
break;
default:
break;
}
sa prelozi (gcc 6.3.1 -O2, intel x86_64) na:
; scanf
xorl %eax, %eax
movl $.LC0, %esi
leaq 12(%rsp), %rdi
call scanf
; if (a > 5)
cmpl $5, 12(%rsp)
ja .L2
; goto $(L4 + a)
movl 12(%rsp), %eax
jmp *.L4(,%rax,8)
.section .rodata
.align 8
.align 4
.L4:
.quad .L2
.quad .L3
.quad .L5
.quad .L6
.quad .L7
.quad .L8
.section .text.startup
.L6:
movl $99, %edi
call putc
.L2:
xorl %eax, %eax
addq $24, %rsp
.cfi_remember_state
.cfi_def_cfa_offset 8
ret
.L8:
.cfi_restore_state
movl $101, %edi
call putc
jmp .L2
.L3:
movl $97, %edi
call putc
jmp .L2
.L5:
movl $98, %edi
call putc
jmp .L2
.L7:
movl $100, %edi
call putc
jmp .L2
to znamena 2 x goto.
Mimo switche, ktery uz ti napovedeli, je tu jeste moznost tabulka referenci na fuknce nebo first class funkci (pripadne nejaka obdoba, jako jsou ve "stare" Jave objekty implementujici nejake rozhani atp.). Zalezi na tom, co mas k dispozici a co presne chces (switch dava smysl, pokud to delas jednou, tabulky ti umozni mit stejne rozhodnuti parametrizovane atp).
Niekedy je použitie GOTO najlepšia možnosť (error handling, atd.). Viz https://github.com/torvalds/linux/search?utf8=%E2%9C%93&q=goto&type=
Donald Knuth, Structured Programming with go to Statements (1974)
Ke stažení třeba zde http://wiki.c2.com/?StructuredProgrammingWithGoToStatements
Tak až se vám udělá lépe, tak si to pročtěte a třeba se vám rozsvítí.
Já hodně programuji implementace stavových automatů. Je velmi zábavné sledovat, jak se nováčci rozděsí, když zjistí, že můj hlavní pracovní nástroj je goto. Část z nich se snaží již nikdy nepřiblížit do mé přítomnosti, aby se ďáblovo znamení nedostalo i na ně. Část se mě ale pokusí zachránit a vysvětlit mi, jak je goto strašné a jak při každém jeho použití umře koťátko. A vytáhnou různé důkazy, že každé použití goto lze odvrátit nějakými prostředky strukturovaného programování. Myslí si, že to nevím - a nechápou, jak mohu používat goto, i když to vím.
A zcela nejzábavnější je, nechat je řešit řešit nějaký praktický problém (např. stavový automat se značným počtem stavů, náročný na rychlost, ale na slabém HW a současně nezneužívat přerušení k tomu, k čemu se používat nemají). To jsou skvosty, jaké šílené a neudržovatelné konstrukce ti kluci vytvoří, jenom aby nemuseli použít goto.
A úplně vrchol je, že když se to řeší v asembleru, tak je nepodmíněný skok samozřejmě v pořádku. Ale pokud by si měli ušetřit práci a psát to v C, tak si práci "ušetří" tak, že jim to trvá 3x déle a odezva programu pak někdy taky tak :-D
Nic proti goto. Narozdíl od zdejších frikulínů vám ho tady nebudu vydávat za svatokrádež.
Třeba může elegantně řešit obsluhu chybového stavu - záleží na situaci...
Ale.
Taky často píšu konečné automaty a už před dávnými léty jsem v C zaznamenal existenci velkého vynálezu: switch - case - break - default. Zkuste se na to mrknout.
záleží na situaci, napriklad kdyz ti zalezi na rychlosti (threading interpret bajtkodu), tak s primym goto usetris 1-2 skoky na kazdy opkod na jediny skok. Ovsem musi to podporovat prekladac, neni to ANSI C (gcc to samozrejme podporuje).
Viz napriklad JamVM, kde je porad mozny pouzit switch-case, ale je to pomalejsi.
pro ten threaded interpret se mysli 'computed goto':
http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
Zajimave cteni o tom, proc to tak funguje a proc je to rychlejsi:
http://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables
Z cehoz je ve finale ... jmp. Respektive cela sekvence vsemoznych skoku, zato kdyz nekdo napise primo do zdroje goto, tak to prekladac ... neprekvapive prelozi ... jako jeden skok.
Vis on se da naprosto prehledne napsat i velmi obsahlej kus asm, pokud dotycnej vi co dela, tak je pak i radost to cist, protoze je ucel zjevny. Kdyz neco pise prase, tak je uplne jedno v cem to pise a jaky konstrukce vyuziva.
Plati spis naprosto naopak, cim sofistikovanejsi je jazyk ve kterym prase pise, tim vetsi prasanu se mu povede stvorit. Pak to vypada tak, ze zobrazeni bitmapy 800x600 na monitoru potrebuje GB ram ... a pri nacteni druhy se mu to zhrouti, protoze prave dosla ram ... ono to prece uvolnuje samo ...
Pokud se dobře pamatuji, tak pra pra původní swich-case bylo generováno překladači jako tabulka jump adres pro celý rozsah switch proměnné (tehdy obvykle 8bit). Což bývalo superrychlé, ale také supernáročné na paměť (z tehdejšího pohledu).
U starších kódů a u novějších starých, kde se ale chtěla zachovat kompatibilita se starými překladači (pro stejnou paměťovou náročnost), proto nebývalo switch-case výhodné pro malou část "case" případů. A pokud bylo třeba rychlosti, jmp (goto) to řešil.
Já se bez mučení přiznám, že většinu realizací konečného automatu bych mohl dělat pomocí příkazu switch. Určitě ne všechny, ale většinu ano. Hardware by to zvládl jak rychlostí, tak pamětí. Nicméně člověk nikdy neví, kdy za ním přijdou a budou to chtít po pár letech rozšířit - a já se pak dostanu přes limit - a co potom? Mám to celé přepisovat do goto, když jsem to mohl napsat s goto od začátku?
Tím spíš, když stavový automat s goto je IMHO přehlednější, než se switchem?
Příspěvěk jsem napsal trochu provokativně (goto jako hlavní pracovní metoda), nečekal jsem tady úplně seriozní diskuzi, ale díky za radu.
Příkaz switch je mi znám. Překvapivě :) Ovšem, jak už mezitím stihl napsat někdo za mnou, tak řídit stavy přes switch je pomalejší. Což někdy vůbec nemusí vadit - a někdy je to zase zásadní problém.
Mně osobně přijde přeskakovat mezi stavy pomocí goto velmi přirozené. Těžko hledat v programovacím světě něco, co se přechodu mezi dvěma stavy podobá více, než goto. Takže kód, kde jsou přechody psané pomocí goto je IMHO mnohem čitelnější, než kód, kde ty přechody uměle svazujete do switche. Ale to je věc názoru a vkusu každého soudruha. Výkonové zaostávání switche v některých úlohách a HW konfiguracích je argument tvrdý.
Pravé šílenství nastává, když máte stavový automat, který má stovky stavů a switch se stovkami casů vás stojí příliš času. Takže potom celý automat analyzujete a rozbijete ho na několik menších, takže máte hlavní switch s menším počtem "stavových skupin" a každá stavová skupina má potom vlastní malý switch (s jednotkami, nebo nízkými desítkami casů). A tam u těch malých switchů potom garantujete, že u těchto přechodů máte odezvu pod nařízený časový limit (typicky nemáte tvrdý časový limit na všechny přechody, ale na některé, které jsou důležité). Jenže občas se ten stavový automat nedá rozdělit čistě (vlastně je to spíš většinou, že se nedá). Takže máte pak některé stavy ve více skupinách. Anebo některý stav (stav dle zadání např. z grafcetu) realizujete duplicitně (více různými stavy ve vašem programu). A to celé proto, že se někdo štítí příkazu goto :-)
2Gotoista:
Goto/jmp je naprosto vpohode ... jedinej problem je v tom, ze prasata netuse co delaj, skocej z jedny casti kodu do druhy, aniz by jejich slepici mozecek zvladal pobrat co to bude znamenat z hlediska dat.
Takze pokud napriklad inkrementujes ... i++ a skocis nekam vedle, kde mas i-- (ale ono je to jiny i), tak se ti v zavislosti na jazyce/prekladaci/... proste povede dekrementovat to puvodni i
Kdyz pouzijes nongoto strukturu, tak by (hypoteticky) to, ze se neco takovyho nestane, mel zaridit prave jazyk/prekladac. Ale samo zaplatis za to - pameti a vykonem. Tak to bylo vzdycky.
Jen zkratka tehdy zalezelo na kazdym bajtu a kazdym cyklu, a kazdej kdo programoval tak pokud rovnou nevedel, tak mel na stole papir, kde mel narocnost jednotlivych konstrukci vycislenou. Pamatuju casy kdy sme pocitali jestli CPU zvladne danou operaci nez prijde preruseni od zobrazovadla ... pripadne sme resili, jestli pouzit dve instrukce 3 cyklovy nebo 4 jednocyklovy, pricemz to bylo pamet vs vykon.
Znalost narocnosti instrukci a jejich kombinaci na cykly plati tak na velmi primitivni mikrokontrolery nebo pro dnes specialni cipy. U modernich jiz neni narocnost na cykly zcela zrejma a exaktne specifikovana ( a neni to jenom ve spekulativnim vykonavani instrukci(fuj hnusny cesky vyraz)). Vetsinou se to pohybuje v nejakem rozsahu a narocnost se u modernich CPU primo profiluje/testuje/debuguje pres ruzne interface. Je to dan za vykon.
Na cykly deterministicky HW vyzaduje jine zadani a jinou konstrukci. Hlavne pro realtime aplikace. Vsiml jsem si ze v tomto duchu operuje hlavne aerospace divize.
Dalsi vec jsou optimalizace compileru. V zavislosti na zadani je bud compiler ve vypatlanem rezimu kdy bez optimalizaci co najde to prelozi coz se mj. pouziva i na auditovatelny binarni kod nebo kluci s PLCcky co potrebuji resit ladeni SW, pripadne je to ultra optimalizovane ze kdyz je nekde v compileru ktery neprosel testovanim buga, tak se to pekne blbe hleda.
Nektere compilery jsou skoro nadany umelou inteligenci ze pokud je to mozne, tak goto dopadne jako switch:)
dneska ti develove nic nevydrzi :-) Cituji klasika "Opravdový programátor nepotřebuje ke své práci všechny tyto abstraktní pojmy, je úplně šťastný, má-li děrovač štítků, kompilátor FORTRAN 4 a pivo"
Taky:
Opravdovým programátorům nevadí používání GOTO
Opravdový programátor může napsat cyklus DO na 5 stran a nesplete se.
Opravdový programátor miluje aritmetické IF, program dělá zajímavější.
Opravdový programátor píše samomodifikující se programy, zvláště v tom případě, když může ušetřit 20 ns v nejvnitřnějším cyklu.
Opravdový programátor nepotřebuje poznámky - vlastní kód je zřejmý.
Poněvadž FORTRAN neobsahuje strukturované IF, REPEAT, ..... UNITL ani CASE, opravdový programátor nezná starosti s jejich použitím.
Jééééj, ja som sa síce narodil len pár rokov pred tým ako vzniklo toto, a programovanie ma baví odmalička a toto mi pripomína moje programovanie v Pascale keď som písal programy pre radosť a z vlastného záujmu :D Ono sa v podstate nič nezmenilo, sú jednoducho ľudia ktorí programujú a baví ich to, celý ten program je tak prehľadný že na jeho pochopenie stačí tak 5 min. Thumbs up!
Mne se pascal nelibil. Zbytecne moc pismenek, nemam nejak rad dlouhe textove popisy keywordu, debilni definice poli... Hnus fialovej. Akorat tehdy v kakademicke a pedagogicky sfere porad nekdo pyskoval ze pascal je vhodny pro vyuku protoze to tak Niki Wirth navrhl. No a tenhle hloupy jazyk se rozmohl jako mor.
Nejkontroverznejsi je ze Turbo Paskvil byl velmi inovativni na svou dobu a textovy interface turbo vision byl bomba. Nebyt toho debilniho jazyka.
Delfini byly uz docela dobre pouzitelni, kdyz si clovek chtel splacat aplikaci typu automaticke splachovani WC s klikatkem...
To šlo v Turbo C taky. Pokrytecky přiznávám že jsem si asi jako každý programatorsky onanista te dob dělal vlastní knihovnu SVGA obsluznych rutin na Trident karty. Neb standartni knihovna snad používala biosi rutiny jak byla pomalá a neschopna. Nejdriv v osklivem placalu a pak naštěstí v C. Po přechodu na gcc resp pro dosove věci djgcc nejvíc vadila at&t syntaxe vkladaneho asm proti inteli.
Klasicky Pascal saje. To jen TurboPascal balhe pameti byl vyjimka a skvela implementace. Tehdy jsem ho sice mel upriatenej, ale dnes jsu uz bohatej tak bych rad Borlandu neco i zaplatil, existuji jeste? Kdyz jsem presel na TurboC tak jsem sklamane koukal, ze spusteni Run neni okamzite ale se neco kompiluje, linkuje a trva to minuty...
GOTO:
když jsme se na základce v počítačovém kroužku učili programovat ( BASIC ZX Spectrum ), kámošovi uteklo, že na konci řádku se automaticky spouští následující,
takže:
měl každej řádek max dlouhej ( co vydržely nervy při editaci )
a každý končil: : GOTO .. ( další řádek ).