prolog是否有spread / splat / * args运算符?

时间:2017-01-03 08:07:31

标签: prolog variadic-functions splat

在许多程序语言(例如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"

这是documentation for this language feature

prolog是否有类似的功能?

有没有办法在Prolog中复制这个参数解包行为?

例如,我想在将列表变量传递到call之前解压缩列表变量。

我可以写一个这样的谓词吗?

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 Problem可以,但不要气馁。

  • 你可能会告诉我,我很难接近这个问题。我知道你的意图是好的,但这种建议不会有助于回答这个问题。请参阅上述观点。

  • 也许我在太多的程序性思维中接近 Prolog 。如果是这种情况,那么什么心态可以帮助我解决问题?

  • 我使用的是SWI-Prolog。

2 个答案:

答案 0 :(得分:4)

内置(=..)/2(univ)可达到此目的。 E.g。

?- 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

答案 1 :(得分:4)

首先:如果你知道它的形状,使用统一和模式匹配来获取列表的元素或任何术语的参数都太简单了。换句话说:

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/3open/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.

这就是你可以传递命名参数的方法。

answer by @false中未提及的另一件事:在Prolog中,你可以这样做:

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.