这是一个使用两个谓词的解决方案:
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])
)
)
)
).