이 문서에서는 Verse 프로그래밍 언어 및 구문의 특징을 빠르게 찾아볼 수 있습니다. 아래에 정리되어 있는 링크를 클릭하면 자세한 내용을 확인할 수 있습니다.
표현식
표현식(expression)이란 평가 결과로 값이 생성되는 코드의 최소 단위입니다. 예를 들어 if ... else 표현식의 경우, Verse에서 이 표현식 블록의 내용을 평가하고 그 결과에 따라 다른 값이 생성됩니다.
다음 코드는 MyNumber가 5보다 큰지 여부에 따라 "Big!" 또는 "Small!" 중 하나를 포함하는 string 값으로 평가됩니다.또는 "Small!" 중 하나를 포함하는 string 값으로 평가됩니다.
if (MyNumber > 5):
"Big!"
else:
"Small!"
코드 코멘트
코드 코멘트(code comment)는 프로그래머가 자신의 코드 또는 프로그램을 작성한 방식에 대한 이유를 설명하기 위해 작성하는 코멘트입니다. 프로그램이 실행될 때 코드 코멘트는 무시됩니다.
Verse | 한 줄 코멘트: |
Verse | 인라인 블록 코멘트: |
Verse | 여러 줄 블록 코멘트: |
Verse | 중첩 블록 코멘트: |
Verse | 들여쓰기 코멘트: |
상수와 변수
상수(Constants)와 변수(variables)는 프로그램이 사용하는 정보 또는 값(values)을 저장하고, 이러한 값에 이름을 연결할 수 있습니다. 이러한 이름을 식별자(identifier)라고 합니다.
변수 또는 상수를 생성하려면 Verse에 이러한 세 가지 사항을 알려야 합니다. 이것을 가리켜 선언(declaration)이라고 합니다. 초깃값을 지정하는 것을 가리키는 초기화(initialization)는 변수의 경우 권장되는 선택 사항이지만, 상수의 경우에는 필수입니다.
변수 값은 언제든 변경할 수 있습니다. 이는 값을 변수에 할당하는 것이므로 공식적으로는 할당(assignment)이라고 부르지만, 경우에 따라서는 변수 설정이라고도 합니다.
Verse | 상수 생성: 상수의 값은 프로그램이 실행되는 동안 변경할 수 없습니다. 상수를 생성하려면 해당 이름, 타입, 값을 지정하면 됩니다. | 이미지를 클릭하면 확대됩니다. |
Verse | 변수 생성: 변수의 값은 프로그램이 실행되는 동안 변경할 수 있습니다. 변수를 생성하려면 이름 앞에 키워드 | 이미지를 클릭하면 확대됩니다. |
Verse | 변수 값 변경: 프로그램이 실행되는 동안 키워드 | 이미지를 클릭하면 확대됩니다. |
타입
Verse는 정적 타입 프로그래밍 언어로, 모든 식별자에 타입이 할당됩니다.
함수에서 상수를 생성하는 것과 같이 타입을 명시적으로 명명할 필요가 없는 경우가 있습니다. 예를 들어 MyConstant := 0에서 MyConstant의 타입에는 값 0이 할당되었으므로 추론됩니다.
일반 타입
Verse에는 내장 타입이 있으며, 이러한 타입은 대부분의 프로그램이 수행되는 데 필요한 기본적인 연산을 지원합니다. 내장 타입을 더 큰 구조로 결합하여 자체 타입을 생성할 수 있지만 이러한 일반 타입은 Verse에서 변수 및 상수를 활용하는 기초이므로 알아둬야 합니다.
Verse | logic: Verse에서 |
Verse | int: Verse의
|
Verse | float: Verse에서
|
Verse | string: Verse의 표현식의 결과는 "" 내에 {}를 사용하여 스트링에 삽입할 수 있습니다. 이것을 스트링 보간이라고 합니다.
|
Verse | message: Verse에서 현재 로캘에 따라 텍스트 포맷을 지정하는 현재 |
Verse | locale: 이 타입은 현재 |
Verse | rational:
|
Verse | void:
|
Verse | any: |
Verse | comparable: |
Verse의 타입에 대한 자세한 내용은 일반 타입을 참고하세요.
컨테이너 타입
컨테이너 타입을 사용하면 여러 값을 함께 저장할 수 있습니다. Verse에는 값을 저장할 수 있는 여러 가지 컨테이너 타입이 있습니다. Verse의 컨테이너 타입에 대한 자세한 내용은 컨테이너 타입을 참고하세요.
옵션
option 타입은 값 하나를 포함하거나 비어 있을 수 있습니다.
다음 예시에서 MaybeANumber는 아무 값도 포함하지 않은 옵션 integer ?int입니다. 그런 다음 MaybeANumber는 새 값 42로 설정됩니다.
var MaybeANumber : ?int = false # unset optional value
set MaybeANumber := option{42} # assigned the value 42
이미지를 클릭하면 확대됩니다.
Verse | 옵션 생성하기: 다음 중 하나로 옵션을 초기화할 수 있습니다.
옵션에 저장될 것으로 예상되는 값 타입 앞에 |
Verse | 옵션에서 엘리먼트에 액세스하기: |
다음 예시에서는 옵션 타입을 사용하여 생성된 플레이어에 대한 레퍼런스를 저장하고 플레이어가 생성될 때 트리거 장치가 반응하게 합니다.
my_device := class<concrete>(creative_device):
var SavedPlayer : ?player = false # unset optional value
@editable
PlayerSpawn : player_spawner_device = player_spawner_device{}
@editable
Trigger : trigger_device = trigger_device{}
OnBegin<override>() : void =
범위
범위(range) 표현식은 지정된 범위 내의 모든 숫자를 포함하며, for 표현식 같은 특정 표현식 내에서만 사용할 수 있습니다. 범위 값은 integer여야 합니다.
이미지를 클릭하면 확대됩니다.
Verse | 범위 생성: 범위의 시작은 표현식의 첫 값이고, 범위의 끝은 표현식에서 |
Verse | 범위에서 반복작업하기 |
자세한 내용은 범위를 참고하세요.
배열
배열(array)은 동일한 타입의 엘리먼트를 저장하는 컨테이너로, 인덱스를 호출하여 해당 배열의 위치를 기준으로 엘리먼트에 액세스할 수 있습니다. 배열의 첫 번째 인덱스는 0이고, 마지막 인덱스는 배열의 엘리먼트 숫자에서 1을 뺀 값입니다.
Players : []player = array{Player1, Player2}
이미지를 클릭하면 확대됩니다.
Verse | 배열 생성하기: |
Verse | 배열에서 엘리먼트에 액세스하기: |
Verse | 배열에서 엘리먼트 변경하기: 배열 변수에 저장된 값을 키워드 |
Verse | 배열에서 반복작업하기: 배열의 모든 엘리먼트는 for 표현식을 사용하여 처음부터 끝까지 순서대로 액세스할 수 있습니다. |
Verse | 배열의 엘리먼트 수 구하기: 배열에 |
Verse | 배열 연결하기: 각 배열은 |
자세한 내용은 배열을 참고하세요.
튜플
튜플(tuple)은 단일 표현식처럼 취급되는 표현식 두 개 이상을 하나로 그룹화한 것입니다. 표현식 내 엘리먼트의 순서가 중요합니다. 동일한 표현식이 튜플 내 여러 위치에 있을 수 있습니다. 튜플 표현식은 모든 타입이 될 수 있으며, 한 타입의 엘리먼트만 가질 수 있는 배열과는 달리 혼합된 타입을 가질 수 있습니다.
튜플은 특히 다음과 같은 경우에 유용합니다.
함수에서 여러 값을 반환할 때
함수에 여러 값을 전달할 때
내부 그룹화가 재사용 가능한 데이터 구조체를 생성하는 오버헤드보다 간결할 때(예: struct 또는 class 등)
VerseExampleTuple : tuple(int, float, string) = (1, 2.0, "three")
이미지를 클릭하면 확대됩니다.
Verse | 튜플 생성하기: ` |
튜플에서 엘리먼트에 액세스하기: | |
튜플 확장: 각 개별 실행인자를 지정하는 대신 튜플을 함수의 실행인자로 사용하면 함수는 튜플에서 지정된 순서대로 실행인자로 사용되는 튜플의 각 엘리먼트와 함께 호출됩니다. | |
튜플 배열 강제: 튜플은 튜플 엘리먼트의 타입이 모두 배열과 동일한 타입인 경우, 배열이 예상되는 위치에 관계없이 모두 전달될 수 있습니다. 배열은 튜플이 예상되는 위치에 전달될 수 없습니다. |
자세한 내용은 튜플을 참고하세요.
맵
맵은 다른 값과 관련된 값, 즉 키-값 쌍을 저장하고 해당하는 고유한 키로 엘리먼트에 액세스할 수 있는 컨테이너입니다.
WordCount : [string]int = map {"apple" => 11, "pear" => 7}
이미지를 클릭하면 확대됩니다.
맵 생성하기: | |
맵에서 엘리먼트에 액세스하기: | |
맵에서 반복작업하기: 맵의 모든 엘리먼트는 for 표현식을 사용하여 처음 삽입된 엘리먼트부터 끝까지 순서대로 액세스할 수 있습니다. | |
맵 내 키-값 쌍의 수 구하기: 맵에 |
자세한 내용은 맵을 참고하세요.
복합 타입
복합 타입(composite type)은 프리미티브 타입 또는 다른 타입의 일반적으로 명명된 필드 또는 엘리먼트로 구성될 수 있는 모든 타입입니다. 일반적으로 복합 타입은 수명 동안 고정된 수의 필드 또는 엘리먼트를 갖습니다. 자세한 내용은 복합 타입을 참고하세요.
열거형
열거형(enum)은 enumeration의 약어로, 열거자(enumerators)라고 하는 일련의 요소를 명명 또는 나열하는 것을 의미하며, 요일이나 나침반 방향 등에 사용되는 Verse 타입입니다.
이미지를 클릭하면 확대됩니다.
열거형 생성하기: | |
열거자에 액세스하기: 열거형에 |
구조체
구조체(struct)는 structure의 약어로, 다수의 관련 변수를 한데 묶는 방법으로 생각하면 이해하기가 쉽습니다. 이때 해당 변수들은 어떤 타입이든 될 수 있습니다.
이미지를 클릭하면 확대됩니다.
이미지를 클릭하면 확대됩니다.
구조체 생성하기: 코드 블록 앞에 키워드 | |
구조체 인스턴스화하기: 아키타입(archetype)에서 구조체의 인스턴스를 생성할 수 있습니다. 아키타입은 구조체 필드의 값을 정의합니다. | |
구조체의 필드에 액세스하기: 구조체 인스턴스와 필드 이름 사이에 |
클래스
클래스(class)는 유사한 행동과 프로퍼티(변수 및 메서드)를 갖춘 오브젝트를 생성하기 위한 템플릿으로, 인스턴스화를 통해 실수 값을 가진 오브젝트로 생성되어야 합니다. 클래스는 계층형입니다. 즉, 정보를 부모(수퍼클래스) 클래스에서 상속받아 자손 클래스(서브클래스)와 공유할 수 있습니다. 클래스는 사용자에 의해 정의되는 커스텀 타입이 될 수 있습니다.
이미지를 클릭하면 확대됩니다.
이미지를 클릭하면 확대됩니다.
클래스 생성하기: 클래스 내부에 중첩된 정의는 클래스의 필드를 정의합니다. 클래스의 필드로 정의된 함수는 메서드라고도 합니다. | |
클래스 인스턴스화하기: | |
클래스의 필드에 액세스하기: 클래스 인스턴스와 필드 이름 사이에 | |
클래스의 메서드에 액세스하기: 클래스 인스턴스와 메서드 이름 사이에 | |
Self: |
클래스에 대해 고유하며 해당 행동을 변경하는 지정자도 있습니다. 자세한 내용은 클래스 지정자를 참고하세요.
클래스에 대한 자세한 내용은 클래스를 참고하세요.
서브클래스
서브클래스(subclass)는 다른 클래스의 필드 및 메서드를 추가 또는 수정하여 다른 클래스의 정의를 확장하는 클래스, 즉 수퍼클래스(superclass)를 생성할 수 있습니다.
이미지를 클릭하면 확대됩니다.
서브클래스 생성: | |
수퍼클래스의 필드 오버라이드하기: 필드 이름에 | |
수퍼클래스의 메서드 오버라이드하기: [INCLUDE:#overriding_methods_on_superclass_description] | |
Super: |
자세한 내용은 서브클래스를 참고하세요.
생성자
생성자는 관련 클래스의 인스턴스를 생성하는 특수 함수입니다. 신규 오브젝트의 초깃값을 설정하는 데 사용될 수 있습니다.
클래스의 생성자 정의하기: 함수 이름에 | |
생성자에 변수 및 실행 코드 추가하기: block 표현식으로 생성자 내에서 표현식을 실행하고, 키워드 | |
생성자에서 다른 생성자 호출하기: 생성자에서 다른 생성자를 호출할 수 있습니다. 또한 모든 필드가 초기화되어 있는 한 클래스의 생성자에서 클래스의 수퍼클래스에 대한 생성자를 호출할 수도 있습니다. 생성자가 다른 생성자를 호출하고 두 생성자 모두 필드를 초기화하면 첫 번째 생성자에 제공된 값만 필드에 사용됩니다. 두 생성자 간의 표현식 평가 순서는 표현식이 작성된 순서를 따르지만 부작용을 고려하여 첫 번째 생성자에 제공된 값만 사용됩니다. |
인터페이스
인터페이스 타입은 해당 인터페이스를 구현하는 클래스와의 상호작용 방식에 대한 계약을 제공합니다. 인터페이스는 인스턴스화될 수 없지만, 클래스는 인터페이스에서 상속하고 해당 메서드를 구현할 수 있습니다. 인터페이스는 추상 클래스와 비슷하지만 부분 구현 또는 필드를 정의의 일부로 허용하지 않는다는 점이 다릅니다.
이미지를 클릭하면 확대됩니다.
이미지를 클릭하면 확대됩니다.
인터페이스 생성하기: 인터페이스 정의는 클래스와 비슷하지만 | |
인터페이스 확장하기: 인터페이스는 | |
인터페이스 구현하기: | |
여러 인터페이스 구현: 하나의 클래스가 여러 인터페이스를 구현할 수 있습니다. 인터페이스는 | |
인터페이스 및 다른 클래스에서 상속하기: 클래스는 인터페이스를 구현할 수 있으며, 다른 클래스의 서브클래스가 될 수 있습니다. 인터페이스 및 수퍼클래스는 |
자세한 내용은 인터페이스를 참고하세요.
타입 작업하기
Verse에서는 몇 가지 방법을 통해 타입 작업을 손쉽게 수행할 수 있습니다.
타입 에일리어싱: 코드를 통해 타입에 다양한 이름을 부여하고, 해당 새 이름으로 타입을 참조할 수 있습니다. 구문은 상수 초기화와 유사합니다. 또한 함수 타입에도 타입 에일리어스를 지정할 수 있습니다. 자세한 내용은 타입 에일리어싱을 참고하세요. | |
명시적 타입 실행인자인 파라미터 타입: Verse는 파라미터 타입, 즉 실행인자로 예상되는 타입을 지원합니다. 이는 오로지 클래스 및 함수와 작동합니다. 자세한 내용은 파라미터 타입을 참고하세요. | |
함수에 대한 묵시적 타입 실행인자인 파라미터 타입: 함수의 묵시적 파라미터 타입을 사용하는 이유는 이것을 통해 함수와 함께 사용되는 각 타입에 대해서가 아니라 특정 타입이었던 불변 코드를 작성할 수 있기 때문입니다. 자세한 내용은 파라미터 타입을 참고하세요. | |
타입 매크로: Verse에서는 임의 표현식의 타입을 구하는 데 사용할 수 있는 특수한 구조체를 제공합니다. 타입이 사용될 수 있는 위치 어디서든 사용할 수 있습니다. 자세한 내용은 타입 매크로를 참고하세요. | |
서브타입: |
자세한 내용은 Verse 타입으로 작업하기를 참고하세요.
연산자
연산자는 Verse 프로그래밍 언어에서 정의된 특수 함수로, 자신의 피연산자에서 수학 연산 등의 액션을 수행합니다.
여러 연산자가 동일한 표현식에 사용된 경우 높은 우선순위부터 낮은 우선순위의 순서대로 평가됩니다. 동일한 표현식에 동일한 우선순위의 연산자가 여러 개 있는 경우 왼쪽에서 오른쪽 순서로 평가됩니다.
아래 표에는 Verse에 내장된 모든 연산자가 가장 높은 우선순위부터 낮은 우선순위로 정리되어 있습니다.
| 연산자 | 설명 | 연산자 포맷 | 연산자 우선순위 | 예시 |
|---|---|---|---|---|
쿼리 |
| 후위 | 9 |
|
부정 |
| 전위 | 8 |
|
양수 |
| 전위 | 8 |
|
음수 |
| 전위 | 8 |
|
곱셈 |
| 중위 | 7 |
|
나눗셈 |
| 중위 | 7 |
|
덧셈 | + 연산자는 두 숫자 값을 서로 더합니다. 스트링 및 배열과 함께 사용되는 경우 두 값은 연결됩니다. 자세한 내용은 수학을 참고하세요. | 중위 | 6 |
|
뺄셈 |
| 중위 | 6 |
|
덧셈 할당 | 이 연산자로는 동일한 연산에 덧셈과 할당을 결합하여 변수 값을 업데이트할 수 있습니다. 자세한 내용은 수학을 참고하세요. | 중위 | 5 |
|
뺄셈 할당 | 이 연산자로는 동일한 연산에 뺄셈과 할당을 결합하여 변수 값을 업데이트할 수 있습니다. 자세한 내용은 수학을 참고하세요. | 중위 | 5 |
|
곱셈 할당 | 이 연산자로는 동일한 연산에 곱셈과 할당을 결합하여 변수 값을 업데이트할 수 있습니다. 자세한 내용은 수학을 참고하세요. | 중위 | 5 |
|
나눗셈 할당 | 이 연산자로는 동일한 연산에 나눗셈과 할당을 결합하여 변수가 integer가 아닌 경우 변수 값을 업데이트할 수 있습니다. 자세한 내용은 수학을 참고하세요. | 중위 | 5 |
|
같음 |
| 중위 | 4 |
|
같지 않음 |
| 중위 | 4 |
|
작음 |
| 중위 | 4 |
|
작거나 같음 |
| 중위 | 4 |
|
큼 |
| 중위 | 4 |
|
크거나 같음 |
| 중위 | 4 |
|
그리고 |
| 중위 | 3 |
|
또는 |
| 중위 | 2 |
|
변수 및 상수 초기화 | 이 연산자로 상수 또는 변수에 값을 저장할 수 있습니다. 자세한 내용은 변수와 상수를 참고하세요. | 중위 | 1 |
|
변수 할당 | 이 연산자로 변수에 저장된 값을 업데이트할 수 있습니다. 자세한 내용은 변수와 상수를 참고하세요. | 중위 | 1 |
|
자세한 내용은 연산자를 참고하세요.
그룹화
()로 표현식을 그룹화(grouping)하여 연산자의 평가 순서를 변경할 수 있습니다. 예를 들어 (1+2)*3과 1+(2*3)는 ()로 먼저 평가될 표현식을 그룹화하기 때문에 동일한 값으로 평가되지 않습니다.
다음 예시는 그룹화를 사용하여 게임 내 폭발을 계산하여 폭발이 일어난 위치에서 플레이어까지의 거리를 기반으로 피해를 측정하고, 플레이어의 방어력에 따라 플레이어가 받는 총 피해를 감소시키는 방법을 보여줍니다.
BaseDamage : float = 100.0
Armor : float = 15.0
DistanceScaling : float = Max(1.0, Pow(PlayerDistance, 2.0))
ExplosionDamage : float = Max(0.0, (BaseDamage / DistanceScaling) - Armor)
자세한 내용은 그룹화를 참고하세요.
코드 블록
코드 블록(code block)은 0개 이상의 표현식 그룹으로, 스코프가 지정된 새로운 바디를 추가합니다. 코드 블록은 반드시 식별자 뒤에 와야 합니다. 이는 if와 for 같은 함수 식별자일 수도 있고 컨트롤 플로 식별자일 수도 있습니다.
공백 삽입 포맷: | |
중괄호를 사용한 여러 줄 포맷: | |
중괄호를 사용한 한 줄 포맷: |
또한 ;을 사용하여 같은 줄에 표현식 하나 이상을 사용하는 것도 가능합니다. 각 표현식이 새 줄에 배치되는 포맷에서는 {} 문자를 각 줄에 사용하지 않아도 됩니다.
코드 블록의 마지막 표현식은 코드 블록의 결과입니다. 다음 예시에서 if 표현식 코드 블록의 결과는 IsLightOn?이 성공하는 경우 false, IsLightOn?이 실패하는 경우 true입니다. 그런 다음 logic 결과가 NewLightState에 저장됩니다.
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
자세한 내용은 코드 블록을 참고하세요.
스코프
스코프(scope)는 Verse 프로그램에서 식별자(이름)와 값의 연관성이 유효하고 해당 값을 참조하는 데 그 이름을 사용할 수 있는 코드를 가리킵니다.
예를 들어 코드 블록 내에서 생성하는 상수 또는 변수는 해당 코드 블록의 컨텍스트 내에서만 존재합니다. 즉, 오브젝트의 수명은 해당 오브젝트가 생성된 스코프 내로 제한되며 코드 블록 밖에서는 사용될 수 없습니다.
다음 예시에서는 플레이어가 가진 동전 개수로 구매할 수 있는 최대 화살 개수의 계산 방법을 보여줍니다. 상수 MaxArrowsYouCanBuy는 if 블록 내에서 생성되었기 때문에 스코프가 if 블록으로 제한됩니다. 상수 MaxArrowsYouCanBuy가 출력 스트링에서 사용되면 오류가 발생하는데, 이는 MaxArrowsYouCanBuy 이름이 해당 if 표현식 스코프 밖에 존재하지 않기 때문입니다.
CoinsPerQuiver : int = 100
ArrowsPerQuiver : int = 15
var Coins : int = 225
if (MaxQuiversYouCanBuy : int = Floor(Coins / CoinsPerQuiver)):
MaxArrowsYouCanBuy : int = MaxQuiversYouCanBuy * ArrowsPerQuiver
Print("You can buy at most {MaxArrowsYouCanBuy} arrows with your coins.") # Error: Unknown identifier MaxArrowsYouCanBuy
이미지를 클릭하면 확대됩니다.
Verse는 식별자 재활용을 지원하지 않으며, 식별자 앞에 (qualifying_scope:)를 추가하여 해당 식별자를 한정한 경우가 아니라면 다른 스코프에서 선언되었더라도 이는 마찬가지입니다. 여기에서 qualifying_scope는 식별자의 모듈, 클래스 또는 인터페이스입니다. 식별자를 정의하고 사용할 때마다 식별자에 한정자를 추가해야 합니다.
함수
함수(function)는 재사용할 수 있는 일련의 명명된 표현식입니다. 함수는 액션을 수행하거나 입력에 기반하여 출력을 생성하는 인스트럭션을 제공합니다.
함수 정의
함수를 정의하려면 세 가지 핵심 부분을 제공해야 하는데 고유한 이름(식별자), 결과로 예상되는 정보의 타입 그리고 호출 시 함수가 수행할 역할이 그것입니다. 함수의 기본 구문은 다음과 같습니다.
name() : type =
codeblock
콜론으로 구분된 name()과 type: 함수 시그니처(function signature)라고 합니다. 해당 함수를 호출하고 사용하는 방식으로, 함수가 반환해야 하는 값은 자신이 제공한 타입이어야 합니다. 이 포맷은 상수 생성 방식과 유사하지만 ()가 이름 뒤에 와서 코드에서 함수를 호출하는 방식을 모방한다는 점이 다릅니다.
함수 코드 블록: 함수가 호출되었을 때 수행할 역할은
=codeblock을 제공하여 정의하는데, 여기서codeblock이란 표현식 하나 이상의 어떤 시퀀스도 될 수 있습니다. 함수를 호출할 때마다 코드 블록의 표현식이 실행됩니다.
이미지를 클릭하면 확대됩니다.
함수 결과
함수의 반환 타입을 지정하면 함수 바디는 해당 타입의 결과를 생성해야 하며, 그렇지 않으면 코드가 컴파일되지 않습니다.
값과 함께 반환된 마지막 표현식: 기본적으로 함수의 코드 블록에 포함된 마지막 표현식은 함수의 결과이며, 그 값은 함수의 반환 타입과 일치해야 합니다. | |
값이 있는 명시적 반환: |
결과를 생성할 필요가 없는 함수를 생성하는 경우 함수 반환 타입을 void로 설정할 수 있습니다. 이는 함수가 유용한 결과를 생성할 것이라고 예상하지 않으므로 함수 코드 블록의 마지막 표현식이 어떤 타입이든 될 수 있다는 것을 의미합니다.
반환 타입이 void인 함수는 자체적으로 return 표현식을 사용하여 함수에서 빠져나갈 수 있습니다. 이 표현식은 코드 블록 내에서 그 뒤에 표현식이 더 있더라도 함수에서 즉시 빠져나옵니다.
함수 파라미터
함수에 대한 입력은 파라미터(parameters)를 사용하여 정의됩니다. 파라미터는 함수 시그니처의 괄호 사이에서 선언되는 상수이며, 이를 함수의 바디에서 사용할 수 있습니다.
다음은 파라미터가 두 개인 함수의 구문입니다.
name(parameter1name : type, parameter2name : type) : type =
codeblock
이미지를 클릭하면 확대됩니다.
다음 예시에서 IncreaseScore() 함수는 Points라는 이름의 integer 파라미터를 사용하여 MyScore의 값을 증가시킵니다.
var MyScore : int = 100
IncreaseScore(Points : int) : void =
# Increase MyScore by Points.
set MyScore = MyScore + Points
함수 호출
일련의 명명된 표현식(함수)을 코드에서 사용하려면 코드에서 GetRandomInt(1, 10)와 같이 함수를 이름으로 호출하면 됩니다. 이 호출은 1~10 사이의 랜덤 integer를 반환합니다.
함수 호출의 실패 가능 여부에 따라 함수를 호출하는 방법에는 두 가지가 있습니다.
실패 불가능 함수 호출: 실패 불가능한 함수 호출은 | |
실패 가능 함수 호출: 실패 가능한 함수 호출은 |
함수 실행인자
파라미터 입력을 받는 함수를 호출하는 경우에는 파라미터에 값을 할당해야 합니다. 상수를 사용하려면 상수에 값을 할당해야 하는 것과 같습니다. 할당된 값은 함수에 대한 실행인자(arguments)라고 합니다.
다음은 실행인자가 두 개인 함수 호출의 구문입니다.
name(parameter1name := value, parameter2name := value)
다음 예시에서는 IncreaseScore() 함수가 세 번 호출되며, 매번 다른 실행인자(10, 5, 20)로 호출되어 MyScore 값을 증가시킵니다.
# After this call, MyScore is 110
IncreaseScore(Points := 10)
# After this call, MyScore is 115
IncreaseScore(Points := 5)
# After this call, MyScore is 135
IncreaseScore(Points := 20)
확장 메서드
확장 메서드는 기존 클래스나 타입의 멤버처럼 작동하지만 새 타입 또는 서브클래스 생성은 필요로 하지 않는 함수 타입입니다.
다음은 int 타입 배열의 확장 메서드를 생성하는 방법을 보여줍니다. 이 메서드는 배열의 모든 숫자를 더한 후 총계를 반환합니다.
# Sum extension method for type []int
(Arr : []int).Sum<public>() : int =
var Total : int = 0
for (Number : Arr):
set Total = Total + Number
return Total그러면 모든 int 타입 배열에서 메서드를 호출할 수 있습니다.
SumTotal := array{4, 3, 7}.Sum()
Print("The SumTotal is { SumTotal }")
# "The SumTotal is 14"
실패
부울 값인 true와 false를 사용하여 프로그램 플로를 변경하는 다른 프로그래밍 언어와는 달리, Verse는 성공도 실패도 할 수 있는 표현식을 사용합니다. 이러한 표현식을 실패 가능 표현식(failable expressions)이라고 하며, 실패 컨텍스트(failure context)에서만 실행할 수 있습니다.
실패 가능 표현식
실패 가능 표현식은 성공하여 값을 생성할 수도, 실패하여 아무 값도 생성하지 못할 수도 있는 표현식입니다.
다음 목록에는 Verse에서 제공하는 모든 실패 가능 표현식이 정리되어 있습니다.
함수 호출: 함수 호출이 | |
비교: 비교 표현식은 비교 연산자 중 하나를 사용하여 두 요소를 비교합니다. 자세한 내용은 연산자를 참고하세요. | |
Integer 나눗셈: integer의 경우 나눗셈 연산자 | |
결정: 결정 표현식은 연산자 | |
쿼리: 쿼리 표현식은 연산자 | |
배열에서 엘리먼트에 액세스하기: 배열에 저장된 값에 액세스하는 것은 실패 가능 표현식입니다. 이는 해당 인덱스에 엘리먼트가 없을 수도 있기 때문에 실패 컨텍스트에서 사용되어야 하기 때문입니다. 자세한 내용은 배열을 참고하세요. |
실패 컨텍스트
실패 컨텍스트는 실패 가능 표현식을 실행하는 것이 허용되는 컨텍스트입니다. 이 컨텍스트는 표현식이 실패할 경우 어떤 일이 발생할지 정의합니다. 실패 컨텍스트에서 발생한 모든 실패는 전체 컨텍스트를 실패하게 만듭니다.
Block 표현식의 함수 실행인자나 표현식과 같이, 실패 컨텍스트에서는 중첩된 표현식이 실패 표현식이 될 수 있습니다.
다음 목록에는 Verse에서 제공하는 모든 실패 컨텍스트가 정리되어 있습니다.
| |
| |
| |
| |
| |
|
추측 실행
Verse에서 실패 컨텍스트가 유용한 것은 커밋할 필요 없이 액션을 시험해 볼 수 있는 추측 실행(speculative execution)이라는 점입니다. 표현식이 성공하면 변수 값이 변경되는 것과 같은 표현식의 이펙트가 커밋됩니다. 표현식이 실패하면 해당 표현식이 아예 없었던 것처럼 표현식 이펙트가 롤백됩니다.
이 방식을 통해 변경사항을 누적시키는 다양한 액션을 실행할 수 있지만, 이러한 액션은 실패 컨텍스트 중 어디서든 실패하는 경우 모두 실행 취소됩니다.
이렇게 하려면 실패 컨텍스트에서 호출되는 함수는 모두 transacts 이펙트 지정자가 있어야 합니다.
지정자
Verse에서 지정자는 시맨틱과 관련된 행동을 설명하며, 식별자 및 특정 키워드에 추가할 수 있습니다. 지정자 구문은 IsPuzzleSolved()<decides><transacts> : void와 같이 <와 > 사이에 키워드를 넣으면 됩니다.
다음 섹션에서는 Verse에서 제공되는 모든 지정자와 각 지정자를 언제 사용할 수 있는지 살펴봅니다.
이펙트 지정자
Verse에서 이펙트란 함수가 표시할 수 있는 행동 카테고리를 가리킵니다. 이펙트 지정자는 다음과 같이 추가할 수 있습니다.
함수 정의의 name 뒤에 () 추가:
name()<specifier> : type = codeblockClass키워드:name := class<specifier>():.
이펙트 지정자는 다음과 같은 두 가지 카테고리로 나뉩니다.
익스클루시브(Exclusive): 함수 또는
class키워드에 익스클루시브 이펙트 지정자를 하나만 추가하거나 전혀 추가하지 않을 수 있습니다. 추가된 익스클루시브 이펙트 지정자가 없는 경우 디폴트 이펙트는no_rollback이 됩니다.애디티브(Additive): 함수 또는
class키워드에 애디티브 이펙트 지정자를 모두 또는 일부 추가하거나 전혀 추가하지 않을 수 있습니다.
| 예시 | 이펙트 |
|---|---|
no_rollback: 익스클루시브 이펙트가 지정되지 않은 경우의 디폴트 이펙트입니다. | |
익스클루시브 이펙트 | |
transacts: 이 이펙트는 함수가 수행한 액션을 모두 롤백할 수 있음을 나타냅니다. transacts 이펙트는 변경 가능 변수( | |
varies: 이 이펙트는 함수에 동일한 입력을 제공하더라도 항상 동일한 출력을 반환하지는 않는다는 것을 나타냅니다. 또한 | |
computes: 이 이펙트는 함수에 부작용이 없어야 하며 완료가 보장되지 않습니다. 함수가 동일한 실행인자와 함께 제공되면 동일한 결과를 생성한다는 미확인 요구 사항도 있습니다. | |
converges: 이 이펙트는 관련 함수의 실행으로 인해 부작용이 발생하지 않을 뿐 아니라 해당 함수가 확실하게 완료된다는 것을 보장합니다. 즉, 무한히 재귀하지 않습니다. 이 이펙트는 native 지정자가 있는 함수에서만 나타날 수 있지만 컴파일러에서는 확인되지 않습니다. 글로벌 변수에 대한 값 또는 클래스의 디폴트값을 제공하는 데 사용되는 코드에는 이 이펙트가 있어야 합니다. | |
애디티브 이펙트 | |
decides: 함수가 실패할 수 있으며 해당 함수 호출은 실패 가능 표현식임을 나타냅니다. | |
suspends: 함수가 비동기임을 나타냅니다. 함수 바디에 대한 비동기화 컨텍스트를 생성합니다. |
모든 경우, 특정 이펙트가 있는 함수를 호출하려면 호출자 역시 해당 이펙트가 있어야 합니다.
액세스 지정자
액세스 지정자는 멤버와 상호작용할 수 있는 요소와 그 방식을 정의합니다. 액세스 지정자는 다음과 같은 요소에 적용할 수 있습니다.
멤버의 식별자:
name<specifier> : type = value멤버의 키워드
var:var<specifier> name : type = value
클래스 지정자
클래스 지정자는 클래스의 서브클래스 생성 가능 여부와 같은 클래스의 특정 특성 또는 해당 멤버를 정의합니다.
abstract: 클래스 또는 클래스 메서드에 추상 지정자가 있는 경우 클래스의 인스턴스를 생성할 수 없습니다. 추상 클래스는 일부 구현을 갖춘 수퍼클래스 또는 일반적인 인터페이스로 사용됩니다. 수퍼클래스의 인스턴스를 갖추는 것이 적절하지 않지만 유사한 클래스 간에 프로퍼티 및 행동을 복제하고 싶지 않은 경우에 유용합니다. | |
concrete: 클래스에 concrete 지정자가 있는 경우 빈 아키타입을 사용하여 클래스의 인스턴스를 생성할 수 있어야 합니다. 즉, 해당 클래스의 모든 필드가 디폴트값을 가져야 합니다. concrete 클래스의 모든 서브클래스는 묵시적으로 concrete입니다. concrete 클래스가 추상 클래스를 상속할 수 있으려면 두 클래스 모두 동일한 모듈에서 정의되어 있어야 합니다. | |
unique: unique 지정자를 클래스에 적용하여 고유 클래스로 만들 수 있습니다. 고유 클래스의 인스턴스를 생성하려면 Verse는 결과 인스턴스에 고유 ID를 할당합니다. 이렇게 하면 ID를 비교함으로써 고유 클래스의 인스턴스가 동등한지 비교할 수 있습니다. unique 지정자가 없는 클래스에는 이러한 ID가 없으므로 필드 값에 기반해서만 동등한지 비교할 수 있습니다. 즉, 고유 클래스는 `=` 및 <> 연산으로 비교되며, comparable 타입의 서브타입입니다. |
구현 지정자
코드를 작성할 때는 구현 지정자를 사용할 수 없지만, UEFN API에서는 확인할 수 있습니다.
native_callable: 인스턴스 메서드가 C++로 구현된 네이티브이며, 다른 C++ 코드에서 호출할 수 있다는 것을 나타냅니다. 이 지정자는 인스턴스 메서드에서 사용됩니다. 이 지정자는 서브클래스로 전파되지 않기 때문에 해당 지정자가 있는 메서드를 오버라이드할 때 정의에 추가하지 않아도 됩니다. |
현지화 지정자
새 메시지를 정의할 때는 localizes 지정자를 사용해야 합니다. 구체적으로는 변수에 message 타입이 있고, string 값으로 변수를 초기화하는 경우가 여기에 해당합니다.
# The winning player's name:
PlayerName<localizes> : message = "Player One"# Build a message announcing the winner.
Announcement<localizes>(WinningPlayerName : string) : message = "...And the winner is: {WinningPlayerName}"
Billboard.SetText(Announcement("Player One"))localizes 지정자는 새 메시지만 정의하는 용도이므로, 이미 생성된 메시지로 멤버 값을 초기화하는 경우 localizes 지정자를 사용하지 않아도 됩니다.
PlayerOne<localizes> : message = "Player One"
# The winning player's name:
PlayerName : message = PlayerOne어트리뷰트
Verse에서 어트리뷰트는 Verse 시맨틱을 설명하는 지정자와 달리 Verse 언어 외부에서 사용되는 행동을 설명합니다. 어트리뷰트는 정의 앞의 코드 줄에 추가할 수 있습니다.
어트리뷰트 구문은 키워드 앞에 @을 사용합니다.
컨트롤 플로
컨트롤 플로는 컴퓨터가 인스트럭션을 실행하는 순서입니다. Verse는 프로그램의 플로를 제어하는 데 사용할 수 있는 다양한 표현식을 제공합니다.
block
Verse에서는 코드 블록 앞에 식별자를 필요로 하는데, block 표현식은 코드 블록을 중첩할 수 있게 해줍니다. 중첩된 코드 블록은 코드 블록과 유사한 방식으로 작동합니다. 코드 블록과 마찬가지로 block 표현식은 중첩된 새 스코프 바디를 추가합니다.
| block |
|---|
결과: |
자세한 내용은 block을 참고하세요.
if
if 표현식을 사용하면 프로그램의 플로를 변경하는 결정을 내릴 수 있습니다. 다른 프로그래밍 언어와 마찬가지로 Verse의 if 표현식도 조건부 실행을 지원하지만 Verse에서는 조건이 성공 및 실패를 사용하여 결정을 내립니다.
| if | if ... then |
|---|---|
결과: | 결과: |
| if ... else | if ... else if ... else |
|---|
다음 예시에서 if 표현식 코드 블록의 결과는 IsLightOn?이 성공하는 경우 false, IsLightOn?이 실패하는 경우 true입니다. 그런 다음 logic 결과가 NewLightState에 저장됩니다.
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
자세한 내용은 if를 참고하세요.
case
case 표현식으로는 선택 목록에서 프로그램의 플로를 제어할 수 있습니다. Verse의 case 구문을 사용하면 =를 사용하는 것처럼 여러 가능한 값에 대해 하나의 값을 테스트하고 일치하는 값을 기준으로 코드를 실행할 수 있습니다.
| case |
|---|
자세한 내용은 case를 참고하세요.
loop
loop 표현식의 경우 루프 블록 내 표현식은 루프의 반복작업마다 반복됩니다.
| loop |
|---|
결과: |
다음 예시에서는 게임이 실행되는 동안 ToggleDelay초마다 플랫폼이 나타났다가 사라지기를 반복합니다.
loop:
Sleep(ToggleDelay) # Sleep(ToggleDelay) waits for ToggleDelay seconds before proceeding to the next instruction.
Platform.Hide()
Sleep(ToggleDelay)
Platform.Show() # The loop restarts immediately, calling Sleep(ToggleDelay) again.
자세한 내용은 loop를 참고하세요.
루프 중단
루프 블록은 영원히 반복되므로, 루프를 멈추려면 break 또는 함수의 반환 표현식으로 루프에서 빠져나와야 합니다.
| loop 및 break | loop 및 return |
|---|---|
결과: | 결과: |
| 중첩된 loop 및 break |
|---|
결과: |
자세한 내용은 loop와 break를 참고하세요.
for
for 표현식은 for 루프라고도 하며, 루프 표현식과 비슷하지만 for의 경우 제너레이터를 사용하여 값의 시퀀스를 한 번에 하나씩 생성하고 각 값에 이름을 지정한다는 점이 다릅니다.
예를 들어 for(Value : 1..3) 표현식은 시퀀스 1, 2, 3을 생성하며, 시퀀스 내 각 숫자에는 반복작업별로 Value라는 이름이 지정됩니다. 따라서 for 루프는 총 3번 실행됩니다.
for 표현식은 다음과 같은 두 부분을 포함합니다.
반복작업 지정: 괄호 내부의 표현식입니다. 첫 번째 표현식은 제너레이터여야 하지만, 다른 표현식은 상수 초기화 또는 필터가 될 수 있습니다.
바디: 괄호 뒤의 코드 블록 내 표현식입니다.
| for | 필터가 있는 for |
|---|---|
결과: | 결과: |
| 여러 제너레이터가 있는 for | 중첩된 for 블록 |
|---|---|
결과: | 결과: 중첩된 |
자세한 내용은 for를 참고하세요.
defer
defer 표현식은 return 같은 결과 표현식을 포함하여 defer 표현식이 표시되는 스코프 바깥으로 프로그램 컨트롤을 전송하기 직전에 실행됩니다. 프로그램 컨트롤의 전송 방식은 상관없습니다.
| defer | 종료 전 defer |
|---|---|
결과: | 결과: |
defer 표현식은 defer에 도달하기 전에 일찍 종료되는 경우 실행되지 않습니다.
| 조기 반환이 있는 defer | 취소된 동시 작업이 있는 defer |
|---|---|
결과: | 결과: |
같은 스코프 내에서 나타나는 다수의 defer 표현식은 누적됩니다. 실행되는 순서는 도달하는 순서의 역순, 즉 선입 후출(first-in-last-out , FILO)입니다. 주어진 스코프에서 마지막으로 도달한 defer가 먼저 실행되므로 마지막으로 도달한 defer 내부 표현식은 그 전에 도달하여 나중에 실행되는 defer 표현식에 의해 정리되는 컨텍스트를 참조할 수 있습니다.
| 한 코드 블록 내 여러 defer 표현식 | 서로 다른 코드 블록 내 여러 defer 표현식 |
|---|---|
결과: | 결과: |
타임 플로 및 동시성
타임 플로 컨트롤은 Verse 프로그래밍 언어의 핵심이며, 동시 표현식으로 구현됩니다. 동시성에 대한 자세한 내용은 동시성 개요를 참고하세요.
구조적 동시성
구조적 동시성 표현식은 비동기 타임 플로를 지정하고 비동기화 표현식의 블로킹 속성을 비동기화 함수 바디 같은 특정 비동기화 컨텍스트 스코프로 제한된 수명으로 수정하는 데 사용됩니다.
이것은 연관 스코프 내로 제한하는 block, if, for, loop 같은 구조적 컨트롤 플로와 유사합니다.
| sync | branch |
|---|---|
코드 블록 내 모든 표현식을 동시에 실행하고, 모든 표현식이 끝나기를 기다린 후에 |
|
결과: | 결과: |
| race | rush |
|---|---|
|
|
결과: | 결과: |
비구조적 동시성
spawn이 유일한 비구조적 동시성 표현식이며, 특정 비동기화 컨텍스트 스코프로 제한되지 않는 수명을 가지므로 잠재적으로 실행된 스코프를 넘어 확장될 수 있습니다.
| spawn |
|---|
|
결과: |
작업
작업은 현재 실행 중인 비동기화 함수의 상태를 나타내는 데 사용되는 오브젝트입니다. 작업 오브젝트는 비동기화 함수가 정지된 위치와 해당 정지 지점에서의 로컬 변수의 값을 식별하는 데 사용됩니다.
# Get task to query / give commands to
# starts and continues independently
Task2 := spawn{Player.MoveTo(Target1)}
Task2.Await() # wait until MoveTo() completed
모듈과 경로
Verse에서 모듈은 재배포 및 종속 대상이 될 수 있는 아토믹 유닛 코드로, 시간 경과에 따라 종속성을 유지하면서 확장할 수 있습니다. Verse 파일에 모듈을 임포트하여 다른 Verse 파일의 코드 정의를 사용할 수도 있습니다.
using: Verse 모듈의 콘텐츠를 사용하려면 모듈의 경로를 사용하여 모듈을 명시해야 합니다. | |
module: 프로젝트 내 폴더에 의해 추가되는 모듈 외에 .verse 파일 내에서도 |
자세한 내용은 모듈과 경로를 참고하세요.