Diskuze: Náhrada konstrukce switch

C# .NET .NET (C# a Visual Basic) Náhrada konstrukce switch American English version English version

Avatar
Petr Nymsa
Redaktor
Avatar
Petr Nymsa:

Nedávno zde proběhla diskuze o "špatnosti" switche a jeho případným nahrazení. Tak mě teď napadlo, jak řešit tyto úlohy jinak než přes switch ?

  1. Generování něčeho zároveň s náhodným směrem => switch použit pro rozeznání směru
  2. Načtení mapy ze souboru. Mapa je uložena ve formátu 1;0;10;8;2 ... každé číslo značí ID nějakého políčka -> tráva, řeka, start,cíl... => switch použit pro rozeznání a přiřazení správné textury, ID apod.

Určitě mě časem napadnou další :P. Já osobně na konstrukci sěitche nevidím nic špatného pokud je použit nějak rozumně. Rozhodně lepší než použít if,else if například u 2. příkladu.

Rozhodně nechci vyvoalt válku, jako je občas zvykem ! :) Každopádně chci slyšet názory a hlavně jiné možné řešení alespoň techto dovu příkladů. První příklad by mohl být teoreticky řešen například přes delegáta, když každý vygenerovaný směr měl přiřazenou určitou metodu ale nevím jestli si pomůžeme.

Díky za názory

Odpovědět 16.5.2013 19:40
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Nymsa
David Čápka:
  1. Nedávno jsem řešil, do směrx a směry vygeneruješ čísla od -1 do 1. Žádný switch není třeba a se směrem lze pak skvěle dělat další výpočty (třeba pohyb pomocí násobení).
  2. Proč switch? Pojmenuji si textury podle čísel. Když to z nějakého důvodu nechci, udělám slovník nebo zvolím vhodnější formát mapy.
Nahoru Odpovědět 16.5.2013 20:20
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na David Čápka
Petr Nymsa:
  1. Vygeneroval jsem si směr 1-4 (vlevo,vpravo,na­horu,dolu) podle toho jsem musel pokládat bloky apod. Jak to řešit jinak ?
  2. Ano slovník by šel, zrovna teď si ho připravuju pro hru. Načítání textur. Trochu mimo hlavní téma - je vhodné použít statiku pro načtené textury, abych je měl přístupné všude ? Klidně mi hned řekni že to je blbost, vyřeším si to jinak :)
Nahoru Odpovědět 16.5.2013 20:33
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
TomBen
Redaktor
Avatar
Odpovídá na David Čápka
TomBen:

V gml například vidím switch docela rád, protože nenásilně vede k používání
hodnoty default a k přemýšlení nad situací, kdy je vstupem nečekaná hodnota.
Ale to je výhoda hlavně pro začátečníky, kteří rádi zapomínají, že se vždycky
najde někdo, kdo na výzvu "Zadej číslo: " napíše: "dvaatřicet". :-)

Editováno 16.5.2013 21:02
Nahoru Odpovědět 16.5.2013 21:01
Za posledních 200 miliónů let se nic zvláštního nestalo, akorát dinosauři vymřeli a opice se naučily programovat.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Nymsa
David Čápka:

1-4 je nic neříkající hodnota, proto ji budeš pořád ifovat a switchovat. Jak jsem psal, udělej si smerx a smery.

Nahoru Odpovědět 16.5.2013 21:03
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Nymsa
David Čápka:

Musíš si uvědomit, že switch je vlastně náhrada if ... else if ... else if ... A když potřebuješ někde v programu takovouhle hrůzu, něco je špatně.

Editováno 16.5.2013 21:04
Nahoru Odpovědět 16.5.2013 21:04
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Nymsa
David Čápka:

Aby těch odpovědí nebylo málo, tady je jak položit loď v daném směru do pole:

Generování:

int smerx = 0;
int smery = 0;
while (Math.Abs(smerx) == Math.Abs(smery))
{
    smerx = random.Next(-1, 2);
    smery = random.Next(-1, 2);
}

A položení lodě ve směru:

int i = x;
int j = y;

for (int k = 0; k < delka; k++)
{
        pole[i, j] = delka;
        i += smerx;
        j += smery;
}

Můj žák tam měl nejprve switch na směr jako ty, kód jsem zmenšil na 1/4 a ještě ubral na generování směru.

Nahoru Odpovědět 16.5.2013 21:08
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na David Čápka
Petr Nymsa:

Dobře přesvědčil jsi mě :). Přemýšlím zda vůbec ukázat ten kód s tím generováním směru. Generuje to bludiště -> pokládá zdi v náhodném směru. Ještě to občas holt mám pěkně ošklivý :[

Mě to asi nedá a posílám kód, je opravdu strašný, je starší a doteď jsem ho neopravil :` i když na něm zakládám novou hru. Musím to přepsat, já vím :D

Bacha na infarkt :)

private bool Put(Point put, int smer, int n)
       {
           int pocet = 0;

           switch (smer)
           {
               case 0: // vlevo
                   for (int x = put.X; x > 0; x--)
                   {
                       if (pole[x, put.Y] == 2)
                       {
                           pole[x, put.Y] = -1;
                           position.Remove(new Point(x, put.Y));
                           pocet++;
                           if (pocet >= n)
                               break;
                       }
                       else if (pole[x, put.Y] == 0)
                           pole[x, put.Y] = -1;
                       else break;
                   }

                   break;
               case 1: //vpravo
                   for (int x = put.X; x < sizeX; x++)
                   {
                       if (pole[x, put.Y] == 2)
                       {
                           pole[x, put.Y] = -1;
                           position.Remove(new Point(x, put.Y));
                           pocet++;
                           if (pocet >= n)
                               break;
                       }
                       else if (pole[x, put.Y] == 0)
                           pole[x, put.Y] = -1;
                       else break;
                   }

                   break;
               case 2: // nahoru
                   for (int y = put.Y; y > 0; y--)
                   {

                       if (pole[put.X, y] == 2)
                       {
                           pole[put.X, y] = -1;
                           position.Remove(new Point(put.X, y));
                           pocet++;
                           if (pocet >= n)
                               break;
                       }
                       else if (pole[put.X, y] == 0)
                           pole[put.X, y] = -1;
                       else break;
                   }

                   break;
               case 3: //dolu
                   for (int y = put.Y; y < sizeY; y++)
                   {
                       if (pole[put.X, y] == 2) // nalezl základnu
                       {
                           pole[put.X, y] = -1; // položí zed
                           position.Remove(new Point(put.X, y)); // vymaže základnu
                           pocet++;
                           if (pocet >= n)
                               break;
                       }
                       else if (pole[put.X, y] == 0)
                           pole[put.X, y] = -1;
                       else break;
                   }
                   break;
           }
           if (position.Count == 0)
               return true;
           else
               return Put(position[rnd.Next(0, position.Count)], rnd.Next(0, 4), rnd.Next(2, position.Count)); ;


       }
Nahoru Odpovědět 16.5.2013 21:18
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
David Hynek
Redaktor
Avatar
David Hynek:

jestli máte neznámou X, které může mít hodnoty 1 až něco, a ty hodnoty mají zastupovat nějaký obsah, tak bych to neřešil přes switch, ale přes pole, kde by každá hodnota X měla svůj obsah. Procházet to něčím komplikovanějším, je dle mého názoru zbytečná zátěž pro výpočty. Samozřejmě, by tam musela být kontrola, zda obsahové pole tuto X hodnotu má a když ne, vsadila by se hodnota univerzální, třeba zeď.

Nahoru Odpovědět 16.5.2013 21:18
Čím víc vím, tím víc věcí nevím.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Nymsa
David Čápka:

No, to je přesně ono :) Máš tam 4x to samé, když uděláš směr jak jsme ti psal, budeš mít 4x kratší kód a ještě kratší generování.

Nahoru Odpovědět 17.5.2013 7:42
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na David Hynek
David Čápka:

Na to by byla rychlejší nějaká hashovací kolekci, ale pořád lepší než switch :)

Nahoru Odpovědět 17.5.2013 7:43
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Martin Dráb
Redaktor
Avatar
Martin Dráb:

Teoretická výhoda konstrukce switch spočívá v tom, že ji překladač může za jistých podmínek optimalizovat a zajistit tím lepší výkon programu (oproti variantě programu s if-selse-...-if-else). Ale půjde to udělat royumně jen za předpokladu, že se v rámci konstrukce switch ptáte na hodnoty v malém rozsahu (například z enumu).

Překladač může bloky kódu následujícími za jednotlivými větvemi (mezi slovy case a break), aderesy těchto bloků umístit do tabulky (pole) a potom prováděcí kód může vypadat nějak takto:

cmp eax, 8       ; prvni hodnota vyssi nez cokoliv v case prikazu
jge default
jmp bloky[eax]

Což je jistě rychlejší než dělat sekvenci if-else-if-else-...-else, samozřejmě co se týče počtu instrukcí. Ale nedá se to takto vyoptimalizovat vždycky, jak už jsem psal výše. Tenhle kód by se asi dal použít, pokud se v rámci konstrukce switch ptám na hodnoty 0 až 7 a ostatní posílám do větve default. Což by splňovalo použití výčtové proměnné (enum).

Jinak co se týče toho problému se směry, tak (jak už tady zaznělo), stačí si daný směr reprezentovat dvojicí (x;y), který budou nabývat hodnot -1,0,1. Pokud se ti používání dvojice nehodí, můžeš ji zakódovat do jednoho čísla, pak ale záleží na tom, kolik hodnot v každé ose dovolíš.

Nahoru Odpovědět  +2 17.5.2013 10:19
2 + 2 = 5 for extremely large values of 2
Avatar
Kit
Redaktor
Avatar
Odpovídá na Petr Nymsa
Kit:

Jak jsem už psal, switch sám o sobě špatný není, ale často se používá špatně. Převádíš dvě hodnoty (x, y) do jedné (0-3), což se dá označit za serializaci. Tuto hodnotu dáváš do switche, který vlastně dělá deserializaci. Jak známo, serializace a deserializace obecně s sebou nese režii. Pokud přenášíme data mezi objekty, použijeme opět objekt či kontejner, které mají tu režii nižší a neztrácí se sémantika (proč je hodnota 2 nahoru?)

Představ si, že bys do svého příkladu chtěl zapracovat i pohyb po úhlopříčce. Tak asi ten infarkt dostaneš. Nezapomeň umět i stát na místě.

Nahoru Odpovědět 17.5.2013 10:33
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na Kit
Petr Nymsa:

Ano máš pravdu :) A psal jsem, tento kód je starší a nyní už to mám přepsané :). Proč 2 nahoru ? Protože jsem si to tak řekl. Switch ještě by se dal použít jak psal Vrtule při enums.

Nahoru Odpovědět 17.5.2013 10:43
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
Kit
Redaktor
Avatar
Odpovídá na Petr Nymsa
Kit:

Další možností je nadefinovat konstantu POHYB_NAHORU = 2.

Pokud pohyb nahoru přeneseš jako dvojici (x=0, y=-1), tak realizaci pohybu můžeš udělat pouhým opakovaným přičítáním x a y k hodnotám v put. A nebudeš potřebovat vůbec žádný switch.

Editováno 17.5.2013 10:54
Nahoru Odpovědět 17.5.2013 10:52
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na Kit
Petr Nymsa:

Za chviličku postnu upravenou a zkrácenější verzi a budu zvědavý na názor zda je to lepší :)

Nahoru Odpovědět 17.5.2013 11:04
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Nymsa
David Čápka:

Ono se musíš prostě zamyslet nad tím, co to pohyb je. Pohyb má 2 složky, posun po 2 osách. Proto ho takto i uložím. Je to jako kdybys switchoval barvy jako 1-modrá, 2 zelená a podobně. Barva má 3 složky RGB, měla by se tedy i takto uložit. Switch jsem již nepoužil strašně dlouho v žádné své aplikaci, když se správně navrhuje, není potřeba. Hodně toho zmůže také reflexe.

Nahoru Odpovědět 17.5.2013 11:20
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na David Čápka
Petr Nymsa:

No při generování si nic neukládám a dávám za pravdu, switch se dá nahradit :). Zneužil jsem tvého příkladu a při rychlejší úpravě to vypadá takto. To náhodné sY je ošetření pouze toho aby vzniklo vždy toto (nechci diagonální směry). Tj vdy musí vzniknout tyto možné kombinace

když Sx == -1 || Sx==1 tak Sy= 0 jinak Sx=0 tak Sy = -1 ||Sy=1

Šlo by to ještě nějak více nahradit ? Každopádně zde celá metody která pokládá zdi

private bool Put(Position put, int n)
        {
            int sX = rnd.Next(-1, 2);
            int sY = 0;
            if (sX == 0)
            {
                while (Math.Abs(sY) != 1)
                {
                    sY = rnd.Next(-1, 2);
                }
            }
            int pX = put.X;
            int pY = put.Y;
            for (int p = 0; p < n; p++)
            {
                if (pX >= 0 && pX < pole.GetLength(0) && pY >= 0 && pY < pole.GetLength(1))
                {
                    if (pole[pX, pY] == 2)
                        Base.Remove(new Position(pX, pY));
                    else if (pole[pX, pY] == 1)
                        break;
                    pole[pX, pY] = 1;
                }
                else break;
                pX += sX;
                pY += sY;
            }
            if (Base.Count == 0)
                return true;
            else
                return Put(Base[rnd.Next(0, Base.Count)], rnd.Next(2,9));
        }

Parametr put je start nového pokládání, n je maximální počet pokládání. Je tam nastavený roszsah 2-8

Editováno 17.5.2013 11:27
Nahoru Odpovědět 17.5.2013 11:26
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Nymsa
David Čápka:

Ta má verze je také bez diagonálních směrů, proto je tam Abs. Vyleze z toho vždy jeden ze 4 směrů.

Nahoru Odpovědět 17.5.2013 11:30
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na David Čápka
Petr Nymsa:

No právě že u tebe vyleze například že x a y = 1 ? Nebo jsem úplně mimo ? :D Pokud to tak je, tak vlastně pokládám například vždy v poli o x+1, y+1 => pokládám diagonální směr.

Nahoru Odpovědět 17.5.2013 11:32
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Petr Nymsa
David Čápka:

Ne, kontroluji právě aby nebyly stejné abs. hodnoty, takže 1,1 vylézt nemůže, ani -1,1 a podobně, stejně tak i 0,0.

Nahoru Odpovědět 17.5.2013 13:47
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Kit
Redaktor
Avatar
Odpovídá na Petr Nymsa
Kit:

Podle mne je jednodušší ten směr do x, y vygenerovat z intervalu 0..3 třeba takto:

int[] Pole = {1, 0, -1, 0, 1};
smer = random.Next(0, 4);
x = Pole[smer];
y = Pole[smer+1];
Editováno 17.5.2013 14:16
Nahoru Odpovědět  +2 17.5.2013 14:15
Vlastnosti objektů by neměly být veřejné. A to ani prostřednictvím getterů/setterů.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na David Čápka
Petr Nymsa:

Jo vidím to a uvědomil jsem si to když jsem opouštěl školu :D. Opět jsem psal příspěvek a trošku poslouchal učitele a občas to prostě nejde spojit bez toho abych nenapsal nějakou blbost, a to já mám občas ve zvyku :)

Nahoru Odpovědět 17.5.2013 17:50
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na Kit
Petr Nymsa:

Stejěn zd přibude podmínka, abychom nešli mimo hranici pole. Ale asi to bude i rychlejší než přes cyklus :)

Nahoru Odpovědět 17.5.2013 17:51
Pokrok nezastavíš, neusni a jdi s ním vpřed
Avatar
David Čápka
Tým ITnetwork
Avatar
Nahoru Odpovědět  +1 17.5.2013 17:53
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Petr Nymsa
Redaktor
Avatar
Odpovídá na David Čápka
Petr Nymsa:

Ach jo já dneska perlím :D ... už mlčím , opravdu ;( nevím na co nesutále myslím :D Samozřejmě, nepřibude :)

Nahoru Odpovědět 17.5.2013 18:07
Pokrok nezastavíš, neusni a jdi s ním vpřed
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 26 zpráv z 26.