【问题标题】:ERROR: "Out of global stack" when processing Prolog list of pairs错误:处理 Prolog 对列表时“超出全局堆栈”
【发布时间】:2014-01-26 04:27:16
【问题描述】:

在 SWI-Prolog 中,我有一个列表,其元素是 Key-ValuesList 形式的对。例如,一个这样的列表可能如下所示:

[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).

将变量 OutputLists 绑定到上面提到的所需结果。虽然看起来是正确的,但当 InputList 有很长的列表作为值时,此实现会导致“Out of global stack”错误。

有没有更少的堆栈消耗方法来做到这一点?对于这种类型的数据结构,这似乎是一种很常见的操作。

【问题讨论】:

    标签: prolog


    【解决方案1】:

    嗯,总而言之,你做错了。

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

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

    获取Key-Values 的列表并返回Key-[Value] 的列表,其中ValueValues 列表的成员。

    我们可以这样做:

    % 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).
    

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

    【讨论】:

    • 谢谢,这真的让事情变得清晰了。但是,对于大型输入列表,它仍然会产生相同的堆栈溢出错误。五个键,每个键有 50 个值的列表会导致堆栈溢出。
    • 但根据您的具体需要,一次返回列表可能没有用处。你也可以调查一下,完全跳过 findall。
    • btw 5 个键,每个键有 50 个值,是 312M 个列表,列表很多。我猜即使扩展堆栈也会留下其他问题。你需要参考我之前的评论我认为
    • 是的,你可能是对的。我没想到我的数据结构会变得如此之大,这可能表明我需要采取不同的方法。
    【解决方案2】:

    正如@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).
    

    测试:

    ?- all_pairs_lists([1-[a,b],2-[],3-[c]],OutputLists).
    OutputLists = [[1-[a], 2-[], 3-[c]], [1-[b], 2-[], 3-[c]]].
    

    【讨论】:

    • 这个版本很好看。尽管如此,就像 Mog 的版本一样,对于具有 5 个键和每个大约 50 个值的列表的输入,它会导致堆栈溢出。
    猜你喜欢
    • 2012-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多