【问题标题】:Prolog - How to remove N number of members from a listProlog - 如何从列表中删除 N 个成员
【发布时间】:2017-10-10 21:10:05
【问题描述】:

所以我创建了一个谓词 removeN(List1, N, List2)。它基本上应该是这样的:

removeN([o, o, o, o], 3, List2).

List2 = [o].

第一个参数是一个包含多个相同成员的列表([o, o, o] 或 [x, x, x])。第二个参数是要删除的成员数,第三个参数是已删除成员的列表。

我应该怎么做,我正在考虑使用某种长度。

提前致谢。

【问题讨论】:

  • 如果列表成员不同怎么办?
  • 它应该会失败,但这不是很重要,因为提供的列表将始终是 O 的列表。

标签: list prolog


【解决方案1】:

另一种方法是使用append/3length/2

remove_n(List, N, ShorterList) :-
    length(Prefix, N),
    append(Prefix, ShorterList, List).

【讨论】:

    【解决方案2】:

    想想谓词应该描述什么。它是列表、数字和等于第一个或缺少指定数量的第一个元素的列表之间的关系。让我们为它取一个描述性的名称,比如 list_n_removed/3。由于您希望删除许多相同的元素,因此出于比较原因,我们保留列表的头部,因此 list_n_removed/3 只是调用谓词和另一个带有附加参数的谓词,我们称之为 list_n_removed_head/4,描述实际关系:

    list_n_removed([X|Xs],N,R) :-
       list_n_removed_head([X|Xs],N,R,X).
    

    谓词 list_n_removed_head/4 必须处理两种不同的情况:N=0,那么第一个和第三个参数是同一个列表或N>0,那么第一个列表的头部必须等于参考元素(第 4 个参数),并且该关系也必须适用于尾部:

    list_n_removed_head(L,0,L,_X).
    list_n_removed_head([X|Xs],N,R,X) :-
       N>0,
       N0 is N-1,
       list_n_removed_head(Xs,N0,R,X).
    

    现在让我们看看它是如何工作的。您的示例查询产生了所需的结果:

    ?- list_n_removed([o,o,o,o],3,R).
    R = [o] ;
    false.
    

    如果前三个元素不相等,则谓词失败:

    ?- list_n_removed([o,b,o,o],3,R).
    false.
    

    如果列表的长度等于N,则结果为空列表:

    ?- list_n_removed([o,o,o],3,R).
    R = [].
    

    如果列表的长度小于N,则谓词失败:

    ?- list_n_removed([o,o],3,R).
    false.
    

    如果N=0 两个列表相同:

    ?- list_n_removed([o,o,o,o],0,R).
    R = [o, o, o, o] ;
    false.
    

    如果N<0 谓词失败:

    ?- list_n_removed([o,o,o,o],-1,R).
    false.
    

    谓词也可以用在另一个方向:

    ?- list_n_removed(L,0,[o]).
    L = [o] ;
    false.
    
    ?- list_n_removed(L,3,[o]).
    L = [_G275, _G275, _G275, o] ;
    false.
    

    但是,如果第二个参数是可变的:

    ?- list_n_removed([o,o,o,o],N,[o]).
    ERROR: >/2: Arguments are not sufficiently instantiated
    

    这可以通过使用 CLP(FD) 来避免。考虑以下更改:

    :- use_module(library(clpfd)).              % <- new
    
    list_n_removed([X|Xs],N,R) :-
       list_n_removed_head([X|Xs],N,R,X).
    
    list_n_removed_head(L,0,L,_X).
    list_n_removed_head([X|Xs],N,R,X) :-
       N #> 0,                                  % <- change
       N0 #= N-1,                               % <- change
       list_n_removed_head(Xs,N0,R,X).
    

    现在上面的查询提供了预期的结果:

    ?- list_n_removed([o,o,o,o],N,[o]).
    N = 3 ;
    false.
    

    与最一般的查询一样:

    ?- list_n_removed(L,N,R).
    L = R, R = [_G653|_G654],
    N = 0 ;
    L = [_G653|R],
    N = 1 ;
    L = [_G26, _G26|R],
    N = 2 ;
    L = [_G26, _G26, _G26|R],
    N = 3 ;
    .
    .
    .
    

    上面的其他查询与 CLP(FD) 版本产生相同的答案。

    【讨论】:

      【解决方案3】:

      使用foldl/4的替代解决方案:

      remove_step(N, _Item, Idx:Tail, IdxPlusOne:Tail) :-
          Idx < N, succ(Idx, IdxPlusOne).
      remove_step(N, Item, Idx:Tail, IdxPlusOne:NewTail) :-
          Idx >= N, succ(Idx, IdxPlusOne),
          Tail = [Item|NewTail].
      
      remove_n(List1, N, List2) :-
          foldl(remove_step(N), List1, 0:List2, _:[]).
      

      这里的想法是在跟踪当前元素的索引时遍历列表。虽然元素索引低于指定的数字 N,但我们基本上什么都不做。在 index 等于 N 之后,我们开始通过附加源列表中的所有剩余元素来构建输出列表。

      效果不佳,但您可能仍对该解决方案感兴趣,因为它展示了一个非常强大的 foldl 谓词的用法,可用于解决范围广泛的列表处理问题。

      【讨论】:

        【解决方案4】:

        倒计时应该可以正常工作

        removeN([],K,[]) :- K>=0.
        removeN(X,0,X).
        removeN([_|R],K,Y) :- K2 is K-1, removeN(R,K2,Y).
        

        【讨论】:

        • 我建议使用succ(K2, K1) 而不是is/2,因为它在两个方向上都成功。不过在这里可能并不重要。
        【解决方案5】:

        这对我有用。 我认为这是最简单的方法。 trim(L,N,L2)L 是列表,N 是元素数量。

        trim(_,0,[]).
        trim([H|T],N,[H|T1]):-N1 is N-1,trim(T,N1,T1).
        

        【讨论】:

        • 您正在收集前 N 个元素,而不是删除它们。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多