【问题标题】:Prolog - deleting pairs with the same first value from listProlog - 从列表中删除具有相同第一个值的对
【发布时间】:2020-04-08 03:16:08
【问题描述】:

我有这样的对象列表

list([obj(x,y),obj(x,z),obj(a,b),obj(b,c)]).

我想删除那些共享相同第一个值的元素,这样我就可以使用修改后的列表。在这种情况下,最终列表将如下所示

list([obj(a,b),obj(b,c)]

有人可以帮忙吗?我真的很努力解决这个问题。

【问题讨论】:

  • Soo ...保留索引最低的对象?
  • 检查this。 :)

标签: list prolog


【解决方案1】:

有效地解决这个问题对于初学者来说并非易事。假设列表的元素是接地的,我们可以首先注意到对列表进行排序会将共享obj/2 复合术语中的第一个参数的所有元素聚集在一起。例如:

| ?- sort([obj(x,y),obj(x,z),obj(a,b),obj(b,c)], S).
S = [obj(a, b), obj(b, c), obj(x, y), obj(x, z)]
yes

sort/2 是标准的内置谓词。任何体面的 Prolog 系统都应该以 O(n*log(n)) 的复杂度来实现它。排序后,我们可以遍历列表,我们可以在 O(n) 中进行过滤:

filter(List, Filtered) :-
    sort(List, Sorted),
    walk(Sorted, Filtered).

walk([], []).
walk([obj(X,Y)| Sorted], Filtered) :-
    walk(Sorted, X, obj(X,Y), Filtered).

walk([], _, Element, [Element]).
walk([obj(X,_)| Sorted], X, _, Filtered) :-
    !,
    delete(Sorted, X, Rest),
    walk(Rest, Filtered).
walk([obj(X,Y)| Sorted], _, Element, [Element| Filtered]) :-
    walk(Sorted, X, obj(X,Y), Filtered).

delete([], _, []).
delete([obj(X,_)| Sorted], X, Rest) :-
    !,
    delete(Sorted, X, Rest).
delete(Rest, _, Rest).

示例调用:

| ?- filter([obj(x,y),obj(x,z),obj(a,b),obj(b,c)], Filtered).
Filtered = [obj(a, b), obj(b, c)]
yes

看起来不错,但我们应该进行更全面的测试。我们可以定义一个所有filter/2 谓词解决方案必须满足的属性:

property(List, Filtered) :-
    filter(List, Filtered),
    % all elements of the output list must
    % be in input list
    forall(
        member(X, Filtered),
        member(X, List)
    ),
    % no two elements in the output list
    % should share the first argument
    \+ (
        select(obj(X,_), Filtered, Rest),
        member(obj(X,_), Rest)
    ),
    % all elements in the input list whose
    % first argument is not repeated must
    % be in the output list
    \+ (
        select(obj(X,Y), List, Rest),
        \+ member(obj(X,_), Rest),
        \+ member(obj(X,Y), Filtered)
    ).

我们现在可以使用 基于属性的测试 实现,例如 Logtalk 的 lgtunit QuickCheck 实现。但有一个问题。基于属性的测试要求我们能够生成带有obj/2 元素的列表。解决办法,我们作弊!首先,我们进行从obj(X,Y)X-Y句法 转换。这种转换不会改变被测试谓词的语义:

filter(List, Filtered) :-
    sort(List, Sorted),
    walk(Sorted, Filtered).

walk([], []).
walk([X-Y| Sorted], Filtered) :-
    walk(Sorted, X, X-Y, Filtered).

walk([], _, Element, [Element]).
walk([X-_| Sorted], X, _, Filtered) :-
    !,
    delete(Sorted, X, Rest),
    walk(Rest, Filtered).
walk([X-Y| Sorted], _, Element, [Element| Filtered]) :-
    walk(Sorted, X, X-Y, Filtered).

delete([], _, []).
delete([X-_| Sorted], X, Rest) :-
    !,
    delete(Sorted, X, Rest).
delete(Rest, _, Rest).

我们对property/2 谓词应用相同的句法转换:

property(List, Filtered) :-
    filter(List, Filtered),
    % all elements of the output list must
    % be in input list
    forall(
        member(X, Filtered),
        member(X, List)
    ),
    % no two elements in the output list
    % should share the first argument
    \+ (
        select(X-_, Filtered, Rest),
        member(X-_, Rest)
    ),
    % all elements in the input list whose
    % first argument is not repeated must
    % be in the output list
    \+ (
        select(X-Y, List, Rest),
        \+ member(X-_, Rest),
        \+ member(X-Y, Filtered)
    ).

我们现在可以使用目标进行测试:

| ?- lgtunit::quick_check(
         property(
             +list(pair(char,char)),
             -list(pair(char,char))
         )
     ).
% 100 random tests passed
% starting seed: seed(25256,26643,1563)
yes

注意:在property/2 谓词的定义中,我们假设事实上的标准member/2select/3 列表谓词在user 中可用(即在顶级解释器中)。如果不是这种情况,请在他们的电话前加上list::

【讨论】:

【解决方案2】:

让我们从测试开始吧!

% Testing

:- begin_tests(collapse).   

test(one)   :- collapse([],[]).
test(two)   :- collapse([obj(a,b)],[obj(a,b)]).
test(three) :- collapse([obj(a,b),obj(b,c)],
                        [obj(a,b),obj(b,c)]).                        
test(four)  :- collapse([obj(a,b),obj(a,c),obj(b,j)],
                        [obj(b,j)]).
test(five)  :- collapse([obj(a,b),obj(a,c),obj(b,j),obj(a,x),obj(b,y)],
                        []).
test(six)   :- collapse([obj(a,b),obj(a,c),obj(b,j),obj(b,y),obj(c,x)],
                        [obj(c,x)]).

:- end_tests(collapse).

rt :- run_tests(collapse).

然后代码:

% This is called

collapse(Lin,Lout) :- collapse(Lin,[],Lout).

/*
 * Helper predicate:
 * collapse(List_over_which_we_recur_getting_smaller,
 *          Elements_which_we_have_already_seen,
 *          List_which_collects_the_result_going_down,
 *          List_which_collects_the_result_coming_up).
 */

collapse([],_Filter,[]).  % base case, kick a [] upwards; don't care about Filter

collapse([obj(A,_)|Objs],Filter,Lup) :- 
   (member(obj(A,_),Objs);member(obj(A,_),Filter)),     % Does the obj(A,_) appear elsewhere (in Filter or Objs)?
   !,                                                   % Commit to this execution path where obj(A,_) is not unique
   (member(obj(A,_),Filter)                             % Slight improvement: add obj(A,_) to "Filter" only it it's not yet in there
       -> NewFilter = Filter
       ;  NewFilter = [obj(A,_)|Filter]),
   collapse(Objs,NewFilter,Lup).                        % Do not retain obj(A,_)

collapse([obj(A,X)|Objs],Filter,Lup) :- 
   \+(member(obj(A,_),Objs);member(obj(A,_),Filter)),   % Does the obj(A,_) appear elsewhere (in Seen or ToSee)?
   !,                                                   % Commit to this execution path where obj(A,_) IS unique   
   collapse(Objs,Filter,Ltmp),                          % Filtering the rest of Objs, which defines Ltmp      
   Lup = [obj(A,X)|Ltmp].                               % DO retain object on the way up, correctly ordering result.

好的,那么:

?- rt.
% PL-Unit: collapse ...... done
% All 6 tests passed
true.

【讨论】:

  • Coding Guidelines for Prolog 说应该避免右侧的 cmets,但它们确实很有用;事实上,他们应该有专门的编辑器支持。
  • Scheme/Lisp 编码风格指南要求在代码行内没有额外的间距(Prolog 可能也是如此),但代码是 2D 中等,和vertical alignment helps,有时非常大。 :) (包括此处仅用于说明的链接。)
猜你喜欢
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
  • 2022-07-27
  • 1970-01-01
  • 2013-03-17
  • 2012-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多