【问题标题】:Splitting a prolog list based on a delimiter from the list?根据列表中的分隔符拆分序言列表?
【发布时间】:2026-01-19 06:05:01
【问题描述】:

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

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

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

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

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

[joke3] 

不在该列表中。

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

谢谢!

【问题讨论】:

    标签: prolog declarative


    【解决方案1】:

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

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

    【讨论】:

    • 这是一个非常好的答案 Fatalize。感谢您帮助我理解我的问题,不胜感激!
    • s(X) 仅用于注释
    • 你原来的定义对split_on_delimiter([a,B],d,[[a,c]]),B=b.成功,对B=b,split_on_delimiter([a,B],d,[[a,c]]).失败,所以效率不是最大的问题。
    • 在像 SICStus 这样的系统中,( Xs = [], Nimporte_0 ; Xs = [_|_], Quelconque_0 ) 将尽可能确定。只有 SWI 需要if_(Xs = [], Nimporte_0, ...) 才能避开 CP。
    • member(X, R), length(X,Length), Length > 1 最好替换为X = [_,_|_], member(X, R)