【问题标题】:Splitting a list of integers into a list of positive integers and a list of negative integers将整数列表拆分为正整数列表和负整数列表
【发布时间】:2012-03-21 18:27:04
【问题描述】:

我一直在尝试在 Prolog 中创建一个谓词,它将整数列表拆分为正整数列表和负整数列表。

具有预期结果的示例查询:

?- split([1,-2,3,4,-8],X,Y).
X = [1,3,4],
Y = [-2,-8].

这是我目前得到的代码:

split([], [], []).
split([Head|Tail], List1, List2) :- split(Tail, [Head|List1], List2), Head>=0.
split([Head|Tail], List1, List2) :- split(Tail, List1, [Head|List2]), Head<0.

我似乎无法弄清楚我做错了什么。

【问题讨论】:

  • 如果输入列表包含一个零会发生什么?
  • 应该放在正整数列表中。

标签: list split prolog clpfd


【解决方案1】:

递归部分不太正确。

split([], [], []).
split([Head|Tail], [Head|List1], List2) :- Head>=0, split(Tail, List1, List2).
split([Head|Tail], List1, [Head|List2]) :- Head<0, split(Tail, List1, List2).

HeadHead &gt;= 0应添加到肯定列表中,Head &lt; 0应添加到否定列表中。

另外,检查Head的符号在开头比较好,因为这样可以防止不必要的递归调用。

【讨论】:

  • 非常感谢!也感谢您解释优化。
  • @MizaCoroda 使用trace 来理解递归:)
  • @FlopCode:您的解决方案很好,但在当前的 Prolog 实现中,它消耗的额外辅助空间与非负元素的数量成正比。最好在这里使用compare/3。
  • 您能否说明给定解决方案的派生树?这将是一个很大的帮助。
【解决方案2】:

在 SWI-Prolog 中,您可以使用谓词 partition/4(通常从 apply 模块自动加载):

?- partition(=<(0), [1,-2,3,4,-8,0], X, Y).
X = [1, 3, 4, 0],
Y = [-2, -8].

【讨论】:

  • SWI-Prolog partition/4 谓词不是内置谓词。它是apply 模块中定义的库谓词(很可能是自动加载的)。
  • @PauloMoura Tnx,已修复。
【解决方案3】:

这是一个使用约束的定义。在这种情况下,它是 SWI 和 YAP(也可能是 XSB)的library(clpfd)。这个库非常通用,它包含了常规的整数用途。

:- use_module(library(clpfd)).

使用具体化:

split([], [], []).
split([E|Es], Poss, Negs) :-
   E #>= 0 #<==> B,
   i_split(B, E, Es, Poss, Negs).

i_split(1, E, Es, [E|Poss], Negs) :-
   split(Es, Poss, Negs).
i_split(0, E, Es, Poss, [E|Negs]) :-
   split(Es, Poss, Negs).

或者,你可以使用zcompare/3,我更喜欢那个版本:

split([], [], []).
split([E|Es], Poss, Negs) :-
   zcompare(Order, E, 0),
   c_split(Order, E, Es, Poss, Negs).

c_split(>, E, Es, [E|Poss], Negs) :-
   split(Es, Poss, Negs).
c_split(=, E, Es, [E|Poss], Negs) :-
   split(Es, Poss, Negs).
c_split(<, E, Es, Poss, [E|Negs]) :-
   split(Es, Poss, Negs).

您可以将它用于常规查询,也可以用于更一般的查询,例如

?- split(Es,[A],[]).
Es = [A],
A in 1..sup ;
Es = [0],
A = 0 ;
false.

【讨论】:

    【解决方案4】:

    保持逻辑纯正和高效。如何?通过使用元谓词tpartition/4 和(#=&lt;)/3!

    首先,让我们定义(#=&lt;)/3,(#=&lt;)/2 的具体化版本,基于 关于bool01_t/2。
    为了完整起见,我们还要定义(#&lt;)/3、(#&gt;)/3和(#&gt;=)/3!

    #=<(X,Y,Truth) :- X #=< Y #<==> B, bool01_t(B,Truth).
    
    #<( X,Y,Truth) :- X #<  Y #<==> B, bool01_t(B,Truth).
    
    #>( X,Y,Truth) :- X #>  Y #<==> B, bool01_t(B,Truth).
    
    #>=(X,Y,Truth) :- X #>= Y #<==> B, bool01_t(B,Truth).
    

    就是这样!现在,让我们进行 OP 想要的拆分:

    ?- tpartition(#=<(0),[1,-2,3,4,-8,0],Ts,Fs).
    Ts = [1,3,4,0], Fs = [-2,-8].                   % succeeds deterministically
    

    这是单调,所以即使使用一般的非基本术语,我们也能得到正确的答案:

    ?- tpartition(#=<(0),[A,B,C],Ts,Fs).
    Ts = [     ], Fs = [A,B,C], A in inf.. -1, B in inf.. -1, C in inf.. -1 ;
    Ts = [    C], Fs = [A,B  ], A in inf.. -1, B in inf.. -1, C in   0..sup ;
    Ts = [  B  ], Fs = [A,  C], A in inf.. -1, B in   0..sup, C in inf.. -1 ;
    Ts = [  B,C], Fs = [A    ], A in inf.. -1, B in   0..sup, C in   0..sup ;
    Ts = [A    ], Fs = [  B,C], A in   0..sup, B in inf.. -1, C in inf.. -1 ;
    Ts = [A,  C], Fs = [  B  ], A in   0..sup, B in inf.. -1, C in   0..sup ;
    Ts = [A,B  ], Fs = [    C], A in   0..sup, B in   0..sup, C in inf.. -1 ;
    Ts = [A,B,C], Fs = [     ], A in   0..sup, B in   0..sup, C in   0..sup .
    

    编辑 2015-06-02

    如果我们在上述查询中使用 SWI-Prolog 库谓词 partition/4 会怎样?

    ?- partition(#=<(0),[A,B,C],Ts,Fs).
    Ts = [A,B,C], Fs = [], A in 0..sup, B in 0..sup, C in 0..sup.
    

    我们将失去 8 个解决方案中的 7 个,因为 partition/4 不是单调的!

    【讨论】:

    • #=&lt;(X, Y, Truth) :- X #=&lt; Y #&lt;==&gt; B, ( B = 0, Truth = false ; B = 1, Truth = true ).(前提是B = 0 ; B = 1有索引)
    • tpartition/4 代替 partitionlist/4?
    • @false。当然,为什么不呢? tfilter等中的t实际上代表什么?-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 2023-02-11
    • 1970-01-01
    • 2015-01-20
    相关资源
    最近更新 更多