根据列表中的分隔符拆分序言列表?

Splitting a prolog list based on a delimiter from the list?

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

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

我正在尝试构建一个列表列表,结果是

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

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

[joke3] 

不在该列表中。

我尝试使用 split_string 但它对我需要获得的东西不起作用。我也尝试过使用 append 进行递归,但效果不佳。希望能给我指明正确的方向。

谢谢!

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

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有多余的选择点(这就是为什么你可以在第一个结果后按;,因为它认为可以有更多的答案但是有none)。您可以使用 !once.

解决此问题

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

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