【问题标题】:How to split a list into list of list by delimiter in Prolog如何通过Prolog中的分隔符将列表拆分为列表列表
【发布时间】:2021-06-15 10:14:45
【问题描述】:

我知道这应该很简单,但我不知道。基本上给定一个列表,例如[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)
    ).

谁能提出一些想法?

【问题讨论】:

  • 大多数声明性解决方案胜出...

标签: list split prolog delimiter


【解决方案1】:

让我们从编写一个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) 必须提出解决方案,直到找到一个匹配。

【讨论】:

    【解决方案2】:

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

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

    类似这样的:

    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.
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-27
      • 2019-04-08
      • 1970-01-01
      • 2018-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多