if 표현식을 사용하면 프로그램의 플로를 바꾸는 결정을 내릴 수 있습니다. 다른 프로그래밍 언어와 마찬가지로 Verse의 if 표현식은 조건부 실행을 지원하지만 Verse에서는 조건이 결정을 위해 성공과 실패를 사용합니다.
예를 들어 플레이어가 피해를 받지 않고 떨어질 수 있는 낙하 높이를 정의하는 코드를 작성할 수 있습니다.
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()이 예시에서 PlayerFallHeight가 3보다 크면 조건이 성공하고 플레이어의 낙하 높이가 리셋되기 전에 DealDamage()가 실행됩니다. 그렇지 않으면 조건이 실패하고 플레이어가 피해를 받지 않으며 플레이어의 낙하 높이가 리셋됩니다.
if
플레이어의 낙하 높이 예시는 다음 구문을 사용합니다.
expression0
if (test-arg-block):
expression1
expression2expression0을 실행한 뒤 Verse 프로그램은 if 블록에 들어갑니다. test-arg-block이 성공하면 Verse 프로그램은 expression1을 실행하며, 이는 하나의 표현식 또는 표현식 블록이 될 수 있습니다. test-arg-block이 실패하면 Verse 프로그램은 expression1을 건너뛰고 expression2만 실행합니다.
if-block 로직의 플로 다이어그램
if ... else
if 표현식이 실패했을 때 실행할 표현식을 지정할 수도 있습니다.
예를 들어 플레이어는 3미터 미만의 높이에서 낙하하면서 점프 미터가 100%일 때 이중 점프 능력을 얻습니다. 그러나 3미터 이상의 높이에서 떨어지거나 점프 미터가 100%가 아닐 경우 캐릭터가 팔을 퍼덕이면서 플레이어에게 이중 점프를 할 수 없음을 알립니다.
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!
이 예시에서 if의 조건은 PlayerFallHeight가 3미터 미만인지, JumpMeter가 100%인지 평가합니다. 조건이 성공하면 ActivateDoubleJump() 및 ZeroPlayerFallHeight()가 SetDoubleJumpCooldown() 이전에 실행됩니다.
if 조건이 실패하면 else 다음의 표현식 ActivateFlapArmsAnimation()이 SetDoubleJumpCooldown()보다 먼저 실행됩니다.
구문적으로 if-else 예시는 다음과 같습니다.
expression0
if (test-arg-block):
expression1
else:
expression2
expression3if-else 블록 로직의 플로 다이어그램
if ... else if ... else
플레이어가 3미터를 초과하는 높이에서 낙하할 때 100% 보호막이 있으면 최대 피해를 받고도 살아남을 수 있습니다. 플레이어에게 이중 점프 능력을 주는 규칙을 바꿔서 플레이어가 3미터 미만에서 낙하하고 점프 미터가 75%를 초과할 때만 이중 점프 능력을 얻게 하겠습니다.
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
구문적으로 if-else if-else 예시는 다음과 같습니다.
expression0
if (test-arg-block0):
expression1
else if (test-arg-block1):
expression2
else:
expression3
expression4if-else if-else-block 로직의 플로 다이어그램
if ... then
작동 방식을 그대로 유지하면서 이전 예시의 if 조건 중 원하는 것을 여러 줄에 작성할 수 있습니다.
expression0
if:
test-arg-block
then:
expression1
expression2코드 블록 test-arg-block은 여러 줄의 조건을 가질 수 있지만 그 조건이 모두 성공해야 expression1이 expression2에 앞서 실행되며, 그렇지 않은 경우 expression2만 실행됩니다.
이 형식으로 다시 작성한 if ... else 섹션의 예시는 다음과 같습니다.
var PlayerFallHeight : float = CalculatePlayerFallHeight()
if:
PlayerFallHeight < 3.0
JumpMeter = 100
then:
# Perform a double jump.
ActivateDoubleJump()
# Reset the player’s fall height.
ZeroPlayerFallHeight()
한 줄 표현식
다른 프로그래밍 언어의 삼항 연산자와 유사하게 if else를 한 줄 표현식으로 작성할 수 있습니다. 예를 들어 플레이어의 ShieldLevel을 기반으로 최대 또는 최소 Recharge 값을 할당하고 싶은 경우 다음과 같은 Verse 코드를 작성할 수 있습니다.
Recharge : int = if(ShieldLevel < 50) then GetMaxRecharge() else GetMinRecharge()술어 요건
if의 술어, 즉 괄호 () 안의 표현식은 다른 프로그래밍 언어와 달리 Verse에서 logic이라고 불리는 부울을 반환할 것으로 예상되지 않습니다. 그 대신 술어는 decides 이펙트를 가질 것으로 예상됩니다. 일반적으로 서브타입을 지정하면 이펙트 서브셋이 주어집니다. 이는 이펙트 세트를 허용하며, if는 decides를 포함하기 위해 술어의 전반적인 이펙트를 필요로 합니다. 이펙트는 주변 스코프로부터 제거됩니다. 즉 if 술어의 모든 연산에서 나오는 decides 이펙트는 if 생성자에 의해 소비됩니다. 예를 들어 아래 코드에서 Main에는 decides 이펙트가 없지만 그 이펙트를 갖는 Foo를 인보크합니다.
Foo()<transacts><decides> : void = {}
Bar() : void = {}
Main() : void =
if (Foo[]):
Bar()이는 어떤 분기로 넘어갈지 선택하기 위해 if에 logic 입력을 사용하는 대신 if의 술어에 포함된 연산의 성공이 적절한 분기를 결정하는 데 사용되기 때문입니다. 모든 연산이 성공하면 then 분기, 연산 중 하나라도 실패하면 else 분기(있는 경우)가 선택됩니다. 즉 상수 추가를 포함한 임의적인 연산이 if 술어에서 사용될 수 있습니다. 예를 들면 다음과 같습니다.
Main(X : int) : void =
Y = array{1, 2, 3}
if:
Z0 := Y[X]
Z1 := Y[X + 1]
then:
Use(Z0)
Use(Z1)바꿔 말하면 then 분기의 스코프에는 if 술어에서 추가된 모든 이름이 포함됩니다.
트랜잭션 행동
다른 프로그래밍 언어와 다른 if의 특징 중 하나는 if 술어의 트랜잭션 행동입니다. if 술어에는 명시적으로 transacts, varies, computes를 지정하지 않는 모든 함수에서 묵시적으로 사용되는 no_rollback 이펙트가 없어야 합니다. 술어가 실패하는 경우, 파일 I/O 등 런타임 밖에서 리소스에 영향을 미치는 작업이나 콘솔에 작성하는 모든 작업 등 술어 실행 도중의 모든 연산이 else 분기 실행 전에 취소되기 때문입니다. 예를 들면 다음과 같습니다.
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)
함수 Foo(-1)는 0을 반환하며 Foo(1)는 1을 반환합니다. Incr 호출은 X > 0 테스트 이전에 발생하지만 그로 인한 Y의 변형은 else 분기 실행 전에 취소됩니다. Incr에서는 transacts 이펙트를 수동으로 지정했다는 점에 유의하세요. 묵시적인 no_rollback 이펙트로 인해 트랜잭션 행동은 기본적으로 제공되지 않지만 transacts 이펙트를 수동으로 지정하여 추가될 수 있습니다. 이때 묵시적 no_rollback 이펙트를 오버라이드합니다.