Prolog: First duplicate value

前端 未结 4 1803
挽巷
挽巷 2020-11-30 15:30

I need to find the first duplicate value in a list.

prep(3,[1,3,5,3,5]). Should be true.

prep(5,[1,3,5,3,5]). Should be false.

4条回答
  •  眼角桃花
    2020-11-30 15:37

    In this answer we improve the logically pure code presented in this earlier answer. Step-by-step:

    1. We combine two predicates memberd/2 and non_member/2 to one, memberd_t/3, which uses an additional argument for reifying list membership into a truth-value (true or false).

      memberd_t/3 is equivalent to memberd/2 + non_member/2, so we could define it like this:

      memberd_t(X,Xs,true) :-
         memberd(X,Xs).
      memberd_t(X,Xs,false) :-
         non_member(X,Xs).
      

      Or, vice versa, we could define memberd/2 and non_member/2 like so:

      memberd(X,Xs) :-
         memberd_t(X,Xs,true).
      
      non_member(X,Xs) :-
         memberd_t(X,Xs,false).
      

      In practise, we use a tuned implementation of memberd_t/3—one with better determinism.

      Let's see that memberd_t/3 in fact covers both memberd/2 and non_member/2!

      ?- memberd_t(X,[1,2,3],T).
        T = true ,     X=1
      ; T = true ,               X=2
      ; T = true ,                         X=3
      ; T = false, dif(X,1), dif(X,2), dif(X,3).
      
    2. Take the following variant of predicate firstdup/2 (defined earlier) as our starting point:

      firstdup(E,[X|Xs]) :-
         (  memberd(X,Xs),
            E=X      
         ;  non_member(X,Xs),
            firstdup(E,Xs)
         ).
      
    3. Let's replace the use of memberd/2 and non_member/2 with memberd_t/3!

      firstdup(E,[X|Xs]) :-
         (  memberd_t(X,Xs,true),
            E=X
         ;  memberd_t(X,Xs,false),
            firstdup(E,Xs)
         ).
      
    4. Let's hoist memberd_t/3!

      firstdup(E,[X|Xs]) :-
         memberd_t(X,Xs,T),
         (  T=true
         -> E=X      
         ;  T=false,
            firstdup(E,Xs)
         ).
      
    5. Above pattern pred_t(OtherArgs,T), (T = true -> Then ; T = false, Else) can be expressed more concisely using if_/3, writing if_(pred_t(OtherArgs),Then,Else).

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

    Let's run some queries!

    ?- firstdup(1,[1,2,3,1]).
    true.                                % succeeds deterministically
    
    ?- firstdup(X,[1,2,3,1]).
    X = 1.                               % succeeds deterministically
    
    ?- firstdup(X,[A,B,C]).              % succeeds, leaving behind a choicepoint
          A=X ,     B=X                  % ... to preserve the full solution set.
    ;     A=X , dif(B,X), C=X
    ; dif(A,X),     B=X , C=X
    ; false.
    

提交回复
热议问题