如何在Prolog中通过分隔符将列表拆分为列表列表

时间:2021-06-15 10:14:45

标签: list split prolog delimiter

我知道这应该很简单,但我不知道。基本上给定一个列表,例如 [a,b,c,' ',d,' ',e,f],列表应该被拆分为列表列表。在此示例中,输出应为 [[a,b,c],[d],[e,f]]

目前我做了这样的事情:

helper([], _, _).
helper([Elem|Rest], Sub, Result):- 
    (Elem == ',' -> 
        append(Result, Sub, NewResult),
        helper(Rest, [], NewResult)
    ;   
        append(Sub, [Elem], New),
        helper(Rest, New, Result)
    ).

有人能提出一些想法吗?

2 个答案:

答案 0 :(得分:0)

让我们首先编写一个 flatten/2 谓词,它将列表列表转换为扁平列表,前面的“列表中断”由 ~(而不是 ~,但只是因为在源码中更容易查找)

% flatten(ListOfLists,ListWithCommas)

flatten([Sublist1|[Sublist2|More]],MergedList) :-
   !, % not needed, but we want to tell Prolog to not 
      % look for more solutions in the next two clauses
      % because there aren't any
   append(Sublist1,[~],Part1),
   flatten([Sublist2|More],Part2),    
   append(Part1,Part2,MergedList).
flatten([Sublist],Sublist).
flatten([],[]).

% - - 8< - - - - - - - 8< - - - - - - - 8< - - - - - - - 
 
:- begin_tests(flatten).

test(a1) :- 
   flatten([],Result),
   assertion(Result == []).
   
test(a2) :- 
   flatten([[a,b,c]],Result),
   assertion(Result == [a,b,c]).

test(a3) :- 
   flatten([[]],Result),
   assertion(Result == []).

test(a4) :- 
   flatten([[a,b,c],[d,e,f]],Result),
   assertion(Result == [a,b,c,~,d,e,f]).

test(a5) :- 
   flatten([[a,b,c],[d,e,f],[g,h,i]],Result),
   assertion(Result == [a,b,c,~,d,e,f,~,g,h,i]).

test(a6) :- 
   flatten([[a,b,c],[],[g,h,i]],Result),
   assertion(Result == [a,b,c,~,~,g,h,i]).
   
:- end_tests(flatten).

这很好用。如果我们运行测试:

?- run_tests.
% PL-Unit: flatten ...... done
true.

我们不能神奇地“反转”上述行为(虽然这很好):

?- flatten(ListOfLists,[a,b,c,~,d,e,f]).
ERROR: Stack limit (1.0Gb) exceeded
ERROR:   Stack sizes: local: 0.7Gb, global: 0.2Gb, trail: 60.4Mb

因此我们必须编写一个 heighten/2,我们知道内置谓词的局限性并了解数据流:

heighten([],[]).  
heighten([Sublist],Sublist) :- 
   \+ member(~,Sublist),
   !.                                % Not needed, but tell Prolog to not look
                                     % for another solution via the next clause because there isn't one
heighten([Sublist1|[Sublist2|More]],MergedList) :-
   append(Part1,Part2,MergedList),   % Propose how to split MergedList into Part1 and Part2   
   append(Sublist1,[~],Part1),       % We demand that Part1 end with ~, which also gives us Sublist1
   \+ member(~,Sublist1),            % And ~ is itself not member of Sublist1
   !,                                % Not needed, but tell Prolog to not backtrack
                                     % past this point, because there are no more solutions there
   heighten([Sublist2|More],Part2).
   
% - - 8< - - - - - - - 8< - - - - - - - 8< - - - - - - - 
   
:- begin_tests(heighten).

test(b1) :- 
   bagof(Prior,heighten(Prior,[]),Bag),
   assertion(Bag == [ [] ,  [[]]] ). % clearly this mapping is not bijective
   
test(b2) :- 
   heighten(Prior,[a,b,c]),
   assertion(Prior == [[a,b,c]]).

test(b4) :- 
   heighten(Prior,[a,b,c,~,d,e,f]),
   assertion(Prior == [[a,b,c],[d,e,f]]).

test(b5) :- 
   heighten(Prior,[a,b,c,~,d,e,f,~,g,h,i]),
   assertion(Prior == [[a,b,c],[d,e,f],[g,h,i]]).

test(b6) :- 
   heighten(Prior,[a,b,c,~,~,g,h,i]),
   assertion(Prior == [[a,b,c],[],[g,h,i]]).
   
:- end_tests(heighten).

这正是我们所需要的。我真的很惊讶它的效果如此之好,我没有完全在脑海中追踪行为:

?- run_tests.
% PL-Unit: flatten ...... done
% PL-Unit: heighten ..... done
% All 11 tests passed
true.

它不如“搜索”的命令式版本那么高效:append(Part1,Part2,MergedList) 必须提出解决方案,直到找到一个匹配为止。

答案 1 :(得分:0)

在分隔符上拆分列表应该不会比这更困难:

  • 从列表中删除项目,直到遇到分隔符,然后
  • 对剩下的东西递归地做同样的事情。

像这样:

split( []    , _ , []       ) .  % if we find the end of the list, we're done.
split( [H|T] , D , [S|LoLs] ) :- % otherwise...
    take([H|T],D,S,R),           % remove items from the list until we find a delimiter
    split(R,D,LoLs).             % and recurse down on whatever is left.

take/4 也很简单:

take( [H|T] , D , [H|S] , R ) :- H \== D , take(T,D,S,R). % non-empty list, no delimiter (yet).
take( [D|R] , D , []    , R ) .                           % non-empty list, delimiter found.
take( []    , _ , []    , []) .                           % empty list.

相关问题