goto

Započeo gagi, 09.11.2010, 13:03

prethodna tema - sledeća tema

gagi

U svim knjigama o C-u negde na samom početku stoji rečenica koja je uglavnom ispisana velikim, zadebljanim slovima - "izbegavajte korišćenje naredbe goto". Kao argument se navodi složena i zamršena struktura koju preterano korišćenje naredbe goto proizvodi ("špageti" kod, "kengurov" kod). Svi tekstovi vezani za struktuirano programiranje su takodje preporučivali isto, čak se na samom početku ere struktuiranog programiranja vodio "slučaj" protiv korišćenja ove naredbe (" A Case Against the Goto Statement", članak koji je napisao Dijkstra a koji je poznatiji kao "Goto Statement Considered Harmful").

Zaista ne treba opisivati koliko sam se začudio kada sam u zaista kvalitetno napisanom kodu naleteo na ovu naredbu. Pomislih da je ovo izolovan slučaj i pošto sam bio u priličnoj gužvi nisam se pozabavio ozbiljno ovim pitanjem. Kada sam ponovo naleteo na ovu istu naredbu u jednom BSP-u prilično sam se zainteresovao. Na kraju, nakon dosta čitanja na ovu temu evo zaključka - goto naredba u nekim slučajevima ne samo da ne treba izbegavati već definitivno predstavlja najbolje moguće rešenje. Ako se pažljivo koristi, ona u pojedinim situacijama doprinosi poboljšanju brzine izvršavanja i smanjuje veličinu koda. Upravo zato su je koristili u kodu na koji sam naleteo.

Siniša Ranđić

Da đavo nije sasvim crn, tj. da se i sa GOTO naredbom mogu pisati strukturirani programi smatrali su mnogi autori. Jedan od gurua savremenog računarstva Donald Knuth je napisao članak pod naslovom "Structured Programming with go to Statement", koji se može preuzeti na adresi http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf.

Marko Аcović

Slazem se sa obe izjave. U principu, treba je izbegavati ali kad ne postoji bolje resenje, zasto da se ne iskoristi. Trebamo biti svesni zasto smo u datom trenutku odabrali goto i sta mogu da budu posledice tog izbora.

gagi

Evo jedne izuzetne diskusije na ovu temu u kojoj je ucestvovao jos jedan od gurua savremenog računarstva, Linus Torvald
http://kerneltrap.org/node/553/2131
Primer koda koji intezivno koristi ovu naredbu se može skinuti na http://www.sics.se/~adam/uip/index.php/Download. Reč je o implementacij TCP/IP steka namenjenoj platformama sa malo resursa (može da trči čak i na osmobitnim kontrolerima). Jedan od mehanizama obaranja veličine koda je upravo naredba goto. Da citiram autora:
uIP is a small implementation of the IP, UDP and TCP protocols (aswell as some basic ICMP stuff). The implementation couples the IP, UDP, TCP and the application layers very tightly. To keep the size of the compiled code down, this code frequently uses the goto statement. While it would be possible to break the uip_process() function into many smaller functions, this would increase the code size because of the overhead of parameter passing and the fact that the optimier would not be as efficient.



vukasheen

Interesantna tema,

efikasnost vs. preglednost, sigurnost
trošenje vremena na izradu koda ili gubitak performansi, što povlači i veću potrošnju energije ( ekologija teba broj 1. :D).

Java ima jedan kompromis što se toga tiče, labele.
Petlje se obeležavaju labelama a posle se može napustiti odgovarajuća petlja pozivanjem break labela ili continue labela.
Ovo ima smisla u višestruko ugnježdenim petljama. Predpostavljam da većina vas to zna ali da se zabeleži možda nekome zatreba
Evo i primera: http://www.wellho.net/resources/ex.php4?item=j704/Bills.java

marjan

Mislim da je preporuka o nekorišćenju goto sasvim na mestu - upravo tamo gde se daje i za cilj joj je da kod programera indukuje naviku pisanja jasnog i strukturiranog koda, pa se onda ovakvi specijalni slučajevi javljaju kao izuzetak koji potvrđuje pravilo. Čini mi se da Liberty (ili tako neki autor) baš navodi da mu je goto bio (bilo?) odlično rešenje svega par puta u životu.
Ako idemo na optimizaciju (opet u nekim slučajevima) ili dublje ka low-level programiranju, goto je očigledno koristan put - pa i JMP je sasvim normalna naredba u asembleru.
Unix is user-friendly—it's just choosy about who its friends are.

holodoc

Citat: vukasheen  09.11.2010, 16:57
Interesantna tema,

efikasnost vs. preglednost, sigurnost
trošenje vremena na izradu koda ili gubitak performansi, što povlači i veću potrošnju energije ( ekologija teba broj 1. :D).

Java ima jedan kompromis što se toga tiče, labele.
Petlje se obeležavaju labelama a posle se može napustiti odgovarajuća petlja pozivanjem break labela ili continue labela.
Ovo ima smisla u višestruko ugnježdenim petljama. Predpostavljam da većina vas to zna ali da se zabeleži možda nekome zatreba
Evo i primera: http://www.wellho.net/resources/ex.php4?item=j704/Bills.java
Nisu labele Java ekskluzivitet :) Čak štaviše labele su ubedljivo jedan od najstarijih izuma programiranja koji se koristio još u vreme BASIC-a. Možda se neko ovde još uvek seća da je za originalan BASIC bilo karakteristično da mu svaka linija koda bude numerisana jednim brojem i to u rastućem nizu a na koji se kod mogao u bilo kom trenutku prebaciti korišćenjem komande GOTO.

Primera radi sledeći kod...
10 REM Test program
20 PRINT "HELLO WORLD!"
30 GOTO 10

Numeracija redova je upravo bila prva varijanta labela a to je izum koji sada ima pa već više od 30 i kusur godina :)

Kasnije su neke savremenije verzije BASIC-a tipa AmigaBASIC uvele mogućnost "numeracije" redova labelama na sledeći način:
POCETAK:
PRINT "HELLO WORLD"
GOTO "POCETAK"

Što se tiče continue i break one su danas sastavni deo bukvalno svakog programskog jezika. Primera radi mali broj ljudi zna da break u okviru switch petlje u stvari nije sastavni deo petlje već da je to potpuno neyavistan element koji može da se izostavi i da se tako realizuje algoritam "propadanja" switch petlje :)
switch(vrednost){
    case 1 :
        // Kod za skucaj 1...
        break;
    case 2 :
        // Kod za vrednost 2...
    case 3 :
        // I ovo ima da se izvrsi ukoliko je vrednost 2...
    default :
        // Default grana...
}

continue je isto tako poprilično standardan u algoritmima za prevremeni izlazak iz petlje.
include <stdio.h>
#include <stdlib.h>

int i, j, suma = 0 ;

int main(){
for(i=0; i < 10; i++){
        for(j = 0; j < 10; j++){
                if(i > 5){
                        continue;
                }
                suma += j;
        }
}
printf("%d\n",suma);
return EXIT_SUCCESS;
}

<?php
abstract class Ignorance extends Stupidity implements Unavoidable 
    private function 
__construct(){
        
parent::__destruct();
    }; 

// EOF -> life.php

Marko Аcović

Samo mala korekcija, ako dozvoljavas :)

  • break iskace iz petlje (iz najdublje petlje ako ima vise ugnezdjenih petlji)
  • continue ide na kraj petlje, tj. prelazi na sledecu iteraciju

gagi

Za one koji su bili lenji da procitaju moje linkove, izdvojicu jedan post Linusa Torvalda u kome on iznosi neke argumente u prilog goto naredbe. Ovakve stvari su upravo ono sto me je iznenadilo jer su donekle narusile moje poimanje proceduralnog programiranja i redefinisalo moje stavove. Zaista, na prvu loptu (ili na prvi goto) sam pomislio da je pisao neki amater i verovao sam da me ceka "spaghetti a la carbonara" jer svi su tako govorili. Naisao sam na zaista elegantna i mocna resenja koja su kod ucinila lepsim, brzim i manjim a to je nesto cemu po mom skromnom misljenju treba stalno teziti.

From: Linus Torvalds
Subject: Re: any chance of 2.6.0-test*?
Date:    Sun, 12 Jan 2003 12:22:26 -0800 (PST)

On Sun, 12 Jan 2003, Rob Wilkens wrote:
>
> However, I have always been taught, and have always believed that
> "goto"s are inherently evil.  They are the creators of spaghetti code

No, you've been brainwashed by CS people who thought that Niklaus Wirth
actually knew what he was talking about. He didn't. He doesn't have a
frigging clue.

> (you start reading through the code to understand it (months or years
> after its written), and suddenly you jump to somewhere totally
> unrelated, and then jump somewhere else backwards, and it all gets ugly
> quickly).  This makes later debugging of code total hell. 

Any if-statement is a goto. As are all structured loops.

Ans sometimes structure is good. When it's good, you should use it.

And sometimes structure is _bad_, and gets into the way, and using a
"goto" is just much clearer.

For example, it is quite common to have conditionals THAT DO NOT NEST.

In which case you have two possibilities

- use goto, and be happy, since it doesn't enforce nesting

   This makes the code _more_ readable, since the code just does what
   the algorithm says it should do.

- duplicate the code, and rewrite it in a nesting form so that you can
   use the structured jumps.

   This often makes the code much LESS readable, harder to maintain,
   and bigger.

The Pascal language is a prime example of the latter problem. Because it
doesn't have a "break" statement, loops in (traditional) Pascal end up
often looking like total shit, because you have to add totally arbitrary
logic to say "I'm done now".

      Linus

holodoc

10.11.2010, 01:40 #9 Poslednja Izmena: 10.11.2010, 02:04 od holodoc
Nemam nameru da ocrnjujem čoveka ali opšte je poznata činjenica da Torvalds danas prevashodno živi od slave koju je stekao u svojim studentskim danima i potrebe da se stalno utrkuje sa Stallmanom ko je odmah prvi do Pape :)

Povodom nekih od njegovih izjava navedenih u prethodnom postu mogu samo da kažem da imam osećaj da čovek i dalje misli da su kompajleri ostali na nivou na kojem su bili u vreme dok je on studirao :) Kompajleri a i tehnologija razvoja softvera su se u poslednjih nekoliko godina toliko razvile da su svi ovi pokušaji mikrooptimizacije i traženja litra rakije u buretu vina čist senzacionalizam  B-) Baš zato što većina kompajlera danas potpuno ignoriše način na koji je kod pisan i što kompajleri automatski biraju način optimizacije koda da bi postigli bolje rezultate. Drugim rečima, raditi ovakav tip mikrooptimizacije koda čisto zbog koda i navodnog povećanja performansi je u današnjim uslovima skoro potpuno gubljenje vremena jer opcije pri pisanju koda nisu tu da bi olakšale posao kompajlerima već onima koji pišu kod :)

To opet ne znači da se prema resursima mašine koja izvršava krajnji kod treba odnositi kao svinja prema masnom džaku :) Dakle poptuno podržavam optimizacije koje imaju veliki koeficijent odnosa dobijenih performansi i uloženog vremena ali ovakve tipove gubljenja vremena...Hm... Pa ne baš   >:D<
<?php
abstract class Ignorance extends Stupidity implements Unavoidable 
    private function 
__construct(){
        
parent::__destruct();
    }; 

// EOF -> life.php

holodoc

Citat: marko_gm  10.11.2010, 00:36
Samo mala korekcija, ako dozvoljavas :)

  • break iskace iz petlje (iz najdublje petlje ako ima vise ugnezdjenih petlji)
  • continue ide na kraj petlje, tj. prelazi na sledecu iteraciju
Istina nisam baš upotrebio precizne definicije ali cenim da je poruka jasna bila iz koda :)
<?php
abstract class Ignorance extends Stupidity implements Unavoidable 
    private function 
__construct(){
        
parent::__destruct();
    }; 

// EOF -> life.php

gagi

Citat: holodoc  10.11.2010, 01:40

Povodom nekih od njegovih izjava navedenih u prethodnom postu mogu samo da kažem da imam osećaj da čovek i dalje misli da su kompajleri ostali na nivou na kojem su bili u vreme dok je on studirao :) Kompajleri a i tehnologija razvoja softvera su se u poslednjih nekoliko godina toliko razvile da su svi ovi pokušaji mikrooptimizacije i traženja litra rakije u buretu vina čist senzacionalizam  B-) Baš zato što većina kompajlera danas potpuno ignoriše način na koji je kod pisan i što kompajleri automatski biraju način optimizacije koda da bi postigli bolje rezultate.

Mislim da grešiš jer razmišljaš kroz prizmu desktop i serverskih mašina. U tim sferama sve ovo i nije tako bitno jer je dodatni gigabajt memorije za jedan ili dva servera jeftiniji od jednog programerskog sata pa se ne isplati razmišljati o takvim stvarima. Ti uvek možeš da proširiš tvoj server tako da te baš briga kakav kod pravi Java, C# ili PHP. Sa druge strane, taj isti kernel se nalazi na gomili platformi skromnih hardverskih mogućnosti. Kada se tvoja aplikacija izvršava na takvoj platformi, onda ti je VEOMA bitno kako je kod pisan i šta će kompajler generisati jer nemaš mogućnost kupovine dodatnih resursa (jer je povećane od jednog dolara u seriji od 5 miliona mnogo i to ti niko neće dozvoliti pa ćeš i te kako da se zamlaćuješ ovakvim gubljenjem vremena).

marjan

Super je sve to, ali nazivati Wirtha neznalicom je... hm, malo preterano, ne?
Unix is user-friendly—it's just choosy about who its friends are.

holodoc

10.11.2010, 17:00 #13 Poslednja Izmena: 10.11.2010, 17:07 od holodoc
Citat: gagi44  10.11.2010, 11:47Mislim da grešiš jer razmišljaš kroz prizmu desktop i serverskih mašina. U tim sferama sve ovo i nije tako bitno jer je dodatni gigabajt memorije za jedan ili dva servera jeftiniji od jednog programerskog sata pa se ne isplati razmišljati o takvim stvarima. Ti uvek možeš da proširiš tvoj server tako da te baš briga kakav kod pravi Java, C# ili PHP. Sa druge strane, taj isti kernel se nalazi na gomili platformi skromnih hardverskih mogućnosti. Kada se tvoja aplikacija izvršava na takvoj platformi, onda ti je VEOMA bitno kako je kod pisan i šta će kompajler generisati jer nemaš mogućnost kupovine dodatnih resursa (jer je povećane od jednog dolara u seriji od 5 miliona mnogo i to ti niko neće dozvoliti pa ćeš i te kako da se zamlaćuješ ovakvim gubljenjem vremena).
Situacija je upravo suprotna zato što dok na zasebnoj mašini imaš malu konkurentnost (često samo jedan korisnik) na serverskim mašinama je konkurentnost neuporedivo veća i meri se neretko i hiljadama zahteva u minuti a svaki zahtev je minimlano jedan thread ili proces :)

