수색…


소개

현대의 많은 Prolog 시스템은 지속적으로 개발되고 있으며 언어의 고전적인 단점을 해결할 수있는 새로운 기능을 추가했습니다. 불행하게도 많은 프롤로그 교과서와 심지어 교육 과정조차도 여전히 오래된 프롤로그만을 소개합니다. 이 주제는 현대 Prolog가 이전 Prolog에서 나타나는 일부 문제를 극복하고 오히려 도입 될 수있는 방법을 설명하기위한 것입니다.

정수 연산을위한 CLP (FD)

전통적으로 Prolog는 is=:= 연산자를 사용하여 산술 연산을 수행했습니다. 그러나 현재의 여러 Prolog는 CLP (FD) (정수형 논리 프로그래밍)를 정수 연산을위한보다 깨끗한 대안으로 제공합니다. CLP (FD)는 정수 값에 적용되는 제한 조건을 저장하고이를 메모리에서 함께 결합하는 것에 기반합니다.

CLP (FD)는이를 지원하는 대부분의 프롤로그의 확장이므로 명시 적으로로드해야합니다. 일단로드되면 #= 구문은 is=:= 대신 사용할 수 있습니다. 예를 들어, SWI-Prolog에서 :

?- X is 2+2.
X = 4.

?- use_module(library(clpfd)).
?- X #= 2+2.
X = 4.

is 와 달리 #= 은 간단한 방정식을 풀고 양방향으로 통합 할 수 있습니다.

?- 4 is 2+X.
ERROR: is/2: Arguments are not sufficiently instantiated

?- 4 #= 2+X.
X = 2.

CLP (FD)는 자체 생성기 구문을 제공합니다.

?- between(1,100,X).
X = 1;
X = 2;
X = 3...

?- X in 1..100.
X in 1..100.

생성자는 실제로 실행되지 않습니다. 범위 제약 조건 만 저장되므로 나중에 제약 조건을 결합 할 수 있습니다. 생성자는 label 술어를 사용하여 강제 실행 (및 무차별 대입 제약) 할 수 있습니다.

?- X in 1..100, label([X]).
X = 1;
X = 2;
X = 3..

CLP를 사용하면 무차별 대입을 지능적으로 줄일 수 있습니다. 예를 들어 구식 정수 산술을 사용하면 다음과 같습니다.

?- trace.
?- between(1,10,X), Y is X+5, Y>10.
...
Exit: (8) 6 is 1+5 ? creep
Call: (8) 6 > 10 ? creep
...
X = 6, Y = 11; ...

Prolog는 주어진 조건에서 수학적으로 증명할 수 있지만 1에서 5까지의 값을 반복합니다.이 값은 유용하지 않습니다. CLP (FD) 사용 :

?- X in 1..10, Y #= X+5, Y #> 10.
X is 6..10,
X+5 #= Y,
Y is 11..15.

CLP (FD)는 즉시 수학을 수행하고 사용 가능한 범위를 계산합니다. label([Y]) 을 추가하면 X는 유용한 값 6.10을 통해서만 반복됩니다. 이 장난감 예제에서는 1에서 10 사이의 작은 범위에서 대수 처리가 루프만큼 오래 걸리기 때문에 성능이 향상되지 않습니다. 더 큰 수의 범위가 처리 될 때 이것은 계산 시간을 상당히 줄일 수 있습니다.

CLP (FD)에 대한 지원은 프롤로그간에 가변적입니다. 인정받는 CLP (FD)의 가장 좋은 개발은 상용이며 비용이 많이 드는 SICStus Prolog에 있습니다. SWI-Prolog 및 다른 개방 된 Prolog는 종종 구현을합니다. Visual Prolog는 표준 라이브러리에 CLP (FD)를 포함하지 않지만 확장 라이브러리가 사용 가능합니다.

실패 주도 루프 대신에 forall

일부 "고전적인"Prolog 교과서는 오류 생성기가 모든 생성기 값에 목표를 적용하도록 강제하는 데 fail 구성이 사용되는 혼란스럽고 오류가 발생하기 쉬운 오류 중심 루프 구문을 계속 사용합니다. 예를 들어 모든 숫자를 주어진 한도까지 인쇄하려면 다음과 같이하십시오.

