【问题标题】:Prolog - remove the non unique elementsProlog - 删除非唯一元素
【发布时间】:2017-06-05 01:13:35
【问题描述】:

我有一个谓词来检查元素是否是列表的成员,并且看起来如下:

member(X,[X|_]).
member(X,[_|T]) :- member(X,T).

当我打电话时:?- member(1,[2,3,1,4]) 我明白了:是的。

现在我必须使用它来编写谓词,该谓词将从列表中删除所有非唯一元素,如下所示:

remove([[a,m,t,a],[k,a,w],[i,k,b,b],[z,m,m,c]],X).
X = [[t],[w],[i,b,b],[z,c]]

我该怎么做?

【问题讨论】:

  • 为什么是X = [[t],[k,w],[i,b,b],[z,c]]k[[a,m,t,a],[k,a,w],[i,k,b,b],[z,m,m,c]] 中出现两次,b 在解决方案中出现两次。这没有多大意义。
  • 对不起,是我的错
  • @lurker: b,b 在这里是因为它只出现在一个子列表中——大概
  • @false,好的,但是根据描述和示例,我不清楚 OP 的确切含义。

标签: prolog


【解决方案1】:

使用library(reif) SICStus|SWI:

lists_uniques(Xss, Yss) :-
   maplist(tfilter(in_unique_t(Xss)), Xss, Yss).

in_unique_t(Xss, E, T) :-
   tfilter(memberd_t(E), Xss, [_|Rs]),
   =(Rs, [], T).

请注意,虽然没有限制如何命名谓词,但非关系的命令式名称通常会隐藏纯粹的关系。 remove 是一个真正的命令,但我们只想要一个关系。列表列表与仅包含唯一元素的列表列表之间的关系。

示例用法:

?- lists_uniques([[X,b],[b]], [[X],[]]).
dif(X, b).

所以在这种情况下,我们留下了X 一个未实例化的变量。因此,Prolog 会计算出最一般的答案,找出X 的样子。

(请注意,在这种情况下,您接受的错误答案会失败)

【讨论】:

  • 当然有更高效的版本:特别是,更多元素的计数太多了。
  • 谢谢,但我不能使用任何库,因为这是我大学的任务。
  • 将库复制到您的解决方案中!
  • @user:请看添加的例子!
【解决方案2】:

按照您的示例和@false 的评论,实际问题似乎是从每个子列表中删除任何其他子列表中出现的元素。我很难将其概念化为文字,这导致我构建了一段我认为非常混乱和粗暴的代码。

所以首先我想要一个小辅助谓词来将member/2 移动到子列表列表。

in_sublist(X, [Sublist|_])  :- member(X, Sublist).
in_sublist(X, [_|Sublists]) :- in_sublist(X, Sublists).

这不是一件伟大的工作,事实上我觉得它应该以某种方式内联,因为我只是看不到自己想要单独使用它。

现在,我最初的解决方案不正确,看起来像这样:

remove([Sub1|Subs], [Res1|Result]) :-
    findall(X, (member(X, Sub1), \+ in_sublist(X, Subs)), Res1),
    remove(Subs, Result).
remove([], []).

你可以在这里看到我想要的主题类型:让我们使用findall/3 来枚举这里的子列表的元素,然后我们可以过滤掉其他列表中出现的元素。这并不完全奏效,输出看起来像这样。

?- remove([[a,m,t,a],[k,a,w],[i,k,b,b],[z,m,m,c]], R).
R = [[t], [a, w], [i, k, b, b], [z, m, m, c]].

所以,它开始使用[t] 看起来不错,但随后使用[a,w] 丢失了绘图,因为当我们进行第一个递归调用时,输入[a,m,t,a] 不可见。我们有几种方法可以处理它;一个聪明的方法可能是形成一种拉链,我们将列表的前面元素和后面的元素放在一起。另一种方法是在递归调用之前从所有后续列表中删除此列表中的元素。我选择了一个“更简单”的解决方案,它更混乱、更难阅读,但花费的时间更少。我强烈建议您研究其他选项以提高可读性。

remove(In, Out) :- remove(In, Out, []).
remove([Sub1|Subs], [Res1|Result], Seen) :-
    findall(X, (member(X, Sub1),
                \+ member(X, Seen),
                \+ in_sublist(X, Subs)), Res1),
    append(Sub1, Seen, Seen1),
    remove(Subs, Result, Seen1).
remove([], [], _).

所以基本上现在我保留了一个“已看到”的列表。就在递归调用之前,我将目前看到的东西和这个列表的元素拼接在一起。这不是特别有效,但似乎可以完成工作:

?- remove([[a,m,t,a],[k,a,w],[i,k,b,b],[z,m,m,c]], R).
R = [[t], [w], [i, b, b], [z, c]].

这让我觉得这是一个非常讨厌的问题。老实说,我很惊讶它有多讨厌。我希望其他人能找到更好的解决方案,阅读效果更好。

要研究的另一件事是 DCG,它有助于执行此类列表处理任务。

【讨论】:

  • 非常感谢,但是\+是什么意思?
  • 而且谓词 findall 太高级了。我必须只使用我的谓词。
  • \+ 表示否定。
  • “把《独立宣言》倒着打字,戴上手铐,绑在椅子上,证明你知道。”不,谢谢。 :)
猜你喜欢
  • 2014-03-25
  • 1970-01-01
  • 2011-05-23
  • 2015-01-08
  • 1970-01-01
  • 2020-08-08
  • 1970-01-01
  • 2018-11-05
  • 2016-02-12
相关资源
最近更新 更多