Örvitinn

Jákvæð misnotkun á switch

switch klausan í C/C++ er þannig að enda þarf hvert case með break annars er farið í næsta case. Almenna reglan er sú að allar case klausur skuli enda með break og margir eru þeirrar skoðunar að engar undantekingar séu á þeirri reglu. Ég er annarar skoðunar.

Oftar en einu sinni hef ég lent í tilvikum þar sem snyrtilegasta lausnin er sú að láta lógíkina detta áfram í næsta case. Einfaldasta tilvikið er þar sem maður hefur ekki útfært einhverja valmöguleika og vill skila villu þegar það kemur upp. Í stað þess að hafa svo til sama kóða fyrir hvert tilfelli er hægt að samnýta kóðann. Eftirfarandi tvö dæmu eru jafngild.

enum tegund { A,B,C,D,E };

switch(tegund)
{
   case A:
        // skila villu til notanda
        foo("bla");
        break;
   case B:
        // skila villu til notanda
        foo("bla");
        break;
   case C:
        // skila villu til notanda
        foo("bla");
        break;
   case D:
        // skila villu til notanda
        foo("bla");
        break;
   case E:
         // gera eitthvað gagnlegt
         foo_bar();
         break;
   default:
        // skila villu til notanda

}

switch(tegund)
{
   case A:
   case B:
   case C:
   case D:
        // skila villu til notanda
        foo("bla");
        break;
   case E:
         // gera eitthvað gagnlegt
         foo_bar();
         break;
   default:
        // skila villu til notanda
}

Annar möguleiki er þegar við erum með stigvaxandi lógík. Segjum sem svo að fyrir C þurfi að framkvæma sull(), fyrir B bæði sull() og foobar() og fyrir A þurfi að gera foo(), foobar() og sull(), þá er hægt að gera þetta á tvenna vegu.

switch(tegund)
{
   case A:
        foo();
        foo_bar();
        sull()
        break;
   case B:
        foo_bar();
        sull()
        break;
   case C:
        sull()
        break;
   default:
        // ólöglegt tilvik eða eitthvað slíkt, skila villu
}

switch(tegund)
{
   case A:
        foo();
   case B:
        foo_bar();
   case C:
        sull()
        break;
   default:
        // ólöglegt tilvik eða eitthvað slíkt, skila villu
}

Hugsanlega er þetta síðara dæmi vafasamt. Oft er hægt að leysa sömu vandamál á snyrtilegri hátt en ólíklegt að það sé hægt með minni kóða. Sumum finnst þetta kannski orðið heldur goto-legt og það má taka undir það.

Aftur á móti er þetta skemmtileg lausn að mínu mati og það má ekki vanmeta þann þátt í forritun. Mér hefur a.m.k. þótt það áhugavert þegar ég hef getað notað þetta trix til að einfalda lógík og minnka kóða.

Aftur á móti er spurning hvort kóðinn verður auðlesanlegri, því það skiptir oft meira máli að aðrir geti lesið og skilið kóðann. Ég hef alltaf verið þeirrar skoðunar að það skipti miklu máli og er því lentur í ákveðinni klípu, mér finnst þetta mjög skýrt en líklegast myndu þeir lesa yfir svona kóða ekki taka eftir því að lógikin dettur niður. Það þarf því að skjala þetta ansi vel, en það á líka við um allan C/C++ kóða.

c++
Athugasemdir

Halldór E. - 28/07/04 11:27 #

Nú er ég svo sem ekki fagforritari, en les þó stundum kóða mér til ánægju. Ég verð að segja að þessi aðferð sem þú kynnir er mjög illlæsileg. Hún krefst þess að þú eyðir tíma í að raða upp "case"-unum á fyrirframákveðin hátt og ef það bætist við "case", þá þarftu að eyða tíma í að sjá hvar það á við í röðuninni, eða setja það upp á hefðbundinn hátt neðst (með break þ.e.) og þá er sullumbullið orðið verulega mikið.

Matti Á. - 28/07/04 11:30 #

Nákvæmlega, þetta er klípan :-) En lausnin er skemmtileg þegar hún á við :-P En það er góð regla í forritun að forðast öll trix, a.m.k. ef einhverjar líkur eru á að aðrir þurfi að lesa og viðhalda kóðanum.

Bjarni Rúnar - 28/07/04 12:53 #

Mér finnst þetta engin misnotkun.

Það þarf auðvitað að vega og meta hverju sinni hve læsilegt þetta er og viðhaldanlegt - og þá stangast á sjónarmiðin "slæmt að fjölfalda kóða að óþörfu" og "slæmt ef kóði er brothættur og erfitt að breyta eftirá". Hvort vegur þyngra hlýtur að fara eftir kóðanum hverju sinni.

Í praksís kemur þetta samt frekar sjaldan upp - stigvaxandi lógík er ekki mjög algeng í minni reynslu. Það er hinsvegar mjög algengt að mörg "case" eigi að leiða til sömu hegðunar, og þá einmitt er augljóslega til bóta að ekki sé verið að fjölfalda sama kóðabút aftur og aftur.

Þess má annars geta að þessi eiginleiki forritunarmálsins er eitthvað sem C++ erfði frá C.

Matti Á. - 28/07/04 13:34 #

Stigvaxandi lógík er mjög sjaldgæf, en það er einmitt ástæðan fyrir því að mér finnst svo gaman þegar ég get notað svona lausn :-)

Þetta er að sjálfsögðu allt löglegur C kóði líka, ég skrifa bara aldrei kóða í hreinu C, jafnvel þegar ég skrifa hálfgerðan C kóða í dag notast ég við C++ þýðanda og yfirleitt reyni ég að nota C++ fyrirbæri að einhverju leyti. Breytti textanum hér að ofan og tala nú um C/C++ eins og algengt er.

Oft er notkun á switch klausum í C++ dæmi um hönnum sem mætti laga (ætlaði að segja ranga en hætti við) en stundum kemst maður ekki hjá því að leysa vandamál á þennan hátt. Þetta er þó efni í annan pistil en miðað við tíðnina á forritunarpistlum á þessu bloggi má gera ráð fyrir að einhverjir mánuðir líði þar til ég kem því frá mér :-)