If, then, else, switch zamiast else - jak traktuje je GM

Czwartek, 22 Listopada 2007, 21:07
Czas czytania 9 minut, 17 sekund
Zgodne z GM: gm5 gm6 gm7 gm8 gms1 gms2
Artykuł zwróca uwagę na to, jak można zapisywać i czym skutkuje zapisywanie warunków w Game Makerze. Będzie mowa o tym jak gdzie i kiedy używać if, then i else, oraz kiedy warto konstrukcję if ... else if ... else zastąpić konstrukcją swit
Często na GMC poruszane są problemy dotyczące złego napisania jakiegoś skryptu, ze względu na nieprawidłowe umieszczenie if i else w całym skrypcie. Dzisiaj chciałbym zwrócić uwagę na to, jak można zapisywać i czym skutkuje zapisywanie warunków w Game Makerze. Będzie mowa o tym jak gdzie i kiedy używać if, then i else, oraz kiedy warto konstrukcję if ... else if ... else zastąpić konstrukcją switch.

Załóżmy, że dla wszystkich przykładów w create definiujemy zmienne:

kodtest=true;
test2=false;
predkosc=5;

1. if <cośtam>

Sprawdzanie cy jakaś wartość jest większa czy mniejsza, dokonujemy poprzez używanie znaków >, >=, =, <, <=, <>

Na przykład:

kodif predkosc>5
if predkosc>=5
if predkosc<5
if predkosc<=5
if predkosc<>5
if predkosc=5
if predkosc==5 //oznacza to samo co wyżej

Jest to dość oczywiste. Skupmy się jednak na ostatnich dwóch przykładach. Pomijam już to, że dla GM oba zapisy są prawidłowe, pomimo, że znakiem = nadajemy również wartość, a powinno być to jakoś rozróżniane. Ale dla wygody jest tak. O ile porównujemy liczby, to jeszcze wszystko jest jasne, ale jak się okazuje, porównując wartości boolean, czyli true i false, można zastosować jeszcze kilka innych sztuczek. Warto zwrócić uwagę, że każdy warunek jaki postawimy po if, tak na prawdę zwraca właśnie true, lub false i wykonuje się, gdy zwrócona wartość będzie równa true. Zatem poniższe warunki tak naprawdę oznaczają to samo

kodif test=true //zwracana jest wartosc true, bo skoro zdefiniowaliśmy, że test=true, to nasz warunek tak naprawdę to if true=true...
if test //też zwracana jest wartość true, bo test=true, co zostało zdefiniowane wcześniej

Jak widać, jeżeli coś przybiera wartość true, nie trzeba pisać znaku równości co natomiast, gdybym napisał if test2 ?? Warunek oczywiście nie będzie prawdziwy, a co za tym idzie dalszy kod nie wykona się, ponieważ zdefiniowana przez nas zmienna test2=false, a warunek if test2 to inaczej if test2=true. W takim razie czy da się jakoś skrócić też warunkiem dla zmiennych równych false? Oczywiście. Wystarczy użyć znaku ! który w programowaniu oznacza negację. Neguje zatem następujące po nim wyrażenie, np.:

kodif predkosc!=5 //to taki sam zapis jak if predkosc<>5
No tak, a jak ma się to do wartości false? A tak:

kodif test2=false if test2 //test 2 false, zatem otrzymujemy funkcję if false=true - nie jest ona prawdziwa
if !test2 //test 2 jest negowany, zatem z false zrobi się true i wyjdzie nam funkcja if true=true, lub jak kto woli if false=false, w każdym bądź razie sama prawda

Skoro wiemy już jak to działa, to można pobawić się dalej:

kodif !test!=true //if false<>true
if !test!=!true //if false<>false

Warto też wiedzieć, że w podobny sposób można zmieniać wartość z true na false, na przykład gdy na zmianę po naciśnięciu spacji obiekt znika i pojawia się.:

kodif keyboard_check(vk_space) visible=!visible;
Dzięki temu, gdy visible było true, zmieni się na false (bo ! neguje czyli zamienia na przeciwne true i false), a jak visible=false to zmienia się na true. Ot, taka ciekawostka. Kolejna ciekawostka to taka, ze true to inaczej 1, a false to inaczej 0 (czyli bity jak ktoś nie wie). Zatem zamiast if test=true można napisać też:

kodif test=1
I to też prawda. Warto jeszcze zaznaczyć, że postawienie; oznacza zakończenie danej linijki kodu, zatem napiasnie if test=true; to tak naprawdę nie napisanie niczego,bo wszystko co napiszemy za znakiem ; traktowane jest, jak coś nowego.

