我知道这应该很简单,但我不知道。基本上给定一个列表,例如 [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)
).
有人能提出一些想法吗?
答案 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.