【问题标题】:Splice in sublists in a list, without using flatten/2拼接列表中的子列表,不使用 flatten/2
【发布时间】:2021-03-04 06:52:47
【问题描述】:

我想在不使用 Prolog 中的 flatten 谓词的情况下制作一个包含多个子列表的列表。

这是我的代码:

acclistsFromList([],A,A).
acclistsFromList([H|T],Listwithinlist,A):-
  not(is_list(H)), 
  acclistsFromList(T,Listwithinlist,A).
acclistsFromList([H|T],Listwithinlist,A):-
  is_list(H), 
  append([H],Listwithinlist,Acc2), 
  acclistsFromList(T,Acc2,A).

我得到这个作为输出

?- listsFromList([1,2,[a,b,c],3,4,[d,e]],X). 
X = [[d, e], [a, b, c]] ;

但我想要这个:

?- listsFromList([1,2,[a,b,c],3,4,[d,e]],X).
X = [a, b, c, d, e] .

?- listsFromList([1,[],2,3,4,[a,b]],X).
X = [a, b] .

?- listsFromList([[[[a]]],b,c,d,e,[f]],X).
X = [f, a] .

?- listsFromList([[[[a]],b,[c]],d,e,[f]],X).
X = [f, a, b, c] .

不使用flatten 的最佳方法是什么?

【问题讨论】:

  • 在您的acclistsFromList([H|T],Listwithinlist,A):-not(is_list(H)), acclistsFromList(T,Listwithinlist,A). 中,在确认H 不是列表后,您不会对它进行任何操作。
  • 你可以使用你自己的扁平谓词:stackoverflow.com/a/9059827/8080648
  • 编辑不得使现有答案无效,这是 SO 规则所禁止的。请不要再这样做了。通过在 SO 上发帖,您永久授予其内容许可,您无法收回。任何进一步破坏帖子的尝试都将报告给版主。

标签: list prolog concat flatten


【解决方案1】:

如果你想自己动手,这里是任意嵌套列表的广度优先枚举:

bfs( XS, L) :- bfs( s(z), [XS|Q], Q,  L, []).

bfs( z, _, _, Z, Z).
bfs( s(N), [[]   |P], Q,  L, Z) :- bfs( N, P, Q,  L, Z).
bfs( s(N), [[A|B]|P], Q,  L, Z) :-
  is_list(A)                                     % if is_list(A), 
  -> Q = [A|R], bfs( s(s(N)), [B|P], R,  L, Z)   % then      enqueue A, 
  ;  L = [A|R], bfs(   s(N),  [B|P], Q,  R, Z).  % otherwise produce A

第一个参数是队列上读写点之间的距离。当他们遇到队列已经筋疲力尽时,我们停下来。输入队列和输出列表都被维护为头尾变量的差异列表对。

试一试:

12 ?- bfs( [[[6]],1,2,[4,[[[[[7]]]]],5],3], A).
A = [1, 2, 3, 4, 5, 6, 7] .

您需要对其进行扩充以跳过顶层的非列表。

【讨论】:

  • 这真的很紧凑。使用 Peano 数字而不是整数还允许直接在头部执行匹配以及内联减法和加法。这是一个非常好的主意。从来没想过。否定的is_list/1 可以变成肯定的,对吧?
  • 一开始有简单的整数。希望它会成为确定性,与 Peano ......但无论如何它都会缩短代码。是的,我们可以翻转分支的顺序。
【解决方案2】:

一种使用“开放列表”来附加在遍历列表列表时遇到的元素的解决方案,列表本质上是一棵树,以前缀方式。

指示的示例表明深度为 0 的非列表元素应被丢弃,其他元素按深度排序。但是,没有给出确切的规格。

确实,人们会期待扁平化的结果

[[[[a]],b,[c]],d,e,[f]]

成为

[b,f,c,a]

通过深度值对的“按深度排序”对列表:

[3-a,1-b,2-c,1-f]

但是问题发布者要求这个结果:

[f, a, b, c]

我真的不知道这是否是一个错误。

:- debug(flatwalker).

