【问题标题】:Prolog: First duplicate valueProlog:第一个重复值
【发布时间】: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


    【解决方案1】:

    这是一个使用dif/2 的纯版本,它实现了合理的不平等。 dif/2 由 B-Prolog、YAP-Prolog、SICStus-Prolog 和 SWI-Prolog 提供。

    firstdup(E, [E|L]) :- 成员(E,L)。 firstdup(E, [N|L]) :- 非成员(N,L), 第一dup(E,L)。 成员(E,[E|_L])。 成员(E,[_X|L]):- 成员(E,L)。 非成员(_E,[])。 非成员(E,[F|Fs]):- 差异(E,F), 非成员(E,Fs)。

    优点是它也可以用于更一般的查询:

    ?- firstdup(E, [A,B,C]). E = A,A = B; E = A,A = C; E = C, B = C, 差异(A,C); 错误的。

    在这里,我们得到三个答案:A 是前两个答案的重复项,但基于两个不同的理由:A 可能等于 BC。在第三个答案中,B 是重复的,但只有在 CA 不同时才会重复。

    要理解定义,请阅读:- 作为箭头 ← 所以右边是你知道的,左边是你得出的结论。在开始的时候读那个方向的谓词通常会有点烦人,毕竟你可能很想遵循“执行线程”。但通常这条线索无处可去——它变得太复杂而无法理解。

    第一条规则如下:

    如果E 是列表L 的一个元素,我们得出结论[E|L] 具有E 作为第一个重复项。

    第二条规则如下:

    假设EL 的第一个重复项(不要惊慌,说我们不知道...)并且假设N 不是L 的元素,我们得出结论: [N|L] 具有 E 作为第一个副本。

    member/2 谓词在许多 Prolog 系统中都提供,non_member(X,L) 可以定义为maplist(dif(X),L)。因此,人们会将firstdup/2 定义为:

    firstdup(E, [E|L]) :- 成员(E,L)。 firstdup(E, [N|L]) :- 地图列表(差异(N),L), 第一dup(E,L)。

    【讨论】:

    • 如果文本的最后一行写成“... and nonmember(X,L) can be defined as maplist(dif(X),L). ...”可能会更好,给X和@提供上下文987654348@那里。 :)
    【解决方案2】:

    在这个答案中,我们改进了this earlier answer 中呈现的逻辑纯代码。一步一步:

    1. 我们将两个谓词memberd/2non_member/2 组合成一个memberd_t/3,它使用附加参数将列表成员关系具体化为真值(truefalse)。

      memberd_t/3 等价于memberd/2 + non_member/2,所以我们可以这样定义它:

      memberd_t(X,Xs,true) :- 成员(X,Xs)。 memberd_t(X,Xs,false) :- 非成员(X,Xs)。

      或者,反之亦然,我们可以像这样定义memberd/2non_member/2

      成员(X,Xs):- memberd_t(X,Xs,true)。 非成员(X,Xs):- memberd_t(X,Xs,false)。

      在实践中,我们使用memberd_t/3 的优化实现——具有更好的确定性。

      让我们看看memberd_t/3 实际上涵盖了memberd/2 non_member/2

      ?- memberd_t(X,[1,2,3],T)。 T = 真,X=1 ; T = 真,X=2 ; T = 真, X=3 ; T = 假,差异(X,1),差异(X,2),差异(X,3)。
    2. 以谓词firstdup/2 (defined earlier) 的以下变体为出发点:

      firstdup(E,[X|Xs]) :- (成员(X,Xs), E=X ;非成员(X,Xs), 第一次备份(E,Xs) )。
    3. 让我们将memberd/2non_member/2 的使用替换为memberd_t/3

      firstdup(E,[X|Xs]) :- ( memberd_t(X,Xs,true), E=X ; memberd_t(X,Xs,false), 第一次备份(E,Xs) )。
    4. 让我们吊起来memberd_t/3

      firstdup(E,[X|Xs]) :- memberd_t(X,Xs,T), (T=真 -> E=X ; T=假, 第一次备份(E,Xs) )。
    5. 上面的模式pred_t(OtherArgs,T), (T = true -> Then ; T = false, Else)可以用if_/3更简洁地表达,写作if_(pred_t(OtherArgs),Then,Else)

      firstdup(E,[X|Xs]) :- if_(memberd_t(X,Xs), E=X, firstdup(E,Xs))。

    让我们运行一些查询!

    ?- 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 ;错误的。

    【讨论】:

      【解决方案3】:

      rep(N,List) :- append(L1,[N|_],List),append(_,[N|_],L1),\+(rep(_,L1))。

      【讨论】:

        【解决方案4】:

        不确定这是否是家庭作业/您可以使用哪些谓词有限制,但这会让 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.
        

        【讨论】:

        • 我仍然是 Prolog 初学者,这对我来说有点难以理解。我们没有在课堂上教授 Prolog,我几乎没有时间自己体面地学习它,这个练习只是自学,所以我稍后会尝试更好地理解它。感谢您的时间! :)
        • 不客气..这是一种奇怪的语言,毫无疑问。它为你做了很多,而不是你必须一直告诉它要做什么,所以你需要了解它可以在幕后做什么才能想出最简洁的解决方案。
        • 最后一次添加时,prep(5,[1,3,5,3,5]). 也会返回 true?- prep(N,[1,3,5,3,5]). N = 3 ; N = 5 ; N = 3 ; N = 5 ; No.
        • @false: I1 和 I2 是列表的索引。即在列表“List”中找到两个具有相同值“N”但索引不同的列表项。
        • @will ness - 是的 - 我以为我测试过了。我在上面的编辑 2 中放入了一个固定版本。我的第一个解决方案效率非常低..首先构建所有重复项的列表。编辑 2 包含一个剪切,以确保在找到具有不同索引的两个匹配值后,如果输入值与找到的值不匹配,它不会回溯到另一个 nth0 值。
        猜你喜欢
        • 2022-01-13
        • 1970-01-01
        • 2019-11-29
        • 2019-11-26
        • 2017-03-04
        • 2013-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多