Za pomocą wyrażenia if można decydować o zmianie przepływu programu. Podobnie jak w innych językach programowania, w Verse wyrażenie if służy do wykonywania warunkowego, jednak w przypadku Verse decyzja jest podejmowana na podstawie spełnienia kryteriów powodzenia lub niepowodzenia zadanych w warunkach.
Można na przykład napisać kod, który będzie definiował wysokość, z jakiej gracz może spaść, zanim zacznie odnosić obrażenia.
var PlayerFallHeight : float = CalculatePlayerFallHeight()
# Players take damage if they fall more than 3 meters
if (PlayerFallHeight > 3.0):
DealDamage()
# Reset the player’s fall height
ZeroPlayerFallHeight()W tym przykładzie jeśli wartość PlayerFallHeight będzie większa niż trzy metry, warunek zostanie spełniony, a wyrażenie DealDamage() zostanie wykonane, zanim wysokość spadania gracza zostanie zresetowana. W innym przypadku warunek nie zostanie spełniony, więc gracz nie odniesie żadnych obrażeń, choć wysokość jego spadania zostanie zresetowana.
if
Składnia przykładowej wysokości spadania gracza może być następująca:
expression0
if (test-arg-block):
expression1
expression2Po wykonaniu wyrażenia expression0 program Verse wprowadzi blok if. Jeśli warunek test-arg-block zostanie spełniony, program Verse wykona wyrażenie expression1, które może być pojedynczym wyrażeniem lub blokiem wyrażeń. W innym przypadku, jeśli warunek test-arg-block nie zostanie spełniony, program Verse pominie wyrażenie expression1 i wykona jedynie wyrażenie expression2.
Diagram przepływu logiki bloku if.
if ... else
Można również zdefiniować wyrażenie, które zostanie wykonane w przypadku niepowodzenia wyrażenia if.
Przykładowo gracz powinien zyskać zdolność wykonania podwójnego skoku, jeśli spadnie z wysokości mniejszej niż trzy metry i jeśli jego miernik skoku wskazuje 100 procent. Jeśli jednak gracz spadnie z wysokości ponad trzech metrów lub jego miernik skoku nie wskazuje 100 procent, wówczas postać zamacha rękoma, informując gracza, że nie może wykonać podwójnego skoku.
var PlayerFallHeight : float = CalculatePlayerFallHeight()
if (PlayerFallHeight < 3.0 and JumpMeter = 100):
# Perform a double jump.
ActivateDoubleJump()
# Reset the player’s fall height.
ZeroPlayerFallHeight()
else:
# Flap the character’s arms to tell the player they
# cannot double jump right now!
W tym przykładzie warunek if ocenia, czy wysokość PlayerFallHeight jest mniejsza niż trzy metry oraz czy JumpMeter wskazuje wartość równą 100 procent. Jeśli warunek zostanie spełniony, przed wyrażeniem SetDoubleJumpCooldown() zostaną wykonane wyrażenia ActivateDoubleJump() i ZeroPlayerFallHeight().
Jeśli warunek if nie zostanie spełniony, wówczas przed wyrażeniem SetDoubleJumpCooldown() zostanie wykonane wyrażenie ActivateFlapArmsAnimation() następujące po członie else.
Pod względem składniowym przykład wyrażeń if-else wygląda następująco:
expression0
if (test-arg-block):
expression1
else:
expression2
expression3Diagram przepływu logiki bloku if-else.
if ... else if ... else
Jeśli gracz ma 100-procentowe osłony w momencie upadku z wysokości ponad trzech metrów, powinien odnieść maksymalną szkodę, ale przeżyć. Zmodyfikujmy regułę, która daje graczom zdolność podwójnego skoku, w taki sposób, aby gracze zyskiwali możliwość wykonania podwójnego skoku, jeśli spadną z wysokości mniejszej niż trzy metry, a ich miernik skoku wskazuje powyżej 75 procent.
var PlayerFallHeight : float = CalculatePlayerFallHeight()
if (PlayerFallHeight > 3.0 and shields = 100):
DealMaximalDamage()
return false
else if (PlayerFallHeight < 3.0 and JumpMeter > 75):
ActivateDoubleJump()
return false
else:
return true
Pod względem składniowym przykład wyrażeń if-else if-else wygląda następująco:
expression0
if (test-arg-block0):
expression1
else if (test-arg-block1):
expression2
else:
expression3
expression4Diagram przepływu logiki bloku if-else if-else.
if ... then
Każdy z warunków if z poprzednich przykładów można zapisać w wielu wierszach i nie wpłynie to na sposób ich działania:
expression0
if:
test-arg-block
then:
expression1
expression2Blok kodu test-arg-block może zawierać co najmniej jeden wiersz warunków, przy czym wszystkie one muszą zostać spełnione, aby przed wyrażeniem expression2 zostało wykonane wyrażenie expression1, inaczej wykonane zostanie jedynie wyrażenie expression2.
Przepisany w tym formacie przykład z sekcji if ... else wygląda następująco:
var PlayerFallHeight : float = CalculatePlayerFallHeight()
if:
PlayerFallHeight < 3.0
JumpMeter = 100
then:
# Perform a double jump.
ActivateDoubleJump()
# Reset the player’s fall height.
ZeroPlayerFallHeight()
Wyrażenie jednowierszowe
Wyrażenie if else można zapisać jako wyrażenie jednowierszowe, podobnie jak operatory trójargumentowe w innych językach programowania. Aby na przykład przypisać maksymalną lub minimalną wartość Recharge w oparciu o atrybut ShieldLevel, można użyć następującego kodu Verse:
Recharge : int = if(ShieldLevel < 50) then GetMaxRecharge() else GetMinRecharge()Wymagania dotyczące predykatów
Predykat if, czyli wyrażenie w nawiasie (), działa inaczej niż w innych językach programowania w tym sensie, że nie zwraca wartości logicznej (nazywanej logic w Verse). Zamiast tego predykat ma efekt decides (zauważ, że choć przy tworzeniu podtypów zazwyczaj dopuszcza się podzbiór efektów w miejscach, gdzie dopuszczalny jest zbiór efektów, if wymaga, aby efekt ogólny predykatu zawierał człon decides). Efekt zostaje usunięty z otaczającego zakresu. Chodzi o to, że efekt decides ze wszystkich operacji w predykacie if jest wykorzystywany przez konstrukt if. Na przykład w poniższym kodzie predykat Main nie zawiera efektu decides, choć wywołuje predykat Foo, który go zawiera.
Foo()<transacts><decides> : void = {}
Bar() : void = {}
Main() : void =
if (Foo[]):
Bar()Dzieje się tak dlatego, że zamiast użycia wartości wejściowej logic w if do wskazania wybranej gałęzi, to powodzenie operacji zawartych w predykacie if jest podstawą decyzji o wyborze gałęzi – gałęzi then w przypadku powodzenia wszystkich operacji lub gałęzi else (o ile została zastosowana) w razie niepowodzenia dowolnej z operacji. Zauważ, że oznacza to możliwość zastosowania dowolnych operacji w predykacie if, w tym możliwość wprowadzenia stałych. Na przykład:
Main(X : int) : void =
Y = array{1, 2, 3}
if:
Z0 := Y[X]
Z1 := Y[X + 1]
then:
Use(Z0)
Use(Z1)Innymi słowy, zakres gałęzi then obejmuje dowolne nazwy wprowadzane w predykacie if.
Zachowanie transakcyjne
Kolejną różnicą w zastosowaniu if w porównaniu z innymi językami programowania jest zachowanie transakcyjne predykatu if. Predykat if nie może zawierać efektu no_rollback (używanego w sposób domniemany przez wszystkie funkcje, które nie zawierają wyraźnie wskazanego efektu transacts, varies lub computes). Wynika to z tego, że w razie niepowodzenia predykatu wszystkie operacje podjęte w trakcie wykonywania predykatu (poza jakąkolwiek operacją wpływającą na zasoby poza środowiskiem uruchomieniowym, takie jak wejście do pliku lub wyjście z pliku bądź zapis w konsoli) zostaną cofnięte przed wykonaniem gałęzi else. Na przykład:
int_ref := class:
var Contents : int
Incr(X : int_ref)<transacts> : void =
set X.Contents += 1
Foo(X : int) : int =
Y := int_ref{Contents := 0}
if:
Incr(Y)
Funkcja Foo(-1) zwróci 0, natomiast funkcja Foo(1) zwróci 1. Dzieje się tak dlatego, że choć wywołanie do Incr występuje przed testem X > 0, powodowana przez nie mutacja Y zostaje cofnięta przed wykonaniem gałęzi else. Pamiętaj, że Incr musi ręcznie wskazywać efekt transacts. Domyślnie zachowanie transakcyjne wskazywane przez domniemany efekt no_rollback nie ma zastosowania, jednak można je dodać poprzez ręczne zdefiniowanie efektu transacts (zastąpienie domniemanego efektu no_rollback).