2017-10-01 41 views
1

我有这样的断言:减少谓词调用

check_matrix([[_, E2, E3], 
      [E4, E5, E6], 
      [E7, E8, E9]]) :- 
    E5 = E9, 
    is_valid([E4, E5, E6]), 
    is_valid([E7, E8, R9]), 
    is_valid([E2, E5, E8]), 
    is_valid([E3, E6, E9]). 

,检查如果我的2×2矩阵是有效用的左上角单元格,_被完全ignored.The E2E3E4E7细胞只是2 x 2矩阵的标题。

是他们更好的方式我可以is_valid(),从而我不需要拨打is_valid() 4次。如果矩阵变大,比如5x5,这个谓词需要调用is_valid() 10次。对于Prolog来说这是正常的吗?或者他们是一个更优雅的方式来做到这一点?

我在考虑创建另一个谓词,它创建了我想要传递给is_valid()的所有可能列表的嵌套列表,然后在每个列表中调用该谓词,然后将结果传递回check_matrix()。我觉得他们一定是一个更优雅的方式来做到这一点。任何建议,将不胜感激。

UPDATE

E5 = E9仅仅是一个对角线检查器,其检查如果一个矩阵的对角线是相同的,如果它们不是,则谓词失败。

回答

2

Prolog有像library(apply)提供高阶谓词一些库:谓词你传递另一个谓词的地方。

似乎适用于此的高阶谓词是maplist/2。谓词以谓词(:Goal)和列表(?List)作为参数,并在?List的元素上应用:Goal,直到谓词失败或列表耗尽。你可以稍微将它与迭代列表的命令式语言中的函数进行比较,并检查谓词是否适用于列表中的所有成员。例如Python中的all(..)

我们因此可以用下面的办法来检查所有,但第一行

:- use_module(library(apply)). 

check_rows([_|T]) :- 
    maplist(is_valid,T). 

然而,我们必须检查所有,但第一列也是如此。因此我们可以使用transpose/2谓词。它有两个参数,第二个参数是第一个参数的转置。所以:

?- transpose([[1, 2, 3], 
|    [4, 5, 6], 
|    [7, 8, 9]],X). 
X = [[1, 4, 7], [2, 5, 8], [3, 6, 9]]. 

所以我们可以转置矩阵,然后再次使用maplist/2检查所有,但是,第一个行转置矩阵M。所以我们现在可以实现类似的断言:

:- use_module(library(apply)). 
:- use_module(library(clpfd)). 

check_matrix(M) :- 
    M = [_|MT], 
    maplist(is_valid,MT), 
    transpose(M,[_|TT]), 
    maplist(is_valid,TT). 

编辑

如果你想检查是否所有,但第一个对角线元素,我建议定义两个附加的谓词:

  • all_same/1检查列表中的所有元素是否相同;和
  • diagonal/2构建对角元素列表。

all_same/1可以实现为:

diagonal(M,L) :- 
    diagonal(M,0,L). 

diagonal([],_,[]). 
diagonal([R|RT],I,[D|DT]) :- 
    nth0(I,R,D), 
    I1 is I+1, 
    diagonal(RT,I1,DT). 

现在,我们可以检查对角线如下:

check_matrix(M) :- 
    M = [_|MT], 
    maplist(is_valid,MT), 
    transpose(M,[_|TT]), 
    maplist(is_valid,TT), 
    diagonal(M,[_|DT]), all_same(DT).
+0

感谢

all_same([]). all_same([_]). all_same([H,H|T]) :- all_same([H|T]). 

diagonal/2可以用获得回答。我也忘了在我的'check_matrix'解决方案中放置一个对角线单元格检查器,它基本上检查对角元素是否全部相同。我已经更新它来包含它。 – RoadRunner

+0

@RoadRunner:不要使用append和1-element列表。如果第一个列表没有接地,它将执行递归并继续尝试写列表。 –

2

如果你想避免这种硬编码的谓词,你可以尝试递归:

您在每个列表(除了第一个)检查is_valid,所以你可以写:

check_matrix([H|T]) :- is_valid_each_list(T),.... 

is_vqlid_each_list([]). 
is_vqlid_each_list([L|T]):- length(L,3),is_valid(L), 
          is_vqlid_each_list(T). 

注意,限制length(L,3)表示元素是长度为3的列表(或者如果您愿意,可以添加其他参数)。

现在检查E2,E5,E8(在同一position > 1元素):

is_valid_pos(L):-length(L,3), is_valid_pos(L,1). 

is_valid_pos(_,4). 
is_valid_pos(L,K):- findall(X,(member(L1,L),nth0(K,L1,X)),L2), 
        is_valid(L2),K1 is K+1, is_valid_pos(L,K1). 

所以完全通用的解决方案:

check_matrix([H|T]) :- is_valid_each_list(T), is_valid_pos([H|T]). 

is_vqlid_each_list([]). 
is_vqlid_each_list([L|T]):- length(L,3),is_valid(L), 
          is_vqlid_each_list(T). 

is_valid_pos(L):-length(L,3), is_valid_pos(L,1). 

is_valid_pos(_,4). 
is_valid_pos(L,K):- findall(X,(member(L1,L),nth0(K,L1,X)),L2), 
        is_valid(L2),K1 is K+1, is_valid_pos(L,K1).