(пролог) Могу ли я абстрагироваться от всех перестановок этого шаблона вместо того, чтобы писать каждую по отдельности?

Я только начинаю с пролога, и хотя я думаю, что довольно хорошо разбираюсь в том, как его использовать, я наткнулся на стену здесь. У меня такое правило:

foo(A, B, C) :- bar(A, B), baz(C).
foo(A, B, C) :- bar(A, C), baz(B).
foo(A, B, C) :- bar(B, A), baz(C).
% etc, etc, for all permutations of `A, B, C`

Однако реальное правило имеет более 3 параметров, и запись/генерация их всех привела бы к тому, что я получил бы очень, очень большой файл. Есть ли способ абстрагировать этот шаблон?


person Community    schedule 18.07.2019    source источник


Ответы (2)


Вот что я бы сделал:

foo(A, B, C) :- 
    select(X, [A, B, C], L1),
    select(Y, L1, [Z]),
    bar(X, Y),
    baz(Z).

Идея состоит в том, что мы будем перетасовывать каждую комбинацию этих параметров, используя select/3 (один из моих любимых предикатов), а затем, переставив их, мы можем просто передать два предиката, которые вам интересны.

Это может быть более понятно, но L2 всегда будет содержать один элемент, поэтому я просто сопоставил его в предыдущем предложении выше:

foo(A, B, C) :- 
    select(X, [A, B, C], L1),
    select(Y, L1, L2),
    select(Z, L2, _),
    bar(X, Y),
    baz(Z).

Редактировать: давайте обработаем произвольное N. Сначала нам понадобится select_n/4, который даст мне N элементов из списка. Я собираюсь написать это с помощью DCG, потому что мне так проще:

select_n([X|Xs], N) --> select(X), { succ(N0, N) }, select_n(Xs, N0).
select_n([], 0) --> [].

DCG — это не только отличный способ синтаксического анализа текста, но и удобный способ объединения предикатов в строки, которые имеют список ввода и список вывода.

Теперь мы можем использовать этот помощник для создания более общей версии вашего предиката. Допустим, вы берете список для простоты:

foo(L) :-
    select_n(BarArgs, 2, L, L1),
    select_n(BazArgs, 1, L1, []),
    apply(bar, BarArgs), 
    apply(baz, BazArgs).

Вы можете заменить 2 и 1 выше на все, что хотите, предположительно, исходя из арности bar и baz, которую вы хотите вызвать.

Если вы не используете SWI или просто хотите использовать вместо него call/1, вам придется использовать оператор univ =.. для построения термина перед использованием call/1:

foo(L) :-
    select_n(BarArgs, 2, L, L1),
    select_n(BazArgs, 1, L1, []),
    Bar =.. [bar|BarArgs],
    Baz =.. [baz|BazArgs],
    call(Bar),
    call(Baz).
person Daniel Lyons    schedule 18.07.2019
comment
Пока я не пробовал, не будет ли =.. более полезным. Тогда код не зависит от точного количества аргументов. - person Guy Coder; 18.07.2019
comment
@GuyCoder Я отредактировал ответ, пожалуйста, дайте мне знать, что вы думаете. - person Daniel Lyons; 18.07.2019
comment
Я понимаю, почему у меня это не сработало для массивов, отличных от длины 3. Я думал, что они не зависят от размера, но я вижу, что они жестко закодированы с 1 и 2. - person Guy Coder; 18.07.2019
comment
Я думал, что это будет работать как это - person Guy Coder; 18.07.2019
comment
@GuyCoder, если это то, чего он хочет, этот ответ хороший, но я думаю, что у него всегда есть N аргументов, распределенных по M предикатам, каждый со своей арностью, но что в сумме дает N. По крайней мере, это то, что я сделал из вопроса . Конечно, я не планировал пытаться ответить на подразумеваемый вопрос, когда прямой вопрос был прямым. Обычно лучше просто ответить на прямой вопрос. Но ты заставил меня сделать это. :) - person Daniel Lyons; 18.07.2019
comment
Я не заставлял тебя это делать, ты знаешь, что это был просто вызов, который ты хотел сделать. :) Я бы ответил на этот вопрос, но не думаю, что ответы, которые он здесь получает, являются решением его проблемы в долгосрочной перспективе. Это похоже на проблему, когда вы должны помнить, что данные - это код, а код - это данные, и он жестко запрограммировал что-то в аргументах, которые не должны быть жестко закодированы. Ваш ответ был близок, когда вы изменили аргументы с заданного числа на список. - person Guy Coder; 18.07.2019
comment
@GuyCoder Я думаю, что настоящая проблема заключается в комбинаторном взрыве, который он испытает, когда N превысит довольно небольшие числа, но я думаю, что знание того, как возиться с оценкой Пролога, является ключом к его продуктивному использованию. - person Daniel Lyons; 18.07.2019

Другой ответ уже очень хорош. Еще немного мыслей.

На Прологе очень легко писать мини-интерпретаторы для ваших микроязыков. Вот еще один (дешевый) способ решить исходную проблему:

foo(A, B, C) :-
    permutation([A, B, C], [X, Y, Z]),
    bar(X, Y), baz(Z).

... и с помощью call вы можете параметризовать bar и baz:

foo(Bar, Baz, A, B, C) :-
    permutation([A, B, C], [X, Y, Z]),
    call(Bar, X, Y), call(Baz, Z).

Это означает, что теперь вы можете передавать (по имени) любой предикат с арностью 2 как Bar и любой предикат с арностью 1 как Baz. И если у вас есть предикат bar_x, который принимает 3 аргумента, вы можете связать первый из них раньше. Например, вы можете назвать свой foo/5 как:

?- foo(bar(hello), baz, 1, 2, 3).

это оценит bar(hello, 1, 2) для первого решения.

Так что теперь единственный нетривиальный вопрос, какой у вас интерфейс? Как вы хотите записывать свои запросы? Вы предоставляете три аргумента как отдельные аргументы или как список? Вы хотите параметризовать вызываемые предикаты? И так далее.

person User9213    schedule 19.07.2019