【发布时间】:2012-05-02 21:23:55
【问题描述】:
我需要在列表中找到第一个重复值。
prep(3,[1,3,5,3,5]). 应该是真的。
prep(5,[1,3,5,3,5]). 应该是假的。
我想检查当前值和以前的列表成员是否相等,直到找到重复项,如果找到一个,它将测试与 X 是否相等,但我不知道如何在 Prolog 中做到这一点!
感谢您的帮助!谢谢
【问题讨论】:
标签: list prolog prolog-dif
我需要在列表中找到第一个重复值。
prep(3,[1,3,5,3,5]). 应该是真的。
prep(5,[1,3,5,3,5]). 应该是假的。
我想检查当前值和以前的列表成员是否相等,直到找到重复项,如果找到一个,它将测试与 X 是否相等,但我不知道如何在 Prolog 中做到这一点!
感谢您的帮助!谢谢
【问题讨论】:
标签: list prolog prolog-dif
这是一个使用dif/2 的纯版本,它实现了合理的不平等。 dif/2 由 B-Prolog、YAP-Prolog、SICStus-Prolog 和 SWI-Prolog 提供。
优点是它也可以用于更一般的查询:
?- firstdup(E, [A,B,C]). E = A,A = B; E = A,A = C; E = C, B = C, 差异(A,C); 错误的。在这里,我们得到三个答案:A 是前两个答案的重复项,但基于两个不同的理由:A 可能等于 B 或 C。在第三个答案中,B 是重复的,但只有在 C 与 A 不同时才会重复。
要理解定义,请阅读:- 作为箭头 ← 所以右边是你知道的,左边是你得出的结论。在开始的时候读那个方向的谓词通常会有点烦人,毕竟你可能很想遵循“执行线程”。但通常这条线索无处可去——它变得太复杂而无法理解。
第一条规则如下:
如果E是列表L的一个元素,我们得出结论[E|L]具有E作为第一个重复项。
第二条规则如下:
假设E是L的第一个重复项(不要惊慌,说我们不知道...)并且假设N不是L的元素,我们得出结论:[N|L]具有E作为第一个副本。
member/2 谓词在许多 Prolog 系统中都提供,non_member(X,L) 可以定义为maplist(dif(X),L)。因此,人们会将firstdup/2 定义为:
【讨论】:
nonmember(X,L) can be defined as maplist(dif(X),L). ...”可能会更好,给X和@提供上下文987654348@那里。 :)
在这个答案中,我们改进了this earlier answer 中呈现的逻辑纯代码。一步一步:
我们将两个谓词memberd/2 和non_member/2 组合成一个memberd_t/3,它使用附加参数将列表成员关系具体化为真值(true 或false)。
memberd_t/3 等价于memberd/2 + non_member/2,所以我们可以这样定义它:
或者,反之亦然,我们可以像这样定义memberd/2和non_member/2:
在实践中,我们使用memberd_t/3 的优化实现——具有更好的确定性。
让我们看看memberd_t/3 实际上涵盖了memberd/2 和 non_member/2!
以谓词firstdup/2 (defined earlier) 的以下变体为出发点:
让我们将memberd/2 和non_member/2 的使用替换为memberd_t/3!
让我们吊起来memberd_t/3!
上面的模式pred_t(OtherArgs,T), (T = true -> Then ; T = false, Else)可以用if_/3更简洁地表达,写作if_(pred_t(OtherArgs),Then,Else)。
让我们运行一些查询!
?- firstdup(1,[1,2,3,1])。 真的。 % 确定性地成功 ?- firstdup(X,[1,2,3,1])。 X = 1。 % 确定性地成功 ?- firstdup(X,[A,B,C])。 % 成功,留下一个选择点 A=X , B=X % ... 以保留完整的解决方案集。 ; A=X , 差异 (B,X), C=X ;差异(A,X),B=X,C=X ;错误的。【讨论】:
rep(N,List) :- append(L1,[N|_],List),append(_,[N|_],L1),\+(rep(_,L1))。
【讨论】:
不确定这是否是家庭作业/您可以使用哪些谓词有限制,但这会让 prolog 为您进行递归。
它说.. 找到所有重复项,即。其中有一个具有列表索引 I1 的项目,而另一个具有 I2 的项目,使得它们都具有相同的值 N,并且索引不相同,即不要将相同的列表项目视为重复项。
这些重复项被放置在列表 AllDups 中(按照找到的顺序,从头开始至关重要),如果找到的第一个重复项与您正在检查的值 M 结合,则谓词为真。
第一次尝试:这可行,但效率很低,建立一个所有重复项的列表
prep(M, List) :-
findall(N,
(nth0(I1, List, N),
nth0(I2, List, N),
not(I1 =:= I2)),
AllDups),
nth1(1, AllDups, M).
?- prep(3,[1,3,5,3,5]).
true.
?- prep(5,[1,3,5,3,5]).
false.
即使您不被允许使用 findall,它也可以帮助您了解如何“手动”进行操作。
第二次尝试:这不起作用/回溯太远导致误报
您甚至可以不使用 findall 来执行此操作 - 使用 nth0 查找第一个重复项,然后如果它与您正在检查的值一致,则返回 true,否则返回 false。
prep(N, List) :-
nth0(I1, List, N),
nth0(I2, List, N),
not(I1 =:= I2).
第三次尝试:这可行,并在找到第一个重复项后立即返回
prep(M, List) :-
nth0(I1, List, N),
nth0(I2, List, N),
not(I1 =:= I2), !,
M == N.
【讨论】:
prep(5,[1,3,5,3,5]). 也会返回 true。 ?- prep(N,[1,3,5,3,5]). N = 3 ; N = 5 ; N = 3 ; N = 5 ; No.