전역 변수
전역 변수
전역 변수(global variables)에 값을 할당하고 읽는 GNU Prolog 술어들을 설명해요.
본문
소개
GNU Prolog는 전역 변수를 할당하고 읽는 간단하면서도 강력한 방법을 제공해요. 전역 변수는 각 원자(atom)에 연관되며, 초기 값은 정수 0이에요. 전역 변수는 3가지 종류의 객체를 저장할 수 있어요:
- 항의 복사본(복사본은 백트래킹 가능 여부를 선택 가능).
- 항에 대한 링크(링크는 항상 백트래킹 가능).
- 객체 배열(재귀적으로).
복사본과 배열에 필요한 공간은 가능한 한 빨리 동적으로 할당되고 회수돼요. 예를 들어, 어떤 원자가 현재 값이 배열인 전역 변수와 연관될 때, 이 배열의 공간은 회수돼요(백트래킹 시 할당을 취소하지 않는 한). 항에 대한 링크가 전역 변수와 연관될 때, 이 항에 대한 참조가 저장되므로 변수의 내용을 읽을 때 원래 항이 반환돼요.
전역 변수 이름 짓는 규약: 전역 변수는 원자로 참조돼요. 변수가 배열을 포함하면, 인덱스(0부터)는 대응하는 원자를 주 functor로 하고 인자를 인덱스로 하는 복합 항을 사용해 제공할 수 있어요. 다차원 배열의 경우 각 인덱스가 복합 항의 인자로 주어져요. 변수가 항(링크 또는 복사)을 포함하면, 인자 번호(argument selector라고도 함)를 주어 하위 항만 참조할 수 있어요. 이런 하위 항은 주 functor가 -/2이고 첫 번째 인자는 전역 변수 이름, 두 번째 인자는 인자 번호(1부터)인 복합 항으로 지정돼요. 이를 재귀적으로 적용해 임의 깊이의 하위 항을 지정할 수 있어요. 리스트의 경우 인자 번호 I는 리스트의 I번째 요소를 나타내요. 이 절의 나머지에서는 -가 미리 정의된 중위 연산자(8.14.10절)이므로 연산자 표기를 사용해요.
다음에서 GVarName은 전역 변수에 대한 참조를 나타내며 그 문법은 다음과 같아요:
GVarName ::= atom 변수의 전체 내용
atom(Integer,… ,Integer) 배열의 요소
GVarName - Integer 하위 항 선택
Integer ::= integer 직접 값
GVarName 간접 값
GVarName이 인덱스나 인자 번호(즉 간접 참조)로 사용될 때, 그 변수의 값은 정수여야 해요. 이름 짓는 규약의 몇 가지 예:
a— a에 연관된 변수의 내용(어떤 종류든).t(1)— t에 연관된 배열의 2번째 요소.t(k)— k에 연관된 값이 I이면, t에 연관된 배열의 I번째 요소.a-1-2— a에 연관된 값이f(g(a,b,c),2)이면, 하위 항b.
다음은 전역 변수 이름에 연관되고 모든 술어에 공통인 오류들이에요:
- GVarName이 변수 —
instantiation_error - GVarName이 변수도 callable 항도 아님 —
type_error(callable, GVarName) - GVarName이 유효하지 않은 인자 번호를 포함(GVarName이 배열) —
domain_error(g_argument_selector, GVarName) - GVarName이 유효하지 않은 인덱스를 포함(GVarName이 배열이 아님) —
domain_error(g_array_index, GVarName) - GVarName이 간접 인덱스나 인자 선택자로 사용되고 정수가 아님 —
type_error(integer, GVarName)
배열: g_assign/2, g_assignb/2, g_link/2(8.21.2절) 술어들을 사용해 배열을 만들 수 있어요. 그들은 일부 항을 값으로 인식해요. 예를 들어 주 functor가 g_array인 복합 항은 고정 크기 배열을 정의하는 데 사용돼요. g_array 항에는 3가지 형태가 있어요:
g_array(Size)— Size가 0보다 큰 정수이면 크기 Size이며 모든 요소가 정수 0으로 초기화된 배열을 정의.g_array(Size, Initial)— 위와 같지만 요소가 0 대신 항 Initial로 초기화. Initial은 다른 배열 정의를 포함할 수 있어 다차원 배열이 가능.g_array(List)— List가 길이 Size의 리스트이면 위와 같지만 배열의 요소가 List의 요소에 따라 초기화(List는 다른 배열 정의를 포함 가능).
배열은 주 functor가 g_array_extend인 복합 항을 사용해 명시적으로 확장할 수 있어요. 이는 위에서 자세히 설명한 3가지 형태를 수용해요. 이 경우 기존 배열 요소는 초기화되지 않아요. g_array_extend가 배열이 아닌 객체와 함께 사용되면 g_array와 유사해요. 마지막으로 배열은 필요에 따라 자동으로 확장될 수 있어요. 프로그래머는 자동 배열의 확장을 명시적으로 제어할 필요가 없어요. 인덱스가 배열의 현재 크기 밖에 있으면 배열이 확장돼요. 그런 배열은 주 functor가 g_array_auto인 복합 항으로 정의돼요:
g_array_auto(Size)— Size가 0보다 큰 정수이면 초기 크기가 Size인 자동 배열을 정의. 모든 요소가 정수 0으로 초기화. 암시적 확장 동안 생성된 요소는 0으로 초기화.g_array_auto(Size, Initial)— 위와 같지만 요소가 0 대신 항 Initial로 초기화. Initial은 다른 배열 정의를 포함할 수 있어 다차원 배열이 가능. 암시적 확장 동안 생성된 요소는 Initial로 초기화.g_array_auto(List)— List가 길이 Size의 리스트이면 위와 같지만 배열의 요소가 List의 요소에 따라 초기화(List는 다른 배열 정의를 포함 가능). 암시적 확장 동안 생성된 요소는 0으로 초기화.
어쨌든 배열을 읽으면 g_array([Elem0,…, ElemSize-1]) 형태의 항이 반환돼요. 전역 변수를 사용하는 몇 가지 예는 나중에 제시돼요(8.21.7절).
g_assign/2, g_assignb/2, g_link/2
Templates
g_assign(+callable_term, ?term)
g_assignb(+callable_term, ?term)
g_link(+callable_term, ?term)
Description
g_assign(GVarName, Value)— 항 Value의 복사본을 GVarName에 할당. 이 할당은 백트래킹 시 취소되지 않음.g_assignb/2—g_assign/2와 비슷하지만 할당이 백트래킹 시 취소됨.g_link(GVarName, Value)— GVarName을 항 Value에 링크함. 이는 사용자가 어떤 Prolog 항(특히 비-ground 항)에 이름을 부여할 수 있게 해줌. 그런 할당은 항이 더 이상 존재하지 않을 수 있으므로 백트래킹 시 항상 취소됨. Value가 원자나 정수이면g_link/2와g_assignb/2는 같은 동작을 해요.g_link/2는 기존 항에 대한 링크만 처리하므로 추가 메모리 공간이 필요하지 않고 실행 시간도 많이 들지 않아요.
NB: 인자 선택자는 g_assign/2에서만 사용할 수 있어요(즉 할당 안에서 인자 선택자를 사용할 때는 백트래킹 가능해서는 안 됨).
Errors
- 소개(8.21.1절)에서 자세히 설명한 공통 오류 참조.
- GVarName이 인자 선택자를 포함하고 할당이 백트래킹 가능 —
domain_error(g_argument_selector, GVarName)
Portability: GNU Prolog 술어.
g_read/2
Templates
g_read(+callable_term, ?term)
Description
g_read(GVarName, Value)는 GVarName에 할당된 항과 Value를 통일해요.
Errors: 소개(8.21.1절)에서 자세히 설명한 공통 오류 참조.
Portability: GNU Prolog 술어.
g_array_size/2
Templates
g_array_size(+callable_term, ?integer)
Description
g_array_size(GVarName, Size)는 GVarName에 할당된 배열의 차원(0보다 큰 정수)과 Size를 통일해요. GVarName이 배열이 아니면 실패해요.
Errors
- 소개(8.21.1절)에서 자세히 설명한 공통 오류 참조.
- Size가 변수도 정수도 아님 —
type_error(integer, Size)
Portability: GNU Prolog 술어.
g_inc/3, g_inc/2, g_inco/2, g_inc/1, g_dec/3, g_dec/2, g_deco/2, g_dec/1
Templates
g_inc(+callable_term, ?integer, ?integer)
g_inc(+callable_term, ?integer)
g_inco(+callable_term, ?integer)
g_inc(+callable_term)
g_dec(+callable_term, ?integer, ?integer)
g_dec(+callable_term, ?integer)
g_deco(+callable_term, ?integer)
g_dec(+callable_term)
Description
g_inc(GVarName, Old, New)— GVarName에 할당된 정수와 Old를 통일하고 GVarName을 증가시킨 다음 증가된 값과 New를 통일.g_inc(GVarName, New)—g_inc(GVarName, _, New)와 동일.g_inco(GVarName, Old)—g_inc(GVarName, Old, _)와 동일.g_inc(GVarName)—g_inc(GVarName, _, _)와 동일.
g_dec 술어들은 비슷하지만 GVarName의 내용을 감소시켜요.
Errors
- 소개(8.21.1절)에서 자세히 설명한 공통 오류 참조.
- Old가 변수도 정수도 아님 —
type_error(integer, Old) - New가 변수도 정수도 아님 —
type_error(integer, New) - GVarName이 배열을 저장 —
type_error(integer, g_array) - GVarName이 정수가 아닌 항 T를 저장 —
type_error(integer, T)
Portability: GNU Prolog 술어.
g_set_bit/2, g_reset_bit/2, g_test_set_bit/2, g_test_reset_bit/2
Templates
g_set_bit(+callable_term, +integer)
g_reset_bit(+callable_term, +integer)
g_test_set_bit(+callable_term, +integer)
g_test_reset_bit(+callable_term, +integer)
Description
g_set_bit(GVarName, Bit)— GVarName에 할당된 정수의 비트 번호 Bit를 1로 설정. 비트 번호는 0부터 정수에 허용된 최대 수까지(아키텍처에 의존) 범위를 가져요. Bit가 이 한계보다 크면 이 한계로 모듈로가 취해져요.g_reset_bit(GVarName, Bit)—g_set_bit/2와 비슷하지만 지정된 비트를 0으로 설정해요.g_test_set_bit/2— 지정된 비트가 1로 설정되어 있으면 성공.g_test_reset_bit/2— 지정된 비트가 0으로 설정되어 있으면 성공.
Errors
- 소개(8.21.1절)에서 자세히 설명한 공통 오류 참조.
- Bit가 변수 —
instantiation_error - Bit가 변수도 정수도 아님 —
type_error(integer, Bit) - Bit가 0보다 작은 정수 —
domain_error(not_less_than_zero, Bit) - GVarName이 배열을 저장 —
type_error(integer, g_array) - GVarName이 정수가 아닌 항 T를 저장 —
type_error(integer, T)
Portability: GNU Prolog 술어.
예제
g_inc/3 시뮬레이션: 이 술어는 다음과 같이 동작해요(전역 변수 사용):
my_g_inc(Var, Old, New) :-
g_read(Var, Old),
N is Value + 1,
g_assign(Var, X),
New = N.
쿼리 my_g_inc(c, X, _)는 X를 0과 통일하며 성공할 거예요. my_g_inc(a, Y, _)를 또 호출하면 Y를 1과 통일할 거예요. 이런 식으로 계속돼요.
g_assign/2와 g_assignb/2의 차이: g_assign/2는 백트래킹 시 할당을 취소하지 않는 반면 g_assignb/2는 취소해요.
test(Old) :- testb(Old) :-
g_assign(x,1), g_assign(x,1),
( g_read(x, Old), ( g_read(x, Old),
g_assign(x,2) g_assignb(x,2)
; g_read(x, Old), ; g_read(x, Old),
g_assign(x,3) g_assign(x,3)
). ).
쿼리 test(Old)는 Old를 1과 통일하며 성공하고 백트래킹 시 2와 통일해요(즉 값 2의 할당이 취소되지 않음). 쿼리 testb(Old)는 Old를 1과 통일하며 성공하고 백트래킹 시 1과 통일해요(즉 값 2의 할당이 취소됨).
g_assign/2와 g_link/2의 차이: g_assign/2(및 g_assignb/2)는 항의 복사본을 만드는 반면 g_link/2는 만들지 않아요. g_link/2는 큰 데이터 구조(예: 사전 등)를 술어 인자로 전달하는 것을 피하는 데 사용할 수 있어요.
test(B) :- test(B) :-
g_assign(b, f(X)), g_link(b, f(X)),
X = 12, X = 12,
g_read(b, B). g_read(b, B).
쿼리 test(B)는 B를 f(_)와 통일하며 성공해요(g_assign/2가 값의 복사본을 할당). test(B) 쿼리는 B를 f(12)와 통일하며 성공해요(g_link/2가 항에 대한 포인터를 할당).
간단한 배열 정의: 배열이 어떻게 다뤄질 수 있는지 보여주는 몇 가지 쿼리예요:
| ?- g_assign(w, g_array(3)), g_read(w, X).
X = g_array([0,0,0])
| ?- g_assign(w(0),16), g_assign(w(1),32), g_assign(w(2),64), g_read(w, X).
X = g_array([16,32,64])
이것은 다음과 동일해요:
| ?- g_assign(k, g_array([16,32,64])), g_read(k, X).
X = g_array([16,32,64])
| ?- g_assign(k, g_array(3,null)), g_read(k, X), g_array_size(k, S).
S = 3
X = g_array([null,null,null])
2차원 배열 정의:
| ?- g_assign(w, g_array(2, g_array(3))), g_read(w, X).
X = g_array([g_array([0,0,0]), g_array([0,0,0])])
| ?- ( for(I,0,1), for(J,0,2), K is I*3+J, g_assign(w(I,J), K),
fail
; g_read(w, X)
).
X = g_array([g_array([0,1,2]), g_array([3,4,5])])
| ?- g_read(w(1), X).
X = g_array([3,4,5])
혼합 배열:
| ?- g_assign(w, g_array([1,2,g_array([a,b,c]), g_array(2,z), 5])), g_read(w, X).
X = g_array([1,2,g_array([a,b,c]), g_array([z,z]), 5])
| ?- g_read(w(1), X), g_read(w(2,1), Y), g_read(w(3,1), Z).
X = 2
Y = b
Z = z
| ?- g_read(w(1,2), X).
uncaught exception: error(domain_error(g_array_index, w(1,2)), g_read/2)
배열 확장:
| ?- g_assign(a, g_array([10,20,30])), g_read(a, X).
X = g_array([10,20,30])
| ?- g_assign(a, g_array_extend(5,null)), g_read(a, X).
X = g_array([10,20,30,null,null])
| ?- g_assign(a, g_array([10,20,30])), g_read(a, X).
X = g_array([10,20,30])
| ?- g_assign(a, g_array_extend([1,2,3,4,5,6])), g_read(a, X).
X = g_array([10,20,30,4,5,6])
자동 배열:
| ?- g_assign(t, g_array_auto(3)), g_assign(t(1), foo), g_read(t, X).
X = g_array([0,foo,0])
| ?- g_assign(t(5), bar), g_read(t, X).
X = g_array([0,foo,0,0,0,bar,0,0])
| ?- g_assign(t, g_array_auto(2, g_array(2))), g_assign(t(1,1), foo), g_read(t, X).
X = g_array([g_array([0,0]), g_array([0,foo])])
| ?- g_assign(t(3,0), bar), g_read(t, X).
X = g_array([g_array([0,0]), g_array([0,foo]), g_array([0,0]), g_array([bar,0])])
| ?- g_assign(t(3,4), bar), g_read(t, X).
uncaught exception: error(domain_error(g_array_index, t(3,4)), g_assign/2)
| ?- g_assign(t, g_array_auto(2, g_array_auto(2))), g_assign(t(1,1), foo), g_read(t, X).
X = g_array([g_array([0,0]), g_array([0,foo])])
| ?- g_assign(t(3,3), bar), g_read(t, X).
X = g_array([g_array([0,0]), g_array([0,foo]), g_array([0,0]),
g_array([0,0,0,bar])])
| ?- g_assign(t, g_array_auto(2, g_array_auto(2, null))), g_read(t(2,3), U), g_read(t, X).
U = null
X = g_array([g_array([null,null]), g_array([null,null]),
g_array([null,null,null,null]), g_array([null,null])])