Diskuze: Náhrada konstrukce switch
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.


David Hartinger:16.5.2013 20:20
- 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í).
- 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.
Petr Nymsa:16.5.2013 20:33
- Vygeneroval jsem si směr 1-4 (vlevo,vpravo,nahoru,dolu) podle toho jsem musel pokládat bloky apod. Jak to řešit jinak ?
- 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
TomBen:16.5.2013 21:01
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".
David Hartinger:16.5.2013 21:03
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.
David Hartinger:16.5.2013 21:04
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ě.
David Hartinger:16.5.2013 21:08
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.
Petr Nymsa:16.5.2013 21:18
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
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)); ;
}
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ď.
David Hartinger:17.5.2013 7:42
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í.
David Hartinger:17.5.2013 7:43
Na to by byla rychlejší nějaká hashovací kolekci, ale pořád lepší
než switch
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íš.
Kit:17.5.2013 10:33
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ě.
Petr Nymsa:17.5.2013 10:43
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.
Kit:17.5.2013 10:52
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.
Petr Nymsa:17.5.2013 11:04
Za chviličku postnu upravenou a zkrácenější verzi a budu zvědavý na
názor zda je to lepší
David Hartinger:17.5.2013 11:20
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.
Petr Nymsa:17.5.2013 11:26
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
David Hartinger:17.5.2013 11:30
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ů.
Petr Nymsa:17.5.2013 11:32
No právě že u tebe vyleze například že x a y = 1 ? Nebo jsem úplně
mimo ? 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.
David Hartinger:17.5.2013 13:47
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.
Kit:17.5.2013 14:15
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];
Petr Nymsa:17.5.2013 17:50
Jo vidím to a uvědomil jsem si to když jsem opouštěl školu . 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
Petr Nymsa:17.5.2013 17:51
Stejěn zd přibude podmínka, abychom nešli mimo hranici pole. Ale asi to
bude i rychlejší než přes cyklus
Petr Nymsa:17.5.2013 18:07
Ach jo já dneska perlím ... už mlčím , opravdu
nevím na co nesutále myslím
Samozřejmě, nepřibude
Zobrazeno 26 zpráv z 26.