【问题标题】:Split a list on an index in Prolog在 Prolog 中的索引上拆分列表
【发布时间】:2020-10-17 09:56:22
【问题描述】:

我正在编写一个带有 4 个参数的拆分谓词。第一个参数(索引)是完整列表应该被拆分的索引。第二个参数 (CompleteList) 是谓词应在给定索引上拆分的列表。参数 3 和 4 是输出参数(这里是 FirstHalf 和 SecondHalf)。

split(_,[],[],[]).
split(0,CompleteList,FirstHalfList,CompleteList).
split(Index,[CompleteListHead|CompleteListTail],FirstHalfList,SecondHalfList) :-
    append(FirstHalfList,[CompleteListHead],NewFirstHalfList),
    NewIndex is Index-1,
    split(NewIndex,CompleteListTail,NewFirstHalfList,SecondHalfList).

查询所需输出的示例是

split(2,[5,4,3,2,1],X,Y).
X=[5,4],
Y=[3,2,1]

我了解如何返回列表的后半部分,但我无法返回前半部分。示例查询的程序的当前输出是

split(2,[5,4,3,2,1],X,Y).
X=[],
Y=[3,2,1]

是否有可能返回 FirstHalfList(而不是 NewFirstHalfList,它会回溯到一个空列表)还是当前代码是编写 Prolog 谓词的错误方法?

编辑:谢谢大家的回复,他们给了我更好的洞察力。

【问题讨论】:

  • 你应该忽略编译器给出的警告。第二个子句的目的是什么(即split(0,CompleteList,FirstHalfList,CompleteList).)? Prolog 数据模型是 relational,这意味着您应该在谓词的参数之间建立 relations...
  • 第二个子句确定拆分谓词何时知道它已准备好。一旦索引达到零,它就知道 SecondHalfList 等于 CompleteList 的余数。但事实上,FirstHalfList 在这里毫无用处......

标签: prolog


【解决方案1】:

有很多方法可以做到这一点。

这是一种压缩方式:

split(Index,List,Left,Right) :-
   length(Left,Index),       % Actually CREATES a list of fresh variables if "Left" is unbound
   append(Left,Right,List).  % Demand that Left + Right = List.

然后:

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

?- split(2,[],X,Y).
false.

?- split(0,[],X,Y).
X = Y, Y = [].

它甚至可以自动“反向”工作:

?- split(L,[5,4,3,2,1],[5,4],Y).
L = 2,
Y = [3, 2, 1].

想想为什么会这样!

调试打印输出在这里有帮助:

split(Index,List,Left,Right) :-
   debugme(Index,List,Left,Right),
   length(Left,Index),
   debugme(Index,List,Left,Right),   
   append(Left,Right,List),
   debugme(Index,List,Left,Right).
   
debugme(Index,List,Left,Right) :-
   format("Index: ~q, List: ~q, Left: ~q, Right: ~q\n",[Index,List,Left,Right]).

然后:

?- split(L,[5,4,3,2,1],[5,4],Y).
Index: _6640, List: [5,4,3,2,1], Left: [5,4], Right: _6646
Index: 2, List: [5,4,3,2,1], Left: [5,4], Right: _6646
Index: 2, List: [5,4,3,2,1], Left: [5,4], Right: [3,2,1]
L = 2,
Y = [3, 2, 1].   

注意出现在子句中的变量集逐渐填充信息的方式。

【讨论】:

    【解决方案2】:

    分割谓词递归地减少它的第二个参数。 第一个子句处理终端情况(当索引>给定列表的长度时) 当 Index 减少到 0 时,第二个子句处理另一种终端情况。

    split(_,[],[],[]).
    split(0,L,[],L).
    split(Index,[CompleteListHead|CompleteListTail],[CompleteListHead|FirstHalfTail],SecondHalfList) :-
        Index >= 0,
        NewIndex is Index-1,
        split(NewIndex,CompleteListTail,FirstHalfTail,SecondHalfList).
    
    ?- split(2,[5,4,3,2,1],X,Y).
    X = [5, 4],
    Y = [3, 2, 1] ;
    
    ?- split(8,[5,4,3,2,1],X,Y).
    X = [5, 4, 3, 2, 1],
    Y = [] ;
    
    ?- split(4,[5,4,3,2,1],X,Y).
    X = [5, 4, 3, 2],
    Y = [1] ;
    

    【讨论】:

    • 第三个子句中的条件不应该是Index > 0,即使用严格不等式吗?
    【解决方案3】:
    %! split(?INDEX0,?Xs0,?Ys,?Zs)
    %
    % `Xs0` is split at `INDEX0` into `Ys` and `Zs` .
    
    split(_,[],[],[]) .
    
    split(INDEX,Xs0,Ys,Zs)
    :-
    lists:length(Ys,INDEX) ,
    lists:append(Ys,Zs,Xs0)
    .
    
    /*
    ?- split(2,[5,4,3,2,1],Ys,Zs) .
    Ys = [5,4] ,
    Zs = [3,2,1] .
    */
    
    /*
    ?- split(INDEX,Xs0,[5,4],[3,2,1]) .
    INDEX = 2 ,
    Xs0 = [5,4,3,2,1] .
    */
    
    /*
    ?- split(INDEX,Xs0,Ys,Zs) .
    Xs0 = Ys = Zs = [] ? ;
    INDEX = 0 ,
    Xs0 = Zs ,
    Ys = [] ? ;
    INDEX = 1 ,
    Xs0 = [A|Zs] ,
    Ys = [A] ? ;
    INDEX = 2 ,
    Xs0 = [A,B|Zs] ,
    Ys = [A,B] ? ;
    INDEX = 3 ,
    Xs0 = [A,B,C|Zs] ,
    Ys = [A,B,C] ? ;
    INDEX = 4 ,
    Xs0 = [A,B,C,D|Zs] ,
    Ys = [A,B,C,D] ? %etcetera.
    */
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      • 2015-01-20
      • 1970-01-01
      • 1970-01-01
      • 2019-03-17
      相关资源
      最近更新 更多