错误:";“超出全局堆栈”;处理Prolog对列表时

错误:";“超出全局堆栈”;处理Prolog对列表时,prolog,Prolog,在SWI Prolog中,我有一个列表,其元素是表单键值列表的成对元素。例如,一个这样的列表可能如下所示: [1-[a,b],2-[],3-[c]] 我想将这个列表转换为一个嵌套的成对的列表,其形式为Key-[Value],其中Value是ValuesList中的一个元素。上述示例将转换为: [[1-[a],2-[],3-[c]], [1-[b],2-[],3-[c]]] 我目前的解决方案如下: % all_pairs_lists(+InputList, -OutputLists). all

在SWI Prolog中,我有一个列表,其元素是表单键值列表的成对元素。例如,一个这样的列表可能如下所示:

[1-[a,b],2-[],3-[c]]
我想将这个列表转换为一个嵌套的成对的列表,其形式为Key-[Value],其中Value是ValuesList中的一个元素。上述示例将转换为:

[[1-[a],2-[],3-[c]], [1-[b],2-[],3-[c]]]
我目前的解决方案如下:

% all_pairs_lists(+InputList, -OutputLists).
all_pairs_lists([], [[]]).
all_pairs_lists([Key-[]|Values], CP) :-
  !,
  findall([Key-[]|R], (all_pairs_lists(Values,RCP), member(R,RCP)), CP).
all_pairs_lists([Key-Value|Values], CP) :-
  findall([Key-[V]|R], (all_pairs_lists(Values,RCP), member(V,Value), member(R,RCP)), CP).
使用此谓词,调用

all_pairs_lists([1-[a,b],2-[],3-[c]],OutputLists).
将变量OutputList绑定到上面提到的所需结果。虽然它看起来是正确的,但当InputList有很长的列表作为值时,此实现会导致“全局堆栈外”错误


是否有一种更少消耗堆栈的方法来实现这一点?对于这种类型的数据结构来说,这似乎是一种非常常见的操作。

好吧,总而言之,您做得不对

在Prolog中,当我们想表达一个关系而不是一个函数(可能有几个结果而不是一个)时,我们不直接使用
findall/3
member/2
。我们更愿意说明关系是什么,然后,如果我们需要使用
findall/3
的结果列表,可能一旦完成了

这里的意思是我们想要表达以下关系:

获取
键值列表
并返回
键值-[Value]
列表,其中
值列表的成员

我们可以这样做:

% The base case: handle the empty list
a_pair_list([], []).

% The case where the Values list is empty, then the resulting [Value] is []
a_pair_list([Key-[]|List], [Key-[]|Result]) :-
    a_pair_list(List, Result).
% The case where the Values list is not empty, then Value is a member of Values.
a_pair_list([Key-[Not|Empty]|List], [Key-[Value]|Result]) :-
    member(Value, [Not|Empty]),
    a_pair_list(List, Result).
一旦表达了这种关系,我们就可以获得我们想要的所有信息:

?- a_pair_list([1-[a, b], 2-[], 3-[c]], Result).
Result = [1-[a], 2-[], 3-[c]] ;
Result = [1-[b], 2-[], 3-[c]] ;
false.
所需列表现在只是一个相当直接的
findall/3
调用:

all_pairs_lists(Input, Output) :-
    findall(Result, a_pair_list(Input, Result), Output).

重要的是要记住,最好远离额外的逻辑内容:
/0
findall/3
,等等。。。因为它通常会导致不太通用的程序和/或不太正确的程序。在这里,既然我们可以用一种纯粹和干净的方式表达上述关系,我们就应该这样做。通过这种方式,我们可以将
findall/3
的烦人使用限制在最低限度。

总之,你做得不对

在Prolog中,当我们想表达一个关系而不是一个函数(可能有几个结果而不是一个)时,我们不直接使用
findall/3
member/2
。我们更愿意说明关系是什么,然后,如果我们需要使用
findall/3
的结果列表,可能一旦完成了

这里的意思是我们想要表达以下关系:

获取
键值列表
并返回
键值-[Value]
列表,其中
值列表的成员

我们可以这样做:

% The base case: handle the empty list
a_pair_list([], []).

% The case where the Values list is empty, then the resulting [Value] is []
a_pair_list([Key-[]|List], [Key-[]|Result]) :-
    a_pair_list(List, Result).
% The case where the Values list is not empty, then Value is a member of Values.
a_pair_list([Key-[Not|Empty]|List], [Key-[Value]|Result]) :-
    member(Value, [Not|Empty]),
    a_pair_list(List, Result).
一旦表达了这种关系,我们就可以获得我们想要的所有信息:

?- a_pair_list([1-[a, b], 2-[], 3-[c]], Result).
Result = [1-[a], 2-[], 3-[c]] ;
Result = [1-[b], 2-[], 3-[c]] ;
false.
所需列表现在只是一个相当直接的
findall/3
调用:

all_pairs_lists(Input, Output) :-
    findall(Result, a_pair_list(Input, Result), Output).

重要的是要记住,最好远离额外的逻辑内容:
/0
findall/3
,等等。。。因为它通常会导致不太通用的程序和/或不太正确的程序。在这里,既然我们可以用一种纯粹和干净的方式表达上述关系,我们就应该这样做。通过这种方式,我们可以将
findall/3
的烦人使用限制在最低限度。

因为@Mog已经清楚地解释了问题可能是什么,这里有一个版本(ab)使用基本的“功能性”内置项来处理列表:

all_pairs_lists(I, O) :-
    findall(U, maplist(pairs_lists, I, U), O).

pairs_lists(K-[], K-[]) :- !.
pairs_lists(K-L, K-[R]) :- member(R, L).
测试:


正如@Mog已经清楚地解释了问题可能是什么,这里有一个版本(ab)使用基本的“功能”内置来处理列表:

all_pairs_lists(I, O) :-
    findall(U, maplist(pairs_lists, I, U), O).

pairs_lists(K-[], K-[]) :- !.
pairs_lists(K-L, K-[R]) :- member(R, L).
测试:


谢谢,这真的把事情讲清楚了。但是,对于大型输入列表,它仍然会生成相同的堆栈溢出错误。五个键,每个键都有一个50个值的列表,这会导致堆栈溢出。但是,根据您的具体需要,立即返回列表可能不太有用。你也可以研究一下,完全跳过findall。顺便说一句,5个键,每个键有50个值,是312M个列表,有很多列表。我想,即使扩展堆栈也会留下其他问题。你需要参考我之前的评论我想是的,你可能是对的。。我没有预料到我的数据结构会变得如此庞大,这可能表明我需要采取不同的方法。谢谢,这确实让事情变得清楚了。但是,对于大型输入列表,它仍然会生成相同的堆栈溢出错误。五个键,每个键都有一个50个值的列表,这会导致堆栈溢出。但是,根据您的具体需要,立即返回列表可能不太有用。你也可以研究一下,完全跳过findall。顺便说一句,5个键,每个键有50个值,是312M个列表,有很多列表。我想,即使扩展堆栈也会留下其他问题。你需要参考我之前的评论我想是的,你可能是对的。。我没有预料到我的数据结构会变得如此庞大,这可能表明我需要采取不同的方法。这个版本看起来非常好。尽管如此,就像Mog的版本一样,它会导致一个输入的堆栈溢出,该输入有5个键,每个键有一个大约50个值的列表。尽管如此,就像Mog的版本一样,它会导致具有5个键和每个值约50个值的列表的输入出现堆栈溢出。