Что означают числовые аргументы для meta_predicate в SWI-Prolog?

Я пишу программу на Прологе и пытаюсь включить модули в структуру программы, чтобы инкапсулировать сложность и уменьшить избыточную функциональность.

Одна особенность, с которой у меня возникают трудности, — это использование метапредикатов. Я хотел бы определить метапредикат в одном модуле, а затем импортировать его в другой модуль; это вносит осложнения. К счастью, директива meta_predicate помогает разрешать префиксы модулей, но у меня возникли проблемы с пониманием аргументов, описанных здесь: https://www.swi-prolog.org/pldoc/man?section=metapred

В частности, у меня проблемы с числовыми аргументами. Согласно документации:

Аргумент — это термин, который используется для ссылки на предикат с числом аргументов на N больше, чем у данного термина аргумента. Например: call(0) или maplist(1, +).

Я понимаю, что аргумент, обозначенный числовым значением, будет термином, который используется для ссылки на предикат. Чего я не понимаю, так это того, как указанный предикат может иметь больше аргументов, чем термин аргумента. Может ли кто-нибудь предложить более подробное объяснение того, когда подходит числовой аргумент, или пример того, когда его целесообразно использовать?


person AugerAlpha    schedule 29.03.2020    source источник


Ответы (2)


Это формулировка, которую легко понять только тогда, когда знаешь, что она означает, и, вероятно, ее следует переделать.

:-meta_predicate maplist(2, ?, ?).

... просто означает, что аргумент на месте «2» будет использоваться в качестве ядра предиката, который будет вызываться. У нас нет специального обозначения для этого (большая ошибка ИМХО), поэтому мы напишем это стандартно как терм вроде f(foo,bar), или f(foo), или f. Что метапредикат maplist/3 будет делать с этим термином? Что ж, он синтаксически преобразует его и прибавит "2" дополнительных аргумента на его конец (и только на его конец, что приводит к неловкости): f(foo,bar, ARG1,ARG2), или f(foo, ARG1,ARG2), или f(ARG1,ARG2). Затем maplist/3 вызовет его.

Например, для вышеупомянутого maplist/3 возьмите этот предикат с двумя аргументами:

myprint(X,Y) 
   :- format("~w\n",[(X,Y)]).

Его можно использовать в вызове maplist/3 следующим образом, без указания каких-либо аргументов:

maplist(myprint,[0,1,2,3],[a,b,c,d]).

и два аргумента, по одному из каждого списка, будут прикреплены к термину myprint до того, как этот термин будет посвящен в рыцари, чтобы стать предикатом, и будет называться:

?- maplist(myprint,[0,1,2,3],[a,b,c,d]).
0,a
1,b
2,c
3,d
true.

Это позволяет пропускать «частично заполненные вызовы». maplist/2 добавит 1 аргумент в конец своего первого аргумента, поэтому можно сказать:

?- maplist(myprint("foo+"),[a,b,c,d]).
foo+,a
foo+,b
foo+,c
foo+,d
true.

Вышеприведенное можно использовать в сочетании с library(yall) Пауло Моуры, который оборачивает цель в анонимный предикат, раскрывающий аргументы. Тогда можно гибко переставлять вещи

?- maplist([X,Y]>>format("~w\n",[(X,Y)]),[0,1,2,3],[a,b,c,d]).
0,a
1,b
2,c
3,d
true.
?- maplist([Y,X]>>format("~w\n",[(X,Y)]),[0,1,2,3],[a,b,c,d]).
a,0
b,1
c,2
d,3
true.

На самом деле library(yall) обеспечивает правильный синтаксис для лямбда-выражений, который жестоко отсутствует в стандарте ISO, чтобы явно показать отсутствующие параметры.

Давным-давно можно было вообразить такие выражения:

?- maplist(λX.verify(3,X), [1,2,3,4,5]).

или остаться в ASCIIland, что-то вроде:

?- maplist(\X.verify(3,X), [1,2,3,4,5]).

но этого не произошло.

person David Tonhofer    schedule 29.03.2020
comment
... или ?- maplist(\X^verify(3,X), [1,2,3,4,5]) с использованием library(lambda), который просто остается в пределах ISO. - person false; 29.03.2020
comment
... но этого не произошло. Скорее вы не читали. См., например, Mycroft & O'Keefe, A Polymorphic Type System for Prolog. Искусственный интеллект 23 (1984) с. 306 - person false; 29.03.2020
comment
@false Должен признаться, я еще не смотрел на library(lambda). Но должно было быть просто обозначение-заполнитель. Странно, что пропустили. - person David Tonhofer; 29.03.2020
comment
Просто шаг, чтобы включить call/N в одиночку, был большим усилием. - person false; 29.03.2020

Директива meta-predicate указывает количество аргументов, добавляемых к предикату при вызове. Вот простой пример:

:- meta_predicate foo(2, +).

foo(Pred, X) :-
    call(Pred, X, Y),
    format('~q(~q,~q)~n', [Pred, X, Y]).

some_pred(X, Y) :-
    Y is X + 1.

что дает следующий результат. Как видите, директива meta_predicate приводит к добавлению модуля в аргумент Pred (в данном случае user):

?- foo(some_pred, 5).
user:some_pred(5,6)

Нечто похожее на «замыкание» в других языках можно легко сделать, также используя директиву meta_predicate. Например, мы могли бы иметь

:-meta_predicate foo2(1). 

foo2(Pred):-
    call(Pred, Y),
    format('~q=~q',[Pred,Y]).

и назовите это:

?-foo2(some_pred(5)).
person Peter Ludemann    schedule 29.03.2020
comment
Большое спасибо за ваш ответ. Это ответило на мой вопрос и имело дополнительное преимущество в том, что оно было очень кратким. - person AugerAlpha; 30.03.2020