2. if <cośtam> then

Dobra, skoro mamy warunek, trzeba by coś wykonać. Konstrukcja if <cośtam> then po naszemu znaczy po prostu Jeżeli coś tam to wtedy. Ale czy tak naprawdę potrzeba pisać zawsze then ? Okazuje się, że i tutaj GM okazuje się dość tolerancyjny i można sobie odpuścić then. Zatem kod można zapisać np.:

kodif predkosc>5 then predkosc=5;
if predkosc>5 predkosc=5; //j.w.

No dobra, zapis nie wygląda pięknie, bo brak słowa then zanieczyszcza lekko kod. Ale gdy potrzebujemy zapisać trochę więcej kodu, wygląda to już inaczej:

kodif predkosc>5 then {
predkosc=5;
test=false;
}

//i to samo, ale bez then
if predkosc>5
{
predkosc=5;
test=false;
}

No i tym razem brak then tylko upiększa kod. Warto więc czasem zastanowić sie, czy warto użyć then.

3. if <cośtam> then <cośtam> else <cośtam>

Kontrukcja else dość często sprawia początkującym programistom problem. Po prostu zapominają jej użyć, co prowadzi często do błędów. Ale jak jej w ogóle używać? Instrukcja else wykonywana jest, gdy to co wpisaliśmy w if nie jest prawdą, np.:

kodif predkosc>5 then predkosc=5 else predkosc+=1;
albo bardziej rozbudowanie:

kodif predkosc>5 then {
predkosc=5;
test=true;
}
else predkosc+=1;

Jak widać, gdy zmienna prędkość jest większa niż 5, ustawiana jest na 5, w przeciwnym wypadku dodajemy do niej 1 (tak na marginesie, powyższy kod ogranicza narastanie zmiennej predkość do 5, i tak na prawdę to lepiej zapisać go jako if predkosc<5 then predkosc+=1 else predkosc=5; a skoro już podaję przykłady pisania warunków, to również predkosc<=4 then predkosc+=1 else predkosc=5; jest prawidłowym skryptem, ale nieco mniej czytelnym na pierwszy rzut oka.). Dobra, ale przejdźmy to tego najdzczęściej popełnianego błędu - w tym celu posłużę się przykładem. Załóżmy, że dwukrotne wciśnięcie spacji powoduje, że nasz obiekt niszczy się, a po pierwszym wciśnięciu jego prędkość spada o połowę. Wniosek prosty, trzeba zrobić coś, aby za pierwszym razem nie zginął. Ustalamy zatem, że na początku zmienna test2=false;. Gdy wciskamy spację, a test2=false, to test2 ustawiamy na true, a speed=speed/2; gdy natomiast naciskamy spację i test2=true, obiekt zostaje zniszczony. Tylko jak to zapisać? No cóż, większość początkujących w zdarzeniu Keyboard Press <space> wpisze taki kod (pomijam tutaj uproszczenia o których mowa była wcześniej, dla lepszego zobrazowania):

kodif test2=false {
test2=true;
speed=speed/2;
}
if test2=true {
instance_destroy();
}

Na pierwszy rzut oka, kod jest dobry, bo oba warunki zostały wpisane jako osobne warianty. Ale po mimo to, obiekt znika za pierwszym wciśnięciem. Dlaczego? A bo w 3 linijce tego kodu, zmieniamy test2 na true, kod analizuje się dalej, zmienia prędkośc, wpada na klamerkę kończoncą ten blok kodu i co ? Wpada na warunek if test2=true - który zgodnie z tym co chwilę temu ustawiliśmy jest prawdziwy i też się wykonuje. Każde nowe if - o ile nie zostało poprzedzone słowem else i innym if, traktowane jest jako osobny, nowy kod. Wystarczy dodać else i pozbędziemy się tego błędu:

kodif test2=false {
test2=true;
speed=speed/2;
}
else if test2=true {
instance_destroy();
}

Teraz drugi kod nie wykona się bo po pierwszym if pomija wszystkie else w tym bloku, aż napotka kolejne słowo if nie poprzedzone else - czyli nowy kod bloku. Dobra, ale czy musieliśmy pisać drugie else? Nie. Skoro w przypadku, gdy test2=false, pierwsze if jest pomijane, wystarczy samo else, aby wykonać kod - tak naprawdę nie interesuje nas ile teraz wynosi test2, czy true, czy 5 czy może 'tekst' - ważne, ze nie false, bo właśnie tak stanowi pierwszy warunek. Zatem prawidłowy zapis to także:

kodif test2=false {
test2=true;
speed=speed/2;
}
else {
instance_destroy();
}

Dlaczego zatem poprzednio napisałem po else if ? Teraz zakładamy, że na początku zmienna predkosc=1, a za każdym razem, gdy naciskamy spację, zwiększa się o 1. Gdy wyniesie 2, speed zwiększymy razy 2, a gdy wyniesie 3, zniszczymy obiekt. Ten kod zapiszemy tak:

kodif predkosc=1
{
predkosc=2; //mozna tez predkosc+=1;
}
else if predkosc=2
{
predkosc=3;
speed=speed*2;
}
else {
instance_destroy();
}

No i wszystko jasne. Jeżeli predkosc nie równa się 1, to sprawdzamy drugi warunek, ale skoro nie równa się 2, to wykonany zostanie następny, a jako że nie ma tam już żadnego innego warunku, tylko samo else, no to dla każdej innej wartości zostanie wykonany ten właśnie kod. No właśnie - to należy podkreślić - dla każdej innej, nie spełniającej dwóch pierwszych warunków. Zatem nie tylko dla predkosc=3, ale też predkosc=-1, predkosc=10, predkosc='test'; (chociaż doszukiwanie się w zmiennej tekstowej zmiennej liczbowej, może wywalić nam errora, ale to inna bajka :P), zatem aby ominąć ten błąd poprawiamy kolejny raz nasz kod:

kodif predkosc=1
{
predkosc=2; //mozna tez predkosc+=1;
}
else if predkosc=2
{
predkosc=3;
speed=speed*2;
}
else if predkosc=3
{
instance_destroy();
}

No. To mamy za sobą kawał dłuugiej lekcji, z której na pewno dało się sporo wynieść. Nie rozpisuje się już na temat and or i łączenia warunków, czy zagnieżdzania w jedym warunku kolejnych. Zwrócę jednak uwagę na jeszcze jeden zapis - a mianowocie, kiedy if i else warto zastąpić switchem.

4. Switch zamiast if .. else if .. else if .. else

Bierzemy znany nam już kod, który niszczy obiekt, zawsze poza przypadkiem gdy predkosc=1 lub =2.

kodif predkosc=1
{
predkosc=2; //mozna tez predkosc+=1;
}
else if predkosc=2
{
predkosc=3;
speed=speed*2;
}
else instance_destroy();

switch to taka jakby szafa z szufladami, które otwieramy w określonych przypadkach i jedną szufladą, otwieraną, dla pozostałych przypadków, na które nie było już w tej szafie miejsca. Powyższa konstrukcja wygląda jako switch tak:

kodswitch predkosc
{
case 2: predkosc=3; speed=speed*2; break;
case 1: predkosc=2; break;
default: instance_destroy(); break;
}

Trochę mniej miejsca, nie? Od razu mówię, po co tam funkcja break. Otóż break powoduje, przerwanie przetwarzania dalej danego bloku, zatem jeżeli prędkość=2 i wykonany zostaje case 2, to reszta kodu będzie pominięta - czyli inaczej zrobi się else if. Z kolei default to czyste else. Fakt braku break i wykonania nastepnych linijek kodu można wykorzystać - skoro zawsze dodajemy do prędkości 1:

kodswitch predkosc
{
case 2: speed=speed*2;
case 1: predkosc+=1; break;
default: instance_destroy(); break;
}

i teraz dla case 2 zostaną wykonane również instrukcje poniżej - pomimo, że to case 1. Po prostu, gdy otworzymy daną szufladę, automatycznie dostajemy dostęp do wszystkich następnych na tak długo, aż napotkamy break.

Warto zapamiętać to o czym była dzisiaj mowa, warto eksperymentować i mieszać ze sobą poznane techniki - naprawdę można w ten sposób zdziałać wiele, bo ja nie wyczerpałem jeszcze tematu.

Miłej zabawy!
Komentarze (łącznie 28, wyświetlam 1 - 15):
Misztrzunio (Czw., 22 Lis. 07, 21:15)
#1

Eee.. kiedyś próbowałem a = !a i nie działało, a break nie jest funkcją ;p .

Y
Yoda (Czw., 22 Lis. 07, 21:17)
#2

Oo mi tam zmienna=!zmienna działa :D

gnysek (Czw., 22 Lis. 07, 21:22)
#3

dobra, ale jak napiszę że funkcja, to lepiej to zrozumieją :P
zawsze można przerobić wszystko i pozmieniać na konstruktory, mnemoniki i operandy :P

koko123 (Czw., 22 Lis. 07, 23:36)
#4

Przydało by się że jeśli zmienna>=1 to przy sprawdzaniu wynosi "true" a jeśli zmienna<=0 to "false" ;p

Ranmus (Pią., 23 Lis. 07, 11:30)
#5

Moim zdaniem ten artykuł jest mierny, ponieważ uczy złych nawyków. Raz jest składnia if ... then, później bez tego drugiego słowa kluczowego. Jestem zwolennikiem obejmowania warunków w nawiasy i przeciwnikiem stosowania then. W ten sposób kod jest czytelniejszy i nie miesza w głowie początkującym jak ten artykuł (niepotrzebnie).

Borek (Pią., 23 Lis. 07, 13:18)
#6

Niestety zgodzę się z Ranmusem. Sam nauczył mnie nowej techniki zapisu i rzeczywiście wszystko jest czytelniejsze i bardziej zrozumiałe.

k
kryniak (Pią., 23 Lis. 07, 13:41)
#7

To zależy też jakie nawyki są: czy z Pascala, czy z C++.

Misztrzunio (Pią., 23 Lis. 07, 14:30)
#8

Już na art nie zwalajcie xD . To Mark popełnił błąd xD .

Matthew (Pią., 23 Lis. 07, 16:01)
#9

ja zaczynałem programowanie od pascala, a w gmie stosuję metodę ranmusową if (warunek) ...

A o artykule napisałem na stronce Gnyska :)

gnysek (Pią., 23 Lis. 07, 16:43)
#10

To fakt, że może i pokazuję złe nawyki, ale tak naprawdę w każdym języku można inaczej pisać różne rzeczy - a to tabulatory inaczej, a to cośtam.
Artykuł ma wieksze zadanie na zwrócenie uwagi co i jak interpretuje GM i który zapis czym poskutkuje niż uczenie jak pisać.

Tymon (Pią., 23 Lis. 07, 17:42)
#11

Dla mnie zawsze składnia PHP zawsze była uniwersalna. Pascal to już inna rodzina i wymaga znajomości innej składni, to samo Basic. =p
Wnioski - jak byś tego arta nie napisał to zawsze znajdzie się ktoś komu nie będzie to pasować. =)

To samo napisał kryniak jakbyście nie widzieli.

Ranmus (Pią., 23 Lis. 07, 17:52)
#12

Dobre słowa od Borka - bezcenne. :P

Woock (Pią., 23 Lis. 07, 18:28)
#13

Dlaczego w tytule tekstu jest "the" zamiast "then"? :?

Tymon (Pią., 23 Lis. 07, 18:37)
#14

To tak ku Twej chwale. :D

P
Pawlik9 (Pią., 23 Lis. 07, 19:24)
#15

"If, then, else, switch zamiast else" Hm... "else zamiast else" - interesujące.

Najnowsze wersje GameMakera:

Stabilna
2024.2.0.132 • 2024.2.0.163
wydana 15 dni temu
LTS
2022.0.2.51 • 2022.0.2.49
wydana 154 dni temu
Beta
2024.400.0.516 • 2024.400.0.537
wydana  5 dni temu
= IDE, = Runtime
Użytkownicy online
1 użytkownik aktywny:
gości: 1,
(~ostatnie 15 minut)
Discord
Shoutbox
I am Lord (19:15, 17.03.24)
6h mam na to hmmm
I am Lord (19:06, 17.03.24)
Ale temat fajny
gnysek (01:33, 13.03.24)
Powinno działać, jest w kodzie sortowanie wg. najbliższego startu :)
Uzjel (21:59, 11.03.24)
Nie, ale za pierwszym razem zrobiłem fuckup, że było "Tura testowa" X_X
I am Lord (16:58, 11.03.24)
A co Uzjel już masz nawymyślane 100 tematów? 😅
Uzjel (20:08, 10.03.24)
@gnysek a jak bym dodał kilka lig na raz to walnie?
Uzjel (20:08, 10.03.24)
Liga będzie zawsze od piątku 16:00 do poniedziałku 23:59, zawsze w środku miesiąca.
gnysek (08:48, 10.03.24)
Tak, to też jest do poprawy X_X
Adriann (18:22, 09.03.24)
Tylko myślę czy nie leiej gdyby mówiło że zostało tyle i tyle dni i ileś godzin a nie tylko w godzinach ;d Albo konkretna data obok, byłoby czytelniej
I am Lord (15:08, 08.03.24)
o super z tą ligą :)
Starsze wpisy znajdziesz w Archiwum.
Ankieta
Ile zarobiłeś do tej pory na grach stworzonych w GM?