从列表中删除对,仅当它存在时

时间:2014-02-22 16:52:07

标签: list prolog

我正在尝试创建一个带有列表对的谓词,如果它在列表中找到了键,它将从列表中删除该项并返回其余项。但是,如果给定的密钥不存在,它还需要返回完整列表。

unmap(K, M1, M2):-
    select(E, M1, MM1),
    select(E, [(K, _)], K1),
    unmap(MM1, K1, M2).
unmap(X, _, X).

跟:

unmap(key1, [(key1, value1),(key2, value2),(key3, value3)], R).

结果:

R = [(key2, value2), (key3, value3)]

工作,但这是一个问题。我试图让它返回相同的列表,如果key1不存在则给出。这是它的回报:

通话:

unmap(key4, [(key1, value1),(key2, value2),(key3, value3)], R).

返回:

R = key4

我认为这与我的终止规则有关,但我不确定如何修复它。非常感谢所有可以提供帮助的人。

3 个答案:

答案 0 :(得分:2)

当然,你可以用做到!以下是......

让我们调用实际关系unmap/3。这是一个更强烈的描述性名称。 pairs_key_unmapped/3只是unmap(Key,Ps0,Ps) :- pairs_key_unmapped(Ps0,Key,Ps). 的包装器:

pairs_key_unmapped/3

if_/3的实施基于谓词=/3equal_truth/3(又名pairs_key_unmapped([],_,[]). pairs_key_unmapped([P|Ps],K,Us) :- P = (K0,_), if_(K0=K, Ps=Us, (Us=[P|Us0],pairs_key_unmapped(Ps,K,Us0))). ),由answer中的@false定义为& #34; Prolog union for A U B U C":

Key

让我们运行一些查询!

?- unmap(key1,[(key1,value1),(key2,value2),(key3,value3)],Ps).
Ps = [(key2,value2),(key3,value3)].                % succeeds deterministically

?- unmap(key4,[(key1,value1),(key2,value2),(key3,value3)],Ps).
Ps = [(key1,value1),(key2,value2),(key3,value3)].  % succeeds deterministically

让我们尝尝不同的事情......如果在Ps0 Ps0发生两次会怎样?

?- unmap(key1,[(key1,x),(key1,y)],Ps).   % only the 1st occurrence is removed
Ps = [(key1,y)].                         % succeeds deterministically

如果Ps 未知,但{{1}} 已知,该怎么办?

?- unmap(key4,Ps0,[(key1,value1),(key2,value2),(key3,value3)]).
Ps0 = [(key4,_A),    (key1,value1),(key2,value2),(key3,value3)] ;
Ps0 = [(key1,value1),(key4,_A),    (key2,value2),(key3,value3)] ;
Ps0 = [(key1,value1),(key2,value2),(key4,_A),    (key3,value3)] ;
Ps0 = [(key1,value1),(key2,value2),(key3,value3)              ] ;
Ps0 = [(key1,value1),(key2,value2),(key3,value3),(key4,_A)    ] ;
false.

更通用的内容怎么样?

?- unmap(Key,Ps0,[_,_]).
Ps0 = [(Key,_A),_B,       _C      ]                           ;
Ps0 = [(_A,_B), (Key,_C), _D      ], dif(_A,Key)              ;
Ps0 = [(_A,_B), (_C,_D)           ], dif(_A,Key), dif(_C,Key) ;
Ps0 = [(_A,_B), (_C,_D),  (Key,_E)], dif(_A,Key), dif(_C,Key) ;
false.

最常见的查询给出了什么答案?

?- unmap(Key,Ps0,Ps).
Ps0 = [],                    Ps = []                             ;
Ps0 = [(Key,_A)|Ps]                                              ;
Ps0 = [(_A,_B)],             Ps = [(_A,_B)],         dif(_A,Key) ;
Ps0 = [(_A,_B),(Key,_C)|_Z], Ps = [(_A,_B)|_Z],      dif(_A,Key) ;
Ps0 = [(_A,_B),(_C,_D)],     Ps = [(_A,_B),(_C,_D)], dif(_A,Key), dif(_C,Key) ...

答案 1 :(得分:1)

问题在于您的基本情况:

unmap(X, _, X).

如果你的主谓词失败(找不到键),它将恢复为基本情况,它将仅使用键(第一个参数)实例化你的结果(第三个参数)。你的基本情况应该是:

unmap(_, X, X).

将使用原始列表(第二个参数)实例化结果(第三个参数)。

请注意,main子句可以更简单(这可以在GNU或SWI prolog中使用):

unmap(K, M, R):-
    select((K, _), M, M1),
    unmap(K, M1, R), !.

如果第一个子句成功,剪切会阻止回溯到基本案例。

在SWI Prolog中,delete/3谓词对您有利:

unmap(K, M, R) :-
    delete(M, (K,_), R), !.

delete/3在GNU Prolog中更严格,在这种情况下不起作用。

答案 2 :(得分:1)

这不是问题的答案,而是一种更简单的攻击方式,不使用'select'(或任何其他内置谓词),只使用递归。

考虑到输出列表只是与键不匹配的项列表,您需要2个主要子句,并围绕列表进行迭代。密钥与列表头部匹配的密钥,以及不匹配的密钥。

unmap(_, [], []).

% head of the list matches key, do not add K/H to unmatched list (ie remove it)
unmap(K, [(H, _)|Tail], Unmatched) :-
    H == K,
    unmap(K, Tail, Unmatched).

% above rule fails, add H to unmatched list
unmap(K, [H|Tail], [H|Unmatched]) :-
    unmap(K, Tail, Unmatched).


?- unmap(key1, [(key1, value1),(key2, value2),(key3, value3)], R).
R = [ (key2, value2), (key3, value3)] .

?- unmap(key4, [(key1, value1),(key2, value2),(key3, value3)], R).
R = [ (key1, value1), (key2, value2), (key3, value3)] .

因此,如果密钥不存在,它只是迭代添加所有列表项,因此输入和输出列表是相同的。