Есть ли в Prolog оператор спреда/splat/*args?

Во многих процедурных языках (например, Python) я могу "распаковать" список и использовать его в качестве аргументов функции. Например...

def print_sum(a, b, c):
  sum = a + b + c
  print("The sum is %d" % sum)

print_sum(*[5, 2, 1])

Этот код напечатает: "The sum is 8"

Вот документация по этой языковой функции.

Есть ли в Prolog аналогичная функция?

Есть ли способ воспроизвести это поведение распаковки аргументов в Прологе?

Например, я хотел бы распаковать переменную списка перед передачей ее в позвоните.

Могу ли я написать такой предикат?

assert_true(Predicate, with_args([Input])) :-
  call(Predicate, Input).

% Where `Input` is somehow unpacked before being passed into `call`.

... Который я мог бы затем запросить с помощью

?- assert_true(reverse, with_args([ [1, 2, 3], [3, 2, 1] ])).
% Should be true, currently fails.

?- assert_true(succ, with_args([ 2, 3 ]).
% Should be true, currently fails.

?- assert_true(succ, with_args([ 2, 4 ]).
% Should be false, currently fails.

Примечания

  • Вы можете подумать, что это XY-проблема. Может быть, но не расстраивайтесь. Было бы идеально получить ответ на только заголовок моего вопроса.

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

  • Возможно, я подхожу к Prolog со слишком процедурным мышлением. Если это так, то какое мышление поможет мне решить проблему?

  • Я использую SWI-Пролог.


person byxor    schedule 03.01.2017    source источник
comment
Вы, наверное, сами заметили, что call(foo, a, b) это то же самое, что call(foo(a, b)) и call(foo(a), b). Вот почему такой вызов, как include(>(2), [1,2,3,4], R), работает.   -  person    schedule 03.01.2017
comment
Возможный дубликат предиката Prolog с переменным числом аргументов   -  person Anderson Green    schedule 11.09.2018


Ответы (2)


Во-первых: слишком просто, используя унификацию и сопоставление с образцом, получить элементы списка или аргументы любого терма, если вы знаете его форму. Другими словами:

sum_of_three(X, Y, Z, Sum) :- Sum is X+Y+Z.

?- List = [5, 2, 1],
   List = [X, Y, Z], % or List = [X, Y, Z|_] if the list might be longer
   sum_of_three(X, Y, Z, Sum).

Например, если у вас есть аргументы командной строки, и вас интересуют только первые два аргумента командной строки, их легко получить следующим образом:

current_prolog_flag(argv, [First, Second|_])

Многие стандартные предикаты принимают списки в качестве аргументов. Например, любой предикат, которому требуется несколько опций, например open/3 и open/4. Такая пара может быть реализована следующим образом:

open(SrcDest, Mode, Stream) :-
    open(SrcDest, Mode, Stream, []).

open(SrcDest, Mode, Stream, Options) :-
    % get the relevant options and open the file

Здесь получить соответствующие параметры можно с помощью библиотеки, такой как library(option), который можно использовать, например, так:

?- option(bar(X), [foo(1), bar(2), baz(3)]).
X = 2.

Вот как вы можете передавать именованные аргументы.

Еще одна вещь, которая не была упомянута в ответе @false: в Прологе вы можете делать такие вещи:

Goal = reverse(X, Y), X = [a,b,c], Y = [c,b,a]

и в какой-то более поздний момент:

call(Goal)

или даже

Goal

Другими словами, я не вижу смысла передавать аргументы в виде списка, а не просто передавать цель в виде термина. В какой момент аргументы представляют собой список и почему они упакованы в список?

Другими словами: с учетом того, как работает call, обычно нет необходимости распаковывать список [X, Y, Z] в конъюнкцию X, Y, Z, которую затем можно использовать в качестве списка аргументов. Как и в комментарии к вашему вопросу, все в порядке:

call(reverse, [a,b,c], [c,b,a])

а также

call(reverse([a,b,c]), [c,b,a])

а также

call(reverse([a,b,c], [c,b,a]))

Последний такой же, как

Goal = reverse([a,b,c], [c,b,a]), Goal

Вот почему вы можете сделать что-то вроде этого:

?- maplist(=(X), [Y, Z]).
X = Y, Y = Z.

вместо того, чтобы писать:

?- maplist(=, [X,X], [Y, Z]).
X = Y, Y = Z.
person Community    schedule 03.01.2017

Для этой цели служит встроенный (=..)/2 (univ). Например.

?- G =.. [g, 1, 2, 3].
G = g(1,2,3).

?- g(1,2,3) =.. Xs.
Xs = [g,1,2,3].

Однако обратите внимание, что во многих случаях использование (=..)/2, где количество аргументов фиксировано, может быть заменено на call/2...call/8.

person false    schedule 03.01.2017
comment
Это может быть именно то, что мне нужно. Я поковыряюсь с ним и вернусь, если смогу его использовать. Спасибо. - person byxor; 03.01.2017
comment
А принимающая сторона? Как бы это сделать? Как правильно написать my_first(First, *Rest):- nth1(1, Rest, First).? - person pkoch; 06.05.2020
comment
@pkoch то же самое: my_first( First, Term ) :- Term =.. [_Functor | Args], nth1(1, Args, First)., затем ?- my_first( First, g(a,b,c,d) ). ==› First = a.. - person Will Ness; 11.05.2020
comment
Это не совсем то, что я ищу. Как вы можете, не объявляя отношения по арности, заставить все эти вызовы вести себя одинаково? my_first(First, a), my_first(First, a, b), my_first(First, a, b, c), my_first(First, a, b, c, d) Я знаю, что это не стандартная практика, но меня интересуют языковые способности, а не стиль. - person pkoch; 12.05.2020
comment
@pkoch для этого вам нужно будет рассматривать все эти вызовы как данные. с моим определением вам придется позвонить my_first( First, my_first( First, a, b, c, d, ...)). (и, к вашему сведению, нет уведомлений, если вы обычно не используете @). если отношение не определено, нет никакого отношения для вызова, какой бы арности оно ни было. и единственный способ определить отношение — это определить предикат с соответствующими аргументами, то есть арность. вы можете определить все подпункты программно, с помощью asserta / assertz. - person Will Ness; 12.05.2020