전역 변수

전역 변수

전역 변수(global variables)에 값을 할당하고 읽는 GNU Prolog 술어들을 설명해요.

출처: GNU Prolog Manual - Global variables

본문

소개

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/2g_assign/2와 비슷하지만 할당이 백트래킹 시 취소됨.
  • g_link(GVarName, Value) — GVarName을 항 Value에 링크함. 이는 사용자가 어떤 Prolog 항(특히 비-ground 항)에 이름을 부여할 수 있게 해줌. 그런 할당은 항이 더 이상 존재하지 않을 수 있으므로 백트래킹 시 항상 취소됨. Value가 원자나 정수이면 g_link/2g_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])])

더 알아보기