Veruj mi poslednjih nekoliko godina bavim se isključivo razvojem web aplikacija i upgrade hardvera je poslednja stvar na koju će se klijent odlučiti ako ima odvojene dedicated servere a da ne pričam o prelasku na skuplji tarifni paket na nekom VPS ili ne daj bože shared hostingu za koje u slučaju intenzivnijih aplikacija troškove neće podmiriti ni prodaja svih zdravih organa koje poseduješ [-( Za to uglavnom postoji nekoliko razloga:

1) Imaš tu "sreću" da klijent dovoljno dobro ignoriše realne zahteve  aplikacije i pravda se "neopravdanim troškovima za nešto što radi i sad kako treba".
2) Serveri ne koriste baš tako jeftinu memoriju jer zahtevaju izrazito skupu ECC memoriju od kvalitetnih proizvođača tako da pločica po pločica uskoro ispadne čestomesečna plata programera a i jednog dana će ponestati slotova za pločice :)
3) Downtime na bilo kom serveru koji nije deo clustera znači gubljenje vremena i nedostupnost web aplikacije a to u prevodu za klijenta znači gubitak vremena i novca tako da se samo sat vremena nefunkcionisanja sajta na prilično posećenim sajtovima i web aplikacijama smatra katastrofom :D

Ovo poslednje je možda jedna od najbitnijih stavki i pokušaću da ilustrujem na ličnom iskustvu. Naime sećam se da sam nakon neke vanredne situacije na serverima jednog od klijenata morao privremeno da koristim servis koji se nalazi na serveru CSL laboratorije (u pitanju je GeoIP aplikacija na http://csl.tfc.kg.ac.rs/~brezanac/geoip/) na koji sam ja posle sređivanja problema poptuno zaboravio. Nekoliko dana kasnije klijent se javlja i paniči kako su svi njegovi sajtovi (ukupno 4 komada) offline i da ne može da im se pristupi. Za njega koji čak 90% prihoda od krajnje unosne industrije zasniva na online prodaji i negovanju svoje baze vernih kupaca to je katastrofa naravno :) A zbog čega? Ispostavilo se da se toga dana na TFC-u zapalila razvodna kutija pa je struja bila isključena na celom fakultetu :I Kompletna priča se završila skoro četvoročasovnim offline statusom svih sajtova i poprilično velikim gubitkom zarade klijenta tog dana tako da od tada strogo vodim računa da čak i backup rešenja za hitne situacije imaju backup :)

Sa druge strane što se tiče pojedinačnih mašina sa malom konkurentnošću (tipično jedan korisnik) one ne gube mnogo na performansama kada se recimo prebaci maksimalna količna memorije koju računar poseduje jer je čak i virtuelna memorija (page file) dovoljno brz da odradi zahteve sa veoma malim kašnjenjem. Sa druge strane kada server prebaci memorijsko ograničenje u situaciji sa velikom konkurentnošću isti plaća velike penale zbog nesposobnosti CPU-a da isprati sve manipulacije nad virtuelnom memorijom i server se jednostavno guši.

Da skratim priču... Ja nigde nisam naveo da je optimizacija loša stvar. Čak štaviše mogu slobodno da kažem za sebe da sam poprilični paćenik kada je optimizacija u pitanju ali i da pokušavam da se odviknem od navike da baš sve mora da optimizujem na šta naletim jer se takva rabota jednostavno ne isplate kada se uporedi dodatno uloženo vreme i dobijeni rezultat. Takođe, jedna je stvar ako neko piše aplikacije za svoje potrebe pa želi da mu kod bude što je moguće optimizovaniji (recimo kad radi na nekom svom projektu) i nema zadate rokove ali u svakodnevnim uslovima kad ti klijenti dahću za vratom i traže da vide rezultate u roku od odma' ne možeš da razmišljaš o takvim stvarima :)

Ja ću samo navesti da lično ne volim niti podržavam korišćenje bezuslovnih skokova jer isto tako kao što Torvalds kaže da je svaki "if" u stvari "goto" i svaki "goto" je jedan "if" :) Kako? Jednostavno. Veoma su retke situacije da je sam bezuslovni skok potpuno bezuslovan već svakom skoku uvek prethodi neki uslov. U switch petljama (uslovno rečeno) break-u uvek prethodi neki case i ima mali milion sličnih primera. Zašto onda komplikovati kod i činiti ga nečitljivim? Zašto veštački prekidati tokove akcija?