fdl(X) :- between(1,X,Y), print(Y), fail.
fdl(_).

Modern Prologs의 대다수는 더 이상이 구문을 필요로하지 않고,이를 대신하여 더 높은 순위의 술어를 제공합니다.

nicer(X) :- forall(between(1,X,Y), print(Y)).

이 방법을 훨씬 쉽게 읽을 수있을뿐 아니라 실패 할 수있는 목표를 인쇄 대신 사용하면 오류가 올바르게 감지되고 전달되지만 실패 주도 루프의 목표는 오류가 강제 실패와 혼동됩니다 루프를 구동합니다.

Visual Prolog는 함수 술어와 결합 된 이러한 루프에 대한 사용자 지정 구문 설탕을 가지고 있습니다 (아래 참조).

vploop(X) :- foreach Y = std::fromTo(1,X) do
                 console::write(X)
             end foreach.

이것은 for 루프처럼 보일지라도, 여전히 Prolog 규칙을 따른다 : 특히 foreach 의 각 반복은 그 자체의 범위이다.

함수 스타일의 술어

전통적으로 프롤로그에서는 "함수"(하나의 출력 및 바인드 된 입력)가 일반 조건 자로 작성되었습니다.

mangle(X,Y) :- Y is (X*5)+2.

이는 함수 스타일 술어가 여러 번 호출되는 경우 임시 변수를 "데이지 체인"하는 것이 어려워 질 수 있습니다.

multimangle(X,Y) :- mangle(X,A), mangle(A,B), mangle(B,Y).

대부분의 프롤로그에서는 대체 함수를 포함하여 표현식을 확장하는 is 대신에 사용할 중위 연산자를 작성하여이를 피할 수 있습니다.

% Define the new infix operator
:- op(900, xfy, <-).

% Define our function in terms of the infix operator - note the cut to avoid
% the choice falling through
R <- mangle(X) :- R is (X*5)+2, !.

% To make the new operator compatible with is..
R <- X :-
    compound(X),            % If the input is a compound/function
    X =.. [OP, X2, X3],     % Deconstruct it
    R2 <- X2,               % Recurse to evaluate the arguments
    R3 <- X3,
    Expr =.. [OP, R2, R3],  % Rebuild a compound with the evaluated arguments
    R is Expr,              % And send it to is
    !.
R <- X :- R is X, !.        % If it's not a compound, just use is directly

다음과 같이 작성할 수 있습니다.

multimangle(X,Y) :- X <- mangle(mangle(mangle(Y))).

그러나 일부 현대 프롤로그는 더 나아가 이러한 유형의 술어에 대한 사용자 정의 구문을 제공합니다. 예를 들어, Visual Prolog에서 다음을 수행하십시오.

mangle(X) = Y :- Y = ((X*5)+2).
multimangle(X,Y) :- Y = mangle(mangle(mangle(X))).

위의 <- 연산자와 기능적 스타일 술어는 여전히 관계로 작동합니다. 선택 지점을 갖고 여러 통일을 수행하는 것이 합법적입니다. 첫 번째 예에서는 잘라 내기를 사용하여이 문제를 방지합니다. Visual Prolog에서는 관계에 대한 함수 구문을 사용하는 것이 일반적이며 선택 점은 정상적인 방법으로 작성됩니다. 예를 들어, 목표 X = (std::fromTo(1,10))*10 은 바인딩 X = 10에서 성공합니다 , X = 20, X = 30, X = 40 등.

플로우 / 모드 선언

