减少谓词调用

时间:2017-10-01 07:07:03

标签: list prolog

我有这个谓词:

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 x 2矩阵是否有效,左上角的单元格_被完全忽略。E2E3E4和{{ 1}}单元格只是2 x 2矩阵的标题。

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

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

更新

is_valid()只是一个对角线检查器,用于检查矩阵的对角线是否相同,如果不是,则谓词失败。

2 个答案:

答案 0 :(得分: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的列表(如果您愿意,则为任何参数K,只需添加其他参数)。

现在检查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).

答案 1 :(得分:2)

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

这里似乎适用的高阶谓词是maplist/2。谓词将谓词(:Goal)和列表(?List)作为参数,并将:Goal应用于?List的元素,直到谓词失败,或列表为累。您可以将它与在迭代列表的命令式语言中的函数进行比较,并检查谓词是否适用于列表的所有成员。就像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可以实现为:

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

diagonal/2可以通过以下方式获得:

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).