【问题标题】:Best way to remove the first elements from each list in a list of lists in Prolog?从Prolog列表中的每个列表中删除第一个元素的最佳方法?
【发布时间】:2017-10-06 06:26:10
【问题描述】:

我正在尝试删除列表列表中每个列表的第一个元素。 例如,要列出 [[1,2],[3,4]],我应该返回 [[2],[4]]。 在大多数情况下,下面的代码可以正常工作:

remove_firstElem([],[]). 
remove_firstElem([[_H|T]|Ls],[T|L]) :-
    remove_firstElem(Ls,L).

但是对于像 [[1],[2]] 这样的列表,我希望它返回 [] 而不是 [[],[]]。

到目前为止我尝试过的看起来是这样的:

remove_firstElem([_H|Ls],L) :-
    length(_H,1),
    remove_firstElem(Ls,L).

但它返回[ ],[[ ]],[[ ]],[[ ],[ ]],我真的不知道它有什么问题。

谁能帮我解决它?感谢您的帮助!

【问题讨论】:

  • 如果源中有一个空列表怎么办。喜欢remove_firstElem([[]],L)
  • 到[[]]应该返回[],返回的列表中应该没有空列表。

标签: list data-structures prolog


【解决方案1】:

如果我理解正确,您想弹出列表的头部,但如果列表仅包含一个元素(或根本不包含),则应删除该列表。

我们可以检查子列表是否包含至少两个具有模式的元素:

pop_lists([[_,H2|T]|TA],[[H2|T]|TB]) :-
    pop_lists(TA,TB).

所以这里我们有一个模式[_,H2|T] 用于第一个列表。 _ 与第一个元素绑定,H2 与第二个元素绑定,其余元素与尾部绑定。

不能与该模式统一的列表是空列表,或只有一个元素的列表。所以在这种情况下,我们只是忽略它们:

pop_lists([[]|TA],TB) :-
    pop_lists(TA,TB).
pop_lists([[_]|TA],TB) :-
    pop_lists(TA,TB).

如果我们到达列表的末尾,当然我们也将过滤器与空列表统一起来:

pop_list([],[]).

我们最好将此子句放在第一行,以使我们的谓词更具多向性。所以总的来说,我们有以下解决方案:

pop_list([],[]).
pop_list([[_,H2|T]|TA],[[H2|T]|TB]) :-
    pop_list(TA,TB).
pop_list([[]|TA],TB) :-
    pop_list(TA,TB).
pop_list([[_]|TA],TB) :-
    pop_list(TA,TB).

我们可以进一步重新排序语句,使得回溯的量更少:

pop_list([],[]).
pop_list([[]|TA],TB) :-
    pop_list(TA,TB).
pop_list([[_]|TA],TB) :-
    pop_list(TA,TB).
pop_list([[_,H2|T]|TA],[[H2|T]|TB]) :-
    pop_list(TA,TB).

【讨论】:

  • 谢谢,真是一个聪明而清晰的解决方案。但是它返回 [ ] 和 false 来列出例如 [ [1],[2] ],有什么办法可以避免这种错误?
  • @Fallin:实际上是一样的。 false 只是因为 Prolog 需要尝试其他子句(以寻找另一个答案)。既然失败了,它就说错了。所以基本上它说:“答案是[],没有其他答案
  • @Fallin:通过重新排序子句,我们可能可以避免它:)
  • @Fallin 您可能可以使用来自library(reif) 的几个if_/3 来摆脱它们。
【解决方案2】:

更简单的方法:

list_tail([_|Es], Es).
maplist(list_tail, Input, Output).

【讨论】: