将列表分成固定长度子列表的列表

时间:2017-03-02 13:27:33

标签: prolog

给定一个列表L,例如[1,2,3,4,5,6,7]和一个数字N,例如3,我想创建一个谓词来分隔元素L列入大小为N的列表。

因此,结果将是:[[1,2,3], [4,5,6], [7]]在我们的案例中。

我尝试过:

% List containing the first N elements of given list.
    takeN([X|Xs], 0, []) :- !.
    takeN([X|Xs], N, [X|Ys]) :- N1 is N-1, takeN(Xs, N1, Ys).

% Given list without the first N elements.      
    dropN(R, 0, R) :- !.
    dropN([X|Xs], N, R) :- N1 is N-1, dropN(Xs, N1, R).

% size of list.
    sizeL([], 0) :- !.
    sizeL([X|Xs], N) :- sizeL(Xs, N1), N is N1+1.

blockify(R, N, [R|[]]) :- sizeL(R, N1), N1 < N, !.
blockify([X|Xs], N, [Y|Ys]) :- sizeL(R, N1), N1 >= N, takeN([X|Xs], N, Y),
     dropN([X|Xs], N, Res), blockify(Res, N, Ys).

它不起作用(blockify([1,2,3], 2, R)例如返回false,而不是[[1,2], [3]]。)

但是,我无法找到我错误的地方。这有什么问题?

3 个答案:

答案 0 :(得分:2)

我认为你的想法有点过于复杂。首先,让我们排除我们想要blockify/3空列表的情况:

blockify([],_,[]).

现在,在原始列表中有元素的情况下,我们只使用两个累加器:   - 存储运行顺序的某种差异列表;和   - 一个累加器,倒计时并查看我们是否达到零,在这种情况下,我们附加运行差异列表并构建一个新的。

所以这就像是:

blockify([H|T],N,R) :-
    N1 is N-1,
    blockify(T,N1,N1,[H|D],D,R).

现在blockify/5有一些重要的案例:

  • 我们到达列表的末尾:在这种情况下,我们关闭差异列表并将其附加到正在运行的R

    blockify([],_,_,D,[],[D]).
    
  • 我们到达当前计数器的底部,我们将差异列表添加到R,我们使用更新的计数器初始化新的差异列表:

    blockify([H|T],N,0,D,[],[D|TR]) :-
        blockify(T,N,N,[H|D2],D2,TR).
    
  • 这些情况都不是,我们只是将元素附加到运行差异中,递减累加器并继续:

    blockify([H|T],N,M,D,[H|D2],TR) :-
        M > 0,
        M1 is M-1,
        blockify(T,N,M1,D,D2,TR).
    

或者把它们放在一起:

blockify([],_,[]).
blockify([H|T],N,R) :-
    N1 is N-1,
    blockify(T,N1,N1,[H|D],D,R).

blockify([],_,_,D,[],[D]).
blockify([H|T],N,0,D,[],[D|TR]) :-
    blockify(T,N,N,[H|D2],D2,TR).
blockify([H|T],N,M,D,[H|D2],TR) :-
    M > 0,
    M1 is M-1,
    blockify(T,N,M1,D,D2,TR).

因为在每次递归调用中,所有子句都在 O(1)中运行,我们使用 n 进行深度递归 O(n)原始列表中的元素数量,此程序在 O(n)

中运行

答案 1 :(得分:1)

如果您的Prolog提供length / 2,紧凑的解决方案可能是:

blockify(R, N, [B|Bs]) :-
    length(B, N),
    append(B, T, R),
    !, blockify(T, N, Bs).
blockify(R, _N, [R]).

答案 2 :(得分:0)

让我教你如何调试Prolog查询:

1) blockify([1,2,3], 2, R)
2) does it match blockify(R, N, [R|[]]) ? oh yes,
   it can be bound to blockify([1, 2, 3], 2, [[1, 2, 3]])
3) let's evaluate the body: sizeL(R, N1), N1 < N, !.
   Replace R and N, we get: sizeL([1, 2, 3], N1), N1 < 2, !.
4) evaluate sizeL([1, 2, 3], N1): for brevity, since it's a common
   list count predicate, the result should be obvious: N1 = 3
5) evaluate N1 < N: 3 < 2 => false
6) since the rest are all , (and operator) a single false
   is enough to make the whole body to evaluate to false
7) there you go, the predicate is false

看看你的错误在哪里?