flatwalker(ListIn,ListOut) :-
   Tip=Fin,                            % 2 unbound variables
   %                                   % designating the same memory
   %                                   % cell. Hold the Tip, grow at Fin.
   flatwalker_2(0,ListIn,Fin,TerFin),  % Elements are appended at Fin.
   %                                   % The final Fin is found in TerFin
   %                                   % on success.
   TerFin=[],                          % Unify TerFin with [], closing
   %                                   % the list at Tip.
   keysort(Tip,Sorted),                % Sort the closed list at Tip
   %                                   % by pair key, i.e. by depth.
   %                                   % keysort/2 is stable and keeps
   %                                   % duplicates.
   debug(flatwalker,"Got : ~q",[Tip]),
   maplist([_-V,V]>>true,Sorted,ListOut). % Remove depth values.

% ---
% flatwalker_2(+Depth,+TreeIn,+Fin,+TerFin)
% Depth:  Input integer, indicates current tree depth.
% TreeIn: The list to flatten at this depth (it's a node of the tree,
%         which may or may not contain subtrees, i.e. lists)
% Fin:    Always an unbound variable denoting the end of an open list to
%         which we will append.
%         ("points to an empty memory cell at the fin of the open list")
%         Works as an accumulator as a new Fin, advanced by 1 cell at each
%         append operation is handed to the next flatwalker_2/4
%         activation.
% TerFin: When flatwalker_2/ is done, the final Fin is unified with 
%         TerFin so that it can be passed to flatwalker/2.
% ---

% We make the guards explicit and cut heavily.
% Optimizing the guards (if so desired) is left as an exercise.

flatwalker_2(_,[],Fin,Fin) :- !.       % Done as TreeIn is empty. 
                                       % Unify Fin with TerFin.

flatwalker_2(0,[X|Xs],Fin,TerFin) :-   % Case of X is nonlist at depth 0:
   %                                   % discard!
   \+is_list(X),!,
   flatwalker_2(0,Xs,Fin,TerFin).      % Continue with the rest of the
                                       % list at this depth.

flatwalker_2(D,[X|Xs],Fin,TerFin) :-   % Case of X is nonlist at
   %                                   % depth > 0: keep!
   D>0,\+is_list(X),!,
   Fin=[D-X|Fin2],                     % Grow the result list at its
   %                                   % Fin by D-X.
   flatwalker_2(D,Xs,Fin2,TerFin).     % Continue with the rest of the
                                       % list at this depth.

flatwalker_2(D,[X|Xs],Fin,TerFin) :-   % Case of X is a list at any
   %                                   % depth.
   is_list(X),!,
   DD is D+1,
   flatwalker_2(DD,X,Fin,Fin2),        % Collect one level down
   flatwalker_2(D,Xs,Fin2,TerFin).     % On return, continue with the 
                                       % rest of the list at this depth.

一些plunit 测试:

:- begin_tests(flatwalker).

test("empty",true(Out == [])) :-
   flatwalker([],Out).

test("simple",true(Out == [])) :-
   flatwalker([1,2,3],Out).

test("with empties",true(Out == [])) :-
   flatwalker([[],1,[],2,[],3,[]],Out).

test("test 1",true(Out == [a, b, c, d, e])) :-
   flatwalker([1,2,[a,b,c],3,4,[d,e]],Out).

test("test 2",true(Out == [a, b])) :-
   flatwalker([1,[],2,3,4,[a,b]],Out).
   
test("test 3",true(Out == [f, a])) :-
   flatwalker([[[[a]]],b,c,d,e,[f]],Out).

test("test 4",true(Out == [f, a, b, c])) :-
   flatwalker([[[[a]],b,[c]],d,e,[f]],Out).
   
:- end_tests(flatwalker).

所以:

?- run_tests.
% PL-Unit: flatwalker 
% Got : []
.
% Got : []
.
% Got : []
.
% Got : [1-a,1-b,1-c,1-d,1-e]
.
% Got : [1-a,1-b]
.
% Got : [3-a,1-f]
.
% Got : [3-a,1-b,2-c,1-f]
ERROR: flatwalker.pl:66:
        test test 4: wrong answer (compared using ==)
ERROR:     Expected: [f,a,b,c]
ERROR:     Got:      [b,f,c,a]
 done
% 1 test failed
% 6 tests passed
false.

【讨论】:

  • 我认为要愉快地注释 Prolog 代码,需要特殊的编辑器支持。评论 Prolog 很重要!
  • 我对与 cmets 中的 OP 交谈有清晰的记忆(在问题的第一个版本之后,当显示的示例列表只有一个级别时),她最后告诉我订单并不重要。无论如何,问题中提出的顺序是不一致的。但是您认为它实际上是广度优先的想法很有趣。 ----开始阅读您的帖子我以为您进行了正确的广度优先枚举,但是您作弊了! :) 通过使用密钥排序。所以它仍然不是我希望的那个纯粹的版本...... :)
  • @WillNess 听起来像是“迭代深化”的案例。或者......我可以使用延续吗?
  • 只是a queue。 :) (忽略那里的所有 Haskell)(它是关于二叉树的,但我认为总体思路是一样的)顺便说一句,我觉得你的 cmets/代码的态度有点过于迫切;在那次问答中,我也遇到了这个问题,这让我更难找到解决方案,最终这不是最合适的解决方案。 (您可以在那里阅读以了解更多信息。)
  • 哎呀,它是关于从它的 bfs 枚举创建一棵树,即 相反。在答案中也有一个枚举代码,但它在 Haskell 中。 (或者它可能会双向工作?? ....)
【解决方案3】:

这是一个单行,

foo(X,L) :- 
  findall(Z, (member(A,X),is_list(A),member(Z,A)), L).

(如here所见)。

要处理多层嵌套列表,我们需要使用递归谓词,

nembr(Z,A) :-    % member in nested lists
  is_list(A), member(B,A), nembr(Z,B)
  ;
  \+ is_list(A), A=Z.

然后在findall 的目标中使用it 而不是最后的member 调用:

bar(X,L) :- 
  findall(Z, (member(A,X),is_list(A),nembr(Z,A)), L).

测试:

10 ?- foo([1,2,[a,b,c],3,4,[d,e]],X).
X = [a, b, c, d, e].

11 ?- bar([1,2,[a,b,c],3,4,[d,e]],X).
X = [a, b, c, d, e].

12 ?- bar([1,2,[a,b,[[[c]]]],3,4,[d,e]],X).
X = [a, b, c, d, e].

【讨论】:

  • 是的,我的解决方案还有其他用途。我删除了它以考虑是否过夜。
  • @DavidTonhofer 它只是不会跳过顶层的非列表。另一个答案的第一个版本也没有。 :) 顺便说一句,我认为在您的代码中手动操作可能更可取,在这个答案中,有可能不像这种方法那样单向。
  • 谢谢威尔。我稍后会拿起它。需要做现实生活中的事情......
  • 好吧,我搞砸了一些事情。这比预期的要长得多。
【解决方案4】:

如果acclistsFromList/3 的第二个子句已验证H 不是列表,则不会对H 执行任何操作,但您需要在结果前面加上H

acclistsFromList([H|T], Listwithinlist, [H|A]) :-
    \+ is_list(H),
    acclistsFromList(T, Listwithinlist, A).

但这还不够。由于您预先添加到累加器,因此结果是相反的。无论如何,这里不需要累加器:

acclistsFromList([], []).
acclistsFromList([H|T], [H|A]) :-
    \+ is_list(H),
    acclistsFromList(T, A).
acclistsFromList([H|T], Result):-
    is_list(H),
    append(H, Res1, Result),
    acclistsFromList(T, Res1).

或没有“标量”元素:

acclistsFromList([], []).
acclistsFromList([H|T], A) :-
    \+ is_list(H),
    acclistsFromList(T, A).
acclistsFromList([H|T], Result):-
    is_list(H),
    append(H, Res1, Result),
    acclistsFromList(T, Res1).

这进一步在列表上递归。列表列表的列表因此不会被展平。我把它作为一个练习来实现它。

【讨论】:

    猜你喜欢
    • 2021-03-03
    • 2014-07-19
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    • 2011-08-30
    • 2015-06-30
    相关资源
    最近更新 更多