Prolog에서 프로그래밍 할 때 모든 가능한 매개 변수 조합에 대해 통합하는 술어를 작성하는 것이 항상 가능하거나 바람직하지는 않습니다. 예를 들어, between(X,Y,Z) 를 표현하는 between(X,Y,Z) 의 술어 between(X,Y,Z) X와 Y 사이의 숫자입니다. X, Y 및 Z가 모두 바인드 된 경우 (Z는 X와 Y 사이 또는 또는 X와 Y가 바인드되고 Z가 사용 가능하지 않은 경우 (Z는 X와 Y 사이의 모든 수와 통합되거나 Y <X 인 경우 술어가 실패 함); 그러나 X와 Z가 결합되어 있고 Y가 자유로운 경우와 같은 다른 경우에는 잠재적으로 무한한 수의 통일이있을 수 있습니다. 이 방법을 구현할 수는 있지만 대개는 그렇지 않습니다.

플로우 선언 또는 모드 선언 은 바인드 된 매개 변수의 다른 조합으로 호출 될 때 술어가 어떻게 작동하는지 명시 적으로 설명합니다. between 의 경우 선언은 다음과 같습니다.

%! between(+X,+Y,+Z) is semidet.
%! between(+X,+Y,-Z) is nondet. 

각 행은 술어에 대해 하나의 잠재적 인 호출 패턴을 지정합니다. 각각의 인수로 장식되어 + 가 결합되는 경우를 표시하거나하는 - 그렇지 않은 곳의 경우를 나타냅니다 (예 : 부분적으로 결합 될 수있다 튜플 또는 목록과 같은 더 복잡한 유형에 사용할 다른 장식도 있습니다). after 키워드 이 경우의 술어의 동작을 나타내며 다음 중 하나 일 수 있습니다.

  • 술어가 항상 선택 지점없이 성공하면 det . 예를 들어 add(+X,+Y,-Z)det 두 개의 주어진 숫자 X와 Y를 추가하면 항상 하나의 답변 만 있기 때문입니다.
  • 술어가 성공하거나 실패 할 경우 선택 지점이없는 경우 semidet 입니다. 위와 같이, between(+X,+Y,+Z) 사이는 Z가 X와 Y 중 하나이거나 semidet 않기 때문에 semidet 입니다.
  • 술어가 항상 성공하면 multi 가되지만 선택 포인트가있을 수 있습니다 (또한 그렇지 않을 수도 있음). 예를 들어, factor(+X,-Y) 될 것이다 multi 자체를 - - 항상 숫자가 적어도 하나 개의 요소를 가지고 있기 때문에 더있을 수 있습니다.
  • nondet 술어는 선택 사항으로 성공하거나 실패 할 수 있습니다합니다. 예를 들어, between(+X,+Y,-Z)nondet 하지 않을 수 있습니다. 왜냐하면 X와 Y 사이의 숫자에 대해 Z에 대해 여러 가지 가능한 통합이있을 수 있기 때문이며 Y가 X보다 작 으면 숫자가 없어서 조건부가 실패합니다.

흐름 / 모드 선언은 용어의 의미를 명확히하기 위해 인수 레이블과 결합하거나 입력 할 수도 있습니다. 예를 들어, between(+From:Int, +To:Int, +Mid:Int) is semidet 있습니다.

순수 Prologs에서 플로우 및 모드 선언은 선택 사항이며 문서 생성에만 사용되지만 프로그래머가 인스턴스화 오류의 원인을 식별하는 데 매우 유용합니다.

Mercury에서 플로우 및 모드 선언 (및 유형)은 필수이며 컴파일러에서 유효성을 검사합니다. 사용 된 구문은 위와 같습니다.

Visual Prolog에서 플로우 및 모드 선언 및 유형도 필수이며 구문이 다릅니다. 위 선언은 다음과 같이 작성됩니다.

between : (int From, int To, int Mid) determ (i,i,i) nondeterm (i,i,o).

의미는 위와 동일하지만 다음과 같은 차이점이 있습니다.

  • 플로우 / 모드 선언은 타입 선언과 분리되어 있습니다 (하나의 술어에 대한 플로우 / 모드는 타입 오버로딩에 따라 변하지 않는다고 가정되기 때문에).
  • io+- 로 사용되며 순서에 따라 매개 변수와 일치합니다.
  • 사용되는 용어가 다릅니다. det 해진다 procedure , semidet 된다 determ 하고 nondet 된다 nondeterm ( multi 아직 multi ).


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow