基于prolog-dcg的矩阵匹配

基于prolog-dcg的矩阵匹配,prolog,dcg,Prolog,Dcg,DCGs是否可以提取一些2D列表,这样就可以用上下文无关语法来表示 S -> [ A , B ] A -> [0,0,0,0,0] A -> NULL B -> [1,1,1,1,1] B -> NULL 例如: [[0,0,0,0,0], [1,1,1,1,1]] is valid [[1,1,1,1,1]] is valid, where A is NULL. [[0,0,0,0,0]] is valid, where B is NULL. 我试过这样的

DCGs是否可以提取一些2D列表,这样就可以用上下文无关语法来表示

S -> [ A , B ]

A -> [0,0,0,0,0]
A -> NULL

B -> [1,1,1,1,1]
B -> NULL
例如:

[[0,0,0,0,0], [1,1,1,1,1]] is valid
[[1,1,1,1,1]] is valid, where A is NULL.
[[0,0,0,0,0]] is valid, where B is NULL.
我试过这样的东西

zeros --> [].
zeros --> [0,0,0,0,0].
ones --> [].
ones --> [1,1,1,1,1]
matrix --> [A, B],
    {phrase(zeros, A)},
    {phrase(ones, B)}.
但这不会按我希望的方式工作,因为在本例中,编译器认为我需要一个空列表“[]”,而不是NULL

因此[[],[1,1,1,1,1]]将起作用,而[[1,1,1,1]]不起作用


我该怎么做?

问题是,一旦你写了矩阵->[A,B],不管A和B是什么,这个规则肯定会生成一个两元素列表

因此,您可以选择生成一个元素列表[A]或[B]。您可以明确地这样做:

a --> [0, 0, 0, 0, 0].

b --> [1, 1, 1, 1, 1].

matrix -->
    [A],
    { phrase(a, A) }.
matrix -->
    [B],
    { phrase(b, B) }.
matrix -->
    [A, B],
    { phrase(a, A) },
    { phrase(b, B) }.
这项工作:

?- phrase(matrix, Matrix).
Matrix = [[0, 0, 0, 0, 0]] ;
Matrix = [[1, 1, 1, 1, 1]] ;
Matrix = [[0, 0, 0, 0, 0], [1, 1, 1, 1, 1]].
但是这需要大量的输入,如果你想扩展它,它不是很灵活

那么,让我们尝试推广固定[A,B]位。作为第一步,我们可以使用一个列表//1 DCG来描述它自己的参数列表:

list([]) -->
    [].
list([X|Xs]) -->
    [X],
    list(Xs).
optional_list([]) -->
    [].
optional_list([_X|Xs]) -->
    % skip this X!
    optional_list(Xs).
optional_list([X|Xs]) -->
    % keep this X
    [X],
    optional_list(Xs).
我们可以这样使用:

?- phrase(list([a, b, c]), Xs).
Xs = [a, b, c].
?- phrase(optional_list([a, b, c]), Xs).
Xs = [] ;
Xs = [c] ;
Xs = [b] ;
Xs = [b, c] ;
Xs = [a] ;
Xs = [a, c] ;
Xs = [a, b] ;
Xs = [a, b, c].
并使用它定义一个矩阵:

matrix_with_list -->
    list([A, B]),
    { phrase(a, A) },
    { phrase(b, B) }.
看起来我们还没有取得进展:

?- phrase(matrix_with_list, Matrix).
Matrix = [[0, 0, 0, 0, 0], [1, 1, 1, 1, 1]].
但现在我们可以稍微更改list//1,以仅描述其参数列表的子列表:

list([]) -->
    [].
list([X|Xs]) -->
    [X],
    list(Xs).
optional_list([]) -->
    [].
optional_list([_X|Xs]) -->
    % skip this X!
    optional_list(Xs).
optional_list([X|Xs]) -->
    % keep this X
    [X],
    optional_list(Xs).
其行为如下:

?- phrase(list([a, b, c]), Xs).
Xs = [a, b, c].
?- phrase(optional_list([a, b, c]), Xs).
Xs = [] ;
Xs = [c] ;
Xs = [b] ;
Xs = [b, c] ;
Xs = [a] ;
Xs = [a, c] ;
Xs = [a, b] ;
Xs = [a, b, c].
现在我们可以修改前面的定义:

matrix_with_optional_list -->
    optional_list([A, B]),
    { phrase(a, A) },
    { phrase(b, B) }.
我们得到:

?- phrase(matrix_with_optional_list, Matrix).
Matrix = [] ;
Matrix = [[1, 1, 1, 1, 1]] ;
Matrix = [[0, 0, 0, 0, 0]] ;
Matrix = [[0, 0, 0, 0, 0], [1, 1, 1, 1, 1]].
很好!但是,拥有所有这些短语/2调用并不好,即使它们引用的元素不在矩阵中

让我们进一步推广到一个DCG,它的参数是一个DCG列表,它描述了这些DCG所描述的列表的子列表:

optional_phrase([]) -->
    [].
optional_phrase([_Rule|Rules]) -->
    % skip this rule
    optional_phrase(Rules).
optional_phrase([Rule|Rules]) -->
    % apply this rule
    [List],
    { phrase(Rule, List) },
    optional_phrase(Rules).
这里的主要观点是,您可以以更高阶的方式使用短语/2,其中它的第一个参数不是命名DCG的文字原子或函子项,而是绑定到此类原子或项的变量。但是,在应用此规则时,必须确保这些变量确实是绑定的

据此,矩阵的最终定义如下:

matrix_with_optional_phrase -->
    optional_phrase([a, b]).
现在,它像以前一样枚举矩阵,但它只对实际上是矩阵一部分的元素执行短语/2:

?- phrase(matrix_with_optional_phrase, Matrix).
Matrix = [] ;
Matrix = [[1, 1, 1, 1, 1]] ;
Matrix = [[0, 0, 0, 0, 0]] ;
Matrix = [[0, 0, 0, 0, 0], [1, 1, 1, 1, 1]].

问题是,一旦你写了矩阵->[A,B],不管A和B是什么,这个规则肯定会生成一个两元素列表

因此,您可以选择生成一个元素列表[A]或[B]。您可以明确地这样做:

a --> [0, 0, 0, 0, 0].

b --> [1, 1, 1, 1, 1].

matrix -->
    [A],
    { phrase(a, A) }.
matrix -->
    [B],
    { phrase(b, B) }.
matrix -->
    [A, B],
    { phrase(a, A) },
    { phrase(b, B) }.
这项工作:

?- phrase(matrix, Matrix).
Matrix = [[0, 0, 0, 0, 0]] ;
Matrix = [[1, 1, 1, 1, 1]] ;
Matrix = [[0, 0, 0, 0, 0], [1, 1, 1, 1, 1]].
但是这需要大量的输入,如果你想扩展它,它不是很灵活

那么,让我们尝试推广固定[A,B]位。作为第一步,我们可以使用一个列表//1 DCG来描述它自己的参数列表:

list([]) -->
    [].
list([X|Xs]) -->
    [X],
    list(Xs).
optional_list([]) -->
    [].
optional_list([_X|Xs]) -->
    % skip this X!
    optional_list(Xs).
optional_list([X|Xs]) -->
    % keep this X
    [X],
    optional_list(Xs).
我们可以这样使用:

?- phrase(list([a, b, c]), Xs).
Xs = [a, b, c].
?- phrase(optional_list([a, b, c]), Xs).
Xs = [] ;
Xs = [c] ;
Xs = [b] ;
Xs = [b, c] ;
Xs = [a] ;
Xs = [a, c] ;
Xs = [a, b] ;
Xs = [a, b, c].
并使用它定义一个矩阵:

matrix_with_list -->
    list([A, B]),
    { phrase(a, A) },
    { phrase(b, B) }.
看起来我们还没有取得进展:

?- phrase(matrix_with_list, Matrix).
Matrix = [[0, 0, 0, 0, 0], [1, 1, 1, 1, 1]].
但现在我们可以稍微更改list//1,以仅描述其参数列表的子列表:

list([]) -->
    [].
list([X|Xs]) -->
    [X],
    list(Xs).
optional_list([]) -->
    [].
optional_list([_X|Xs]) -->
    % skip this X!
    optional_list(Xs).
optional_list([X|Xs]) -->
    % keep this X
    [X],
    optional_list(Xs).
其行为如下:

?- phrase(list([a, b, c]), Xs).
Xs = [a, b, c].
?- phrase(optional_list([a, b, c]), Xs).
Xs = [] ;
Xs = [c] ;
Xs = [b] ;
Xs = [b, c] ;
Xs = [a] ;
Xs = [a, c] ;
Xs = [a, b] ;
Xs = [a, b, c].
现在我们可以修改前面的定义:

matrix_with_optional_list -->
    optional_list([A, B]),
    { phrase(a, A) },
    { phrase(b, B) }.
我们得到:

?- phrase(matrix_with_optional_list, Matrix).
Matrix = [] ;
Matrix = [[1, 1, 1, 1, 1]] ;
Matrix = [[0, 0, 0, 0, 0]] ;
Matrix = [[0, 0, 0, 0, 0], [1, 1, 1, 1, 1]].
很好!但是,拥有所有这些短语/2调用并不好,即使它们引用的元素不在矩阵中

让我们进一步推广到一个DCG,它的参数是一个DCG列表,它描述了这些DCG所描述的列表的子列表:

optional_phrase([]) -->
    [].
optional_phrase([_Rule|Rules]) -->
    % skip this rule
    optional_phrase(Rules).
optional_phrase([Rule|Rules]) -->
    % apply this rule
    [List],
    { phrase(Rule, List) },
    optional_phrase(Rules).
这里的主要观点是,您可以以更高阶的方式使用短语/2,其中它的第一个参数不是命名DCG的文字原子或函子项,而是绑定到此类原子或项的变量。但是,在应用此规则时,必须确保这些变量确实是绑定的

据此,矩阵的最终定义如下:

matrix_with_optional_phrase -->
    optional_phrase([a, b]).
现在,它像以前一样枚举矩阵,但它只对实际上是矩阵一部分的元素执行短语/2:

?- phrase(matrix_with_optional_phrase, Matrix).
Matrix = [] ;
Matrix = [[1, 1, 1, 1, 1]] ;
Matrix = [[0, 0, 0, 0, 0]] ;
Matrix = [[0, 0, 0, 0, 0], [1, 1, 1, 1, 1]].

DCG表示法保留生产中的列表,以表示“令牌”序列。 然后,您的生产零(例如)将匹配五个零的序列,而不是五个零的列表。这里有一些混乱,仅仅是因为您的目标语言列表序列使用元语言符号Prolog List表示DCG产品中的终端序列。 我想你可以写得很简单

零->[[0,0,0,0,0]]。 一->[[1,1,1,1,1]]。 矩阵->零;一是矩阵;[]. 测试:-短语矩阵,[[1,1,1,1],[0,0,0,0,0]]。
DCG表示法保留生产中的列表,以表示“令牌”序列。 然后,您的生产零(例如)将匹配五个零的序列,而不是五个零的列表。这里有一些混乱,仅仅是因为您的目标语言列表序列使用元语言符号Prolog List表示DCG产品中的终端序列。 我想你可以写得很简单

零->[[0,0,0,0,0]]。 一->[[1,1,1,1,1]]。 矩阵->零;一是矩阵;[]. 测试:-短语矩阵,[[1,1,1,1],[0,0,0,0,0]]。
嗯,用可选短语[rule | rules]将规则传递给另一个规则让我很惊讶,我从来都不知道我可以这样使用它,这让它很难理解。我不明白为什么prolog把可选的_短语[a,b]中的a和b看作规则而不是值
可选_短语[a,b]中的a和b依次与可选_短语//1最后一句中的规则统一。在那里,它们被传递给短语规则,List call。当Rule=a时,调用短语Rule,List完全等同于短语a,List。没有什么神奇的东西可以阻止这一点,短语/2不需要在源代码中具有文字常量参数。运行时需要用到所有的东西。嗯,用可选短语[rule | rules]将规则传递给另一个规则让我很惊讶,我从来不知道我可以这样使用它,这让它很难理解。我不明白为什么prolog将可选_短语[a,b]中的a和b视为规则而不是值。可选_短语[a,b]中的a和b依次与可选_短语/1最后一句中的规则统一。在那里,它们被传递给短语规则,List call。当Rule=a时,调用短语Rule,List完全等同于短语a,List。没有什么神奇的东西可以阻止这一点,短语/2不需要在源代码中具有文字常量参数。它需要在运行时获取任何内容。