2012-01-03 52 views
1

我想寫一個將列表劃分爲N部分的謂詞。 這是我到目前爲止。序言 - 在N部分中劃分列表

partition(1, List, List). 
partition(N, List, [X,Y|Rest]):- 
    chop(List, X, Y), 
    member(NextToChop, [X,Y]), %Choose one of the new parts to chop further. 
    NewN is N-1, 
    partition(NewN, NextToChop, Rest). 

chop(List, _, _):- 
    length(List, Length), 
    Length < 2, %You can't chop something that doesn't have at least 2 elements 
    fail,!. 
chop(List, Deel1, Deel2):- 
    append(Deel1, Deel2, List), 
    Deel1 \= [], 
    Deel2 \= []. 

這個想法是繼續將列表的部分切成兩個其他部分,直到我有N件。 我有這種方法效果平平:

?- partition(2, [1,2,3,4], List). 
List = [[1], [2, 3, 4], 1] ; 
List = [[1], [2, 3, 4], 2, 3, 4] ; 
List = [[1, 2], [3, 4], 1, 2] ; 
List = [[1, 2], [3, 4], 3, 4] ; 
List = [[1, 2, 3], [4], 1, 2, 3] ; 
List = [[1, 2, 3], [4], 4] ; 
false. 

所以我得到了我想要的東西,但我得到它的兩倍,並有附加了一些其他的事情。 將在3個部分事情變得更糟糕:

?- partition(3, [1,2,3,4], List). 
List = [[1], [2, 3, 4], [2], [3, 4], 2] ; 
List = [[1], [2, 3, 4], [2], [3, 4], 3, 4] ; 
List = [[1], [2, 3, 4], [2, 3], [4], 2, 3] ; 
List = [[1], [2, 3, 4], [2, 3], [4], 4] ; 
List = [[1, 2], [3, 4], [1], [2], 1] ; 
List = [[1, 2], [3, 4], [1], [2], 2] ; 
List = [[1, 2], [3, 4], [3], [4], 3] ; 
List = [[1, 2], [3, 4], [3], [4], 4] ; 
List = [[1, 2, 3], [4], [1], [2, 3], 1] ; 
List = [[1, 2, 3], [4], [1], [2, 3], 2, 3] ; 
List = [[1, 2, 3], [4], [1, 2], [3], 1, 2] ; 
List = [[1, 2, 3], [4], [1, 2], [3], 3] ; 
false. 

另一個想法是使用前綴,但我不知道怎麼會真正發揮作用。爲了使用它,我應該能夠讓Prolog知道它需要的前綴不能太短,也不能太長,所以我不會選擇一個太長的前綴,因此下一個遞歸步驟沒有剩餘。

任何人都可以指向正確的方向嗎?

小小澄清:謂詞應該返回所有以N部分(不包括空列表)分割列表的可能性。

+0

應該N部分有相同的長度?如果謂詞返回所有可能的方式,您可以將列表分成N部分? – 2012-01-03 13:43:43

+0

@thanosQR:謂詞應該返回所有可能的方式,您可以將列表分爲N個部分。我會將它添加到OP中。 – Mental 2012-01-03 14:57:52

回答

3

下面是基本的方式,我想用它來實現一個(使用append/2length/2):

list_n_parts(List, Parts, Result) :- 
    length(Result, Parts), 
    append(Result, List). 

現在,沒有按」 t完全符合您的期望:它允許[]。要解決這個問題

一個想法是使用maplist調用格式化事先生成的列表:

list_n_parts(List, Parts, Result) :- 
    length(Result, Parts), 

使用copy_term/2,該maplist/2調用如下:使用functor/3(學分

maplist(copy_term([_|_]), Result), 

@false),它看起來像:

maplist(functor('.', 2), Result), 

使用lambda.pl你可以寫:

maplist(\[_|_]^true, Result), 

因爲 '\' 已經執行期限副本(感謝@false)。

唯一剩下的東西是append/2電話:

append(Result, List). 

另一個想法是使用forall/2過濾(也許簡單搞定,但糟糕的是在複雜性):

list_n_parts(List, Parts, Result) :- 
    length(Result, Parts), 
    append(Result, List), 
    forall(member(X, Result), X \= []). 

等。 。

+0

啊,真是太簡單了,我甚至無法理解它。非常感謝! @mat的解決方案很好,也很乾淨,但它使用了我在課堂上沒有看到的東西,所以我會用你的:) – Mental 2012-01-03 15:04:19

+0

@Mog:'maplist(\ [_ | _]^true,Result)'會描述與'maplist(\ X ^(copy_term([_ | _],NotEmpty),=(NotEmpty,X)),Result)'相同。也就是說,'\'本身已經做了一個'copy_term'。 – false 2012-02-19 18:32:04

+0

在**的情況下,'maplist(functor('。',2),Result)'仍然是非常小的優勢。 – false 2012-02-19 20:42:34

5

當描述涉及列表的關係時,DCG通常非常有用。試想一下:

list_n_parts(List, N, Parts) :- 
     length(Parts, N), 
     phrase(parts(Parts), List). 

parts([]) --> []. 
parts([Part|Parts]) --> part(Part), parts(Parts). 

part([P|Ps]) --> [P], list(Ps). 

list([]) --> []. 
list([L|Ls]) --> [L], list(Ls). 

樣品查詢:

?- list_n_parts([1,2,3,4], 2, Ps). 
Ps = [[1], [2, 3, 4]] ; 
Ps = [[1, 2], [3, 4]] ; 
Ps = [[1, 2, 3], [4]] ; 
false. 
+1

我在另一個課堂上看到過DCG,但從來不知道你可以在Prolog中使用它們。你的解決方案非常乾淨,但由於我沒有在我的聲明語言課程中看到DCG,所以我會使用@Mog的解決方案。無論如何,我在Prolog中學到了DCG的知識^^ – Mental 2012-01-03 15:05:38