Inače lično bih voleo da vidim algoritam koji ne može efikasno da se realizuje isključivo putem koda bez bezuslovnih skokova :)
<?php
abstract class Ignorance extends Stupidity implements Unavoidable 
    private function 
__construct(){
        
parent::__destruct();
    }; 

// EOF -> life.php

gagi

Citat: holodoc  10.11.2010, 17:00
Ovo poslednje je možda jedna od najbitnijih stavki i pokušaću da ilustrujem na ličnom iskustvu. Naime sećam se da sam nakon neke vanredne situacije na serverima jednog od klijenata morao privremeno da koristim servis koji se nalazi na serveru CSL laboratorije (u pitanju je GeoIP aplikacija na http://csl.tfc.kg.ac.rs/~brezanac/geoip/) na koji sam ja posle sređivanja problema poptuno zaboravio. Nekoliko dana kasnije klijent se javlja i paniči kako su svi njegovi sajtovi (ukupno 4 komada) offline i da ne može da im se pristupi. Za njega koji čak 90% prihoda od krajnje unosne industrije zasniva na online prodaji i negovanju svoje baze vernih kupaca to je katastrofa naravno :) A zbog čega? Ispostavilo se da se toga dana na TFC-u zapalila razvodna kutija pa je struja bila isključena na celom fakultetu :I Kompletna priča se završila skoro četvoročasovnim offline statusom svih sajtova i poprilično velikim gubitkom zarade klijenta tog dana tako da od tada strogo vodim računa da čak i backup rešenja za hitne situacije imaju backup :)

Sa druge strane što se tiče pojedinačnih mašina sa malom konkurentnošću (tipično jedan korisnik) one ne gube mnogo na performansama kada se recimo prebaci maksimalna količna memorije koju računar poseduje jer je čak i virtuelna memorija (page file) dovoljno brz da odradi zahteve sa veoma malim kašnjenjem. Sa druge strane kada server prebaci memorijsko ograničenje u situaciji sa velikom konkurentnošću isti plaća velike penale zbog nesposobnosti CPU-a da isprati sve manipulacije nad virtuelnom memorijom i server se jednostavno guši.



Iz ovog zaključujemo da ne pričamo baš o istim stvarima. Ti isključivo razmišljaš o desktop računaru i korisniku koji sedi za tastaturom. Pored računara čiji si vlasnik ti, verovatno i tvoja veš-mašina, automobil i televizor imaju  svoje"računare" a ako imaju sreće da su novije generacije možda ga imaju i toster i četkica za zube. :) Tu se i te kako vodi računa o resursima i brzini izvršavanja, posebno u domenu hard real-rime sistema. Zaista bih voleo da ne moram da se zamlaćujem ovakvim stvarima, ali na žalost moram.

Ono što sam želeo da istaknem jeste da sam u praksi naišao na nešto što odudara od onog što smo učili u knjigama i da sam malo promenio razmišljanje u pogledu proceduralnog programiranja. Jednostavno,  postoje slučajevi u kojima goto čini kod preglednijim od bilo kog drugog rešenja a to je ono što mi ranije ne bi palo ni na kraj pameti. Uz to, mogu znatno da unapredim performanse a to mi je dovoljan razlog da u svojim rešenjima žrtvujem preglednost koda jer na raspolaganju nemam mnogo resursa i ne mogu ih proširiti. Možda grešim i treba da odbacim ovo rešenje tako da bih voleo da čujem argumente koji su bazirani na konkretnim primerima.