根据列表中的分隔符拆分prolog列表?

时间:2016-10-19 02:05:46

标签: prolog declarative

在Prolog中,假设我有一个列表,例如

[fun, joke1, joke2, fun, joke3, fun, joke4, joke5, joke6]

我正在尝试构建一个列表,列表将导致

[ [joke1, joke2], [joke4, joke5, joke6] ] 

使用fun作为分隔符并忽略长度为1的构建列表,因此

[joke3] 

不在该列表中。

我尝试使用split_string,但它不适用于我需要获取的内容。我也尝试使用append进行递归但是也没有用。希望我能指出正确的方向。

谢谢!

1 个答案:

答案 0 :(得分:3)

这是一个使用两个谓词的解决方案:

split_on_delimiter(L, D, S) :-
    split_on_delimiter_(L, D, R),
    findall(X, (member(X, R), length(X,Length), Length > 1), S).

split_on_delimiter_([], _, [[]]).    
split_on_delimiter_([D|T], D, [[]|T2]) :-
    split_on_delimiter_(T, D, T2).
split_on_delimiter_([H|T], D, [[H|T2]|T3]) :-
    dif(H, D),
    split_on_delimiter_(T, D, [T2|T3]).

split_on_delimiter_/3是基于分隔符D真正拆分列表的谓词。 split_on_delimiter/3是您要调用的谓词,而后者将调用split_on_delimiter_/3

要仅保留长度超过1的子列表,我们使用findall查找包含Length > 1的拆分结果的所有子列表。

split_on_delimiter_/3本身很简单:

  • 第一条规则:拆分空列表只会产生一个子列表:空列表。
  • 第二条规则:当列表的第一个元素是分隔符时,结果是空列表,后跟递归调用的结果。
  • 第三条规则:当列表的第一个元素不是分隔符(dif(H, D))时,我们将该元素放在第一个子列表的开头并进行递归调用。

一个例子:

?- split_on_delimiter([fun, joke1, joke2, fun, joke3, fun, joke4, joke5, joke6], fun, Z).
Z = [[joke1, joke2], [joke4, joke5, joke6]] ;
false.

注意

split_on_delimiter_/3有无关的选择点(这就是为什么你可以在第一个结果后按;,因为它认为可以有更多的答案,但没有答案)。您可以使用!once解决此问题。

删除这些选择点的更好解决方案是使用module(reif)if_/3(=)/3(虽然我怀疑这对您有用):

split_on_delimiter_(L, D, [L2|T2]) :-
    if_(L = [],
        [L2|T2] = [[]],
        (   L = [H|T],
            if_(H = D,
                (   L2 = [],
                    split_on_delimiter_(T, D, T2)
                ),
                (   L2 = [H|T3],
                    split_on_delimiter_(T, D, [T3|T2])
                )
            )
        )
    ).