【问题标题】:Recursive definitions in PrologProlog 中的递归定义
【发布时间】:2018-04-29 17:14:50
【问题描述】:

如何在 Prolog 中编写递归定义以按以下方式创建交替列表:

alternate(K, L, M) 成立,如果列表M 是通过采取 列表中的元素交替出现KL,例如:

?- alternate([1,2,3,4,5,6],[a,b,c],Zs).

Zs = [1, a, 2, b, 3, c, 4, 5, 6]

另外,如果一个列表比另一个长,那么剩下的元素 较长的列表出现在结果的末尾,M

我对这个问题的尝试如下,但它不起作用:

alternate([],L,L).
alternate(K, [], K).
alternate([Firstk|Restk], [Firstl|Restl], M):-
   K is [Firstk|Restk], L is [Firstl|Restl],
   M is [M|[Firstk]], K is Restk,
   M is [M|[Firstl]], L is Restl,
   alternate(K, L, M).

任何提示或建议表示赞赏

【问题讨论】:

    标签: prolog


    【解决方案1】:

    您可以通过翻转递归调用中的前两个参数来描述这种关系,正如 cmets 中的 @WillNess 所建议的那样。由于这两个列表的长度可能不同,因此拥有一个描述列表的谓词会很有帮助,以避免匹配非递归规则中的任意项。最好有一个更具描述性的名称来反映谓词的关系性质。考虑到所有这些,谓词可能看起来像这样:

    islist([]).
    islist([_|T]) :-
       islist(T).
    
    list_list_interlocked([],L,L) :-
       islist(L).
    list_list_interlocked([X|Xs],Ys,[X|Zs]) :-
       list_list_interlocked(Ys,Xs,Zs).
    

    您的示例查询按预期工作:

    ?- list_list_interlocked([1,2,3,4,5,6],[a,b,c],Zs).
    Zs = [1, a, 2, b, 3, c, 4, 5, 6].
    

    如果第二个列表更长,谓词也可以:

    ?- list_list_interlocked([1,2,3],[a,b,c,d,e,f],Zs).
    Zs = [1, a, 2, b, 3, c, d, e, f].
    

    而且它只适用于列表:

    ?- list_list_interlocked(definitelynolist,[],Zs).
    false.
    
    ?- list_list_interlocked([],definitelynolist,Zs).
    false.
    

    后者是您需要islist/1 的原因。如果你像这样定义非递归规则:list_list_interlocked([],L,L).,那么 Prolog 可以将任意术语与L 统一起来,查询将产生不正确的结果:

    ?- list_list_interlocked([],definitelynolist,Zs).
    Zs = definitelynolist.
    

    谓词也可以用于其他方向,例如哪些列表在联锁时产生[1,a,2,b,3,c]

    ?- list_list_interlocked(X,Y,[1,a,2,b,3,c]).
    X = [],
    Y = [1, a, 2, b, 3, c] ;
    X = [1, a, 2, b, 3, c],
    Y = [] ;
    X = [1],
    Y = [a, 2, b, 3, c] ;
    X = [1, 2, b, 3, c],
    Y = [a] ;
    X = [1, 2],
    Y = [a, b, 3, c] ;
    X = [1, 2, 3, c],
    Y = [a, b] ;
    X = [1, 2, 3],
    Y = [a, b, c] ;
    false.
    

    【讨论】:

    • islist([a|nonlist]). 列表?
    • @false:我太草率了,我没有考虑过这种可能性。感谢您指出,我修复了我的代码:-)
    • 我不会称之为草率,因为它可能会以过度概括为代价确保终止。我只是抱怨称它为列表。
    【解决方案2】:

    只需编写两个谓词,一个调用另一个谓词,反之亦然,如下所示:

    alternate1([],[],[]).
    alternate1([],L,L).
    alternate1([H|T],L,[H|T1]):-
        alternate2(T,L,T1).
    alternate2([],[],[]).
    alternate2(L,[],L).
    alternate2(L,[H|T],[H|T1]):-
        alternate1(L,T,T1).
    
    ?- alternate1([1,2,3,4,5,6],[a,b,c],Zs).
    Zs = [1, a, 2, b, 3, c, 4, 5, 6]
    false
    

    【讨论】:

    • 如果在整个定义中始终将第一个和第二个参数切换为alternate2 会怎样?你最终会得到一个等价的谓词。关于这个新谓词会有什么有趣的说法吗?
    【解决方案3】:

    我喜欢其他答案,尤其是@tas 的答案。但是,这种方法仍然感觉太……“小步”。如果您正在处理 两个 列表并且应该从每个列表中选择元素,为什么不同时选择这两个元素而不是只从一个列表中选择并交换列表呢?这感觉像是一种“算法”,而不是对关系的逻辑、声明性描述。

    所以我提出以下建议:

    alternate([], Ys, Ys).
    alternate([X|Xs], [], [X|Xs]).
    alternate([A | As], [B | Bs], [A, B | ABs]) :-
        alternate(As, Bs, ABs).
    

    第三个子句涵盖了交替两个非空列表的一般情况:交替列表的前两个元素是要交替的两个列表的第一个元素。前两个子句只处理一个或另一个列表为空的情况。 (您可以将@tas 的islist 目标添加到这些目标中,以强制另一个参数确实是一个正确的列表。)

    这里是测试:

    ?- alternate([1,2,3,4,5,6],[a,b,c],Zs).
    Zs = [1, a, 2, b, 3, c, 4, 5, 6] ;
    false.
    
    ?- alternate([1,2,3],[a,b,c,d,e,f],Zs).
    Zs = [1, a, 2, b, 3, c, d, e, f].
    
    ?- alternate(Xs, Ys, [1,a,2,b,3,c]).
    Xs = [],
    Ys = [1, a, 2, b, 3, c] ;
    Xs = [1, a, 2, b, 3, c],
    Ys = [] ;
    Xs = [1],
    Ys = [a, 2, b, 3, c] ;
    Xs = [1, 2, b, 3, c],
    Ys = [a] ;
    Xs = [1, 2],
    Ys = [a, b, 3, c] ;
    Xs = [1, 2, 3, c],
    Ys = [a, b] ;
    Xs = [1, 2, 3],
    Ys = [a, b, c] ;
    false.
    

    (在 SWI-Prolog 中,它只对第一个参数进行索引,这比一些替代方案留下了更多的选择点。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-09
      • 2014-06-16
      • 1970-01-01
      • 2017-08-09
      • 1970-01-01
      • 2017-04-26
      • 2018-10-09
      • 1970-01-01
      相关资源
      最近更新 更多