使用 Prolog 检查 IP 地址列表
Checking IP like address list with Prolog
我正在尝试构建一个给定列表(例如 ['1', '2', '3', '.', ... , '2']
)的谓词,它必须将其识别为 (NNN.NNN.NNN.NNN)
格式的 IP 字符串,其中 N
是一个数字(在 0
和 9
之间)它 return 如果格式正确则为真。它还必须接受 IP 仅为(N.N.N.N
或 NNN.NNN.N.N
和其他组合)的情况。
这是我目前所做的
test([X,Y,Z,D | String]) :- digit(X), digit(Y), digit(Z), dot(D), test(String).
其中数字是一个事实列表,例如 digit('0').
从 0
到 9
;点是定义为 dot('.')
的事实。我的代码缺乏模块化(它无法识别单个 N
)。你能帮我实现这个吗?
谢谢!
欢迎来到 SO。我会这样做:
将输入列表分成 4 个“块”,其中点是分隔符,并且只接受其中包含 1-3 个数字元素的块。由于数字 (1-3) 的长度限制和点的限制(恰好 3 个点),执行此序言可能有点棘手。所以我的方法看起来像这样(基本但有点扭曲):
test(In):-
digits123(In,['.'|R1]),
digits123(R1,['.'|R2]),
digits123(R2,['.'|R3]),
digits123(R3,[]).
digits123([A,B,C|R],R):-
digit(A),
digit(B),
digit(C).
digits123([A,B|R],R):-
digit(A),
digit(B).
digits123([A|R],R):-
digit(A).
它有什么作用? digits123/2
获取一个列表作为输入,它检查前 3、2 或一个元素是否为数字。此外,列表的其余部分与第二个参数统一,使其成为对数字的测试和对其余列表的 generator/test。 digits123/2
本身就是错误的,因为三位数字可能会被误认为是两位数或一位数。因此调用使其变得特别:首先在输入列表 In
中调用 digits123/2
,返回包含剩余元素的列表。但是剩下的列表必须以点 (['.'|R1]
) 开头。通过这样写,R1
是没有点作为分隔符的剩余列表。再重复两次以检查第二和第三块。最后一个块不同:它有 1-3 个数字,但后面没有点(或其他任何东西)。换句话说:不应该保留任何元素(空列表,[]
)。
使用 SWISH 测试:
?- test(['1', '2', '3', '.', '2', '3', '.', '2', '.' , '2']).
true;
false.
?- test(['1', '2', '3', '.', '2', '3', '.', '2', '.' ]).
false.
最简单的方法是使用 DCG 语法来指定虚线四边形的外观。 IPv4 地址为 4 个八位字节,以十进制表示,并以“.”分隔。我们可以将其表示为这样的 DCG 规则:
dotted_quad --> octet, dot, octet, dot, octet, dot, octet.
.
的规则很简单:
dot --> ['.'].
八位字节的规则更复杂。一个八位字节被限制在域 0-255 中。用十进制表示可以是
- 0-9:
一位小数 0
–9
- 10-99:
两位小数,第一个在域1
–9
,第二个在域0
–9
.
- 100-199:
三位小数。第一个是 1
,第二个和第三个在域 0
–9
. 中
- 200-249:
三位小数。第一个是 2
,第二个的域是 0
–4
,第三个是 0
–9
.
- 250–255:
三位小数。第一个是2
,第二个是5
,第三个被限制在0
–5
.
我们可以编写很多特殊规则来解决这个问题,或者为十进制数字的规则添加一些上下文。让我们为该规则添加一些上下文(因为为什么输入的内容比您需要的多):
digit(D) --> [D].
digit(Min,Max) --> [D], { D @>= Min, D @=< Max }.
这些让我们说 digit('0','5')
表示允许域 0-5 中的数字,或者 digit('2')
表示只允许数字 2。
现在我们已经可以定义八位字节的规则了:
octet --> digit('0','9') .
octet --> digit('1','9') , digit('0','9').
octet --> digit('1') , digit('0','9') , digit('0','9').
octet --> digit('2') , digit('0','4') , digit('0','9').
octet --> digit('2') , digit('5') , digit('0','5').
现在我们可以定义测试谓词了。它需要做的就是调用我们定义的 DCG:
is_dotted_quad(Cs) :- phrase(dotted_quad, Cs).
但是自从输入类似
的内容后
[ '1','2','3', '.', '2','3','4', '.', '7','8', '.', '9' ]`
繁琐且容易出错,让我们添加一些便利让我们使用字符串或原子:
is_dotted_quad(X) :- atom(X) , atom_chars(X,Cs) , is_dotted_quad(Cs) .
is_dotted_quad(X) :- string(X) , atom_chars(X,Cs) , is_dotted_quad(Cs) .
is_dotted_quad(Cs) :- phrase(dotted_quad, Cs).
将所有内容放在一起,您会得到:
is_dotted_quad(X) :- atom(X) , atom_chars(X,Cs) , is_dotted_quad(Cs).
is_dotted_quad(X) :- string(X) , string_chars(X,Cs) , is_dotted_quad(Cs).
is_dotted_quad(Cs) :- phrase(dotted_quad, Cs).
dotted_quad --> octet, dot, octet, dot, octet, dot, octet.
dot --> ['.'].
octet --> digit('0','9') .
octet --> digit('1','9') , digit('0','9') .
octet --> digit('1') , digit('0','9') , digit('0','9').
octet --> digit('2') , digit('0','4') , digit('0','9').
octet --> digit('2') , digit('5') , digit('0','5').
digit(V) --> [V].
digit(Min,Max) --> [D], { D @>= Min, D @=< Max }.
您可以 fiddle 在 https://swish.swi-prolog.org/p/dotted-quad-dcg.pl
使用它
或者,如果您不想使用 DCG,那么 @Dudas 的回答的重复部分也可以——但应该知道这基本上就是上面的 DCG(DCG 本质上是一个简单的香草序言之上的一点语法糖),只是有点不那么明确的意图:
is_dotted_quad(X) :- atom(X) , atom_chars(X,Cs) , is_dotted_quad(Cs).
is_dotted_quad(X) :- string(X) , string_chars(X,Cs) , is_dotted_quad(Cs).
is_dotted_quad(Cs) :- octet(Cs,T1),
dot(T1,T2), octet(T2,T3),
dot(T3,T4), octet(T4,T5),
dot(T5,T6), octet(T6,[]).
chars( [] , [] ) .
chars( [C|Cs] , [C|Cs] ) .
chars( X , Cs ) :- atom(X) , atom_chars(X,Cs) .
chars( X , Cs ) :- string(X) , string_chars(X,Cs) .
dot(['.'|T],T).
octet( [ Z | T ], T ) :- range(Z,'0','9') .
octet( [ Y , Z | T ], T ) :- range(Y,'1','9') , range(Z,'0','9') .
octet( [ '1' , Y , Z | T ], T ) :- range(Y,'0','9') , range(Z,'0','9') .
octet( [ '2' , Y , Z | T ], T ) :- range(Y,'0','4') , range(Z,'0','9') .
octet( [ '2' , '5' , Z | T ], T ) :- range(Z,'0','5') .
range(X,Min,Max) :- X @>= Min, X @=< Max .
我正在尝试构建一个给定列表(例如 ['1', '2', '3', '.', ... , '2']
)的谓词,它必须将其识别为 (NNN.NNN.NNN.NNN)
格式的 IP 字符串,其中 N
是一个数字(在 0
和 9
之间)它 return 如果格式正确则为真。它还必须接受 IP 仅为(N.N.N.N
或 NNN.NNN.N.N
和其他组合)的情况。
这是我目前所做的
test([X,Y,Z,D | String]) :- digit(X), digit(Y), digit(Z), dot(D), test(String).
其中数字是一个事实列表,例如 digit('0').
从 0
到 9
;点是定义为 dot('.')
的事实。我的代码缺乏模块化(它无法识别单个 N
)。你能帮我实现这个吗?
谢谢!
欢迎来到 SO。我会这样做:
将输入列表分成 4 个“块”,其中点是分隔符,并且只接受其中包含 1-3 个数字元素的块。由于数字 (1-3) 的长度限制和点的限制(恰好 3 个点),执行此序言可能有点棘手。所以我的方法看起来像这样(基本但有点扭曲):
test(In):-
digits123(In,['.'|R1]),
digits123(R1,['.'|R2]),
digits123(R2,['.'|R3]),
digits123(R3,[]).
digits123([A,B,C|R],R):-
digit(A),
digit(B),
digit(C).
digits123([A,B|R],R):-
digit(A),
digit(B).
digits123([A|R],R):-
digit(A).
它有什么作用? digits123/2
获取一个列表作为输入,它检查前 3、2 或一个元素是否为数字。此外,列表的其余部分与第二个参数统一,使其成为对数字的测试和对其余列表的 generator/test。 digits123/2
本身就是错误的,因为三位数字可能会被误认为是两位数或一位数。因此调用使其变得特别:首先在输入列表 In
中调用 digits123/2
,返回包含剩余元素的列表。但是剩下的列表必须以点 (['.'|R1]
) 开头。通过这样写,R1
是没有点作为分隔符的剩余列表。再重复两次以检查第二和第三块。最后一个块不同:它有 1-3 个数字,但后面没有点(或其他任何东西)。换句话说:不应该保留任何元素(空列表,[]
)。
使用 SWISH 测试:
?- test(['1', '2', '3', '.', '2', '3', '.', '2', '.' , '2']).
true;
false.
?- test(['1', '2', '3', '.', '2', '3', '.', '2', '.' ]).
false.
最简单的方法是使用 DCG 语法来指定虚线四边形的外观。 IPv4 地址为 4 个八位字节,以十进制表示,并以“.”分隔。我们可以将其表示为这样的 DCG 规则:
dotted_quad --> octet, dot, octet, dot, octet, dot, octet.
.
的规则很简单:
dot --> ['.'].
八位字节的规则更复杂。一个八位字节被限制在域 0-255 中。用十进制表示可以是
- 0-9:
一位小数0
–9
- 10-99:
两位小数,第一个在域1
–9
,第二个在域0
–9
. - 100-199:
三位小数。第一个是1
,第二个和第三个在域0
–9
. 中
- 200-249:
三位小数。第一个是2
,第二个的域是0
–4
,第三个是0
–9
. - 250–255:
三位小数。第一个是2
,第二个是5
,第三个被限制在0
–5
.
我们可以编写很多特殊规则来解决这个问题,或者为十进制数字的规则添加一些上下文。让我们为该规则添加一些上下文(因为为什么输入的内容比您需要的多):
digit(D) --> [D].
digit(Min,Max) --> [D], { D @>= Min, D @=< Max }.
这些让我们说 digit('0','5')
表示允许域 0-5 中的数字,或者 digit('2')
表示只允许数字 2。
现在我们已经可以定义八位字节的规则了:
octet --> digit('0','9') .
octet --> digit('1','9') , digit('0','9').
octet --> digit('1') , digit('0','9') , digit('0','9').
octet --> digit('2') , digit('0','4') , digit('0','9').
octet --> digit('2') , digit('5') , digit('0','5').
现在我们可以定义测试谓词了。它需要做的就是调用我们定义的 DCG:
is_dotted_quad(Cs) :- phrase(dotted_quad, Cs).
但是自从输入类似
的内容后[ '1','2','3', '.', '2','3','4', '.', '7','8', '.', '9' ]`
繁琐且容易出错,让我们添加一些便利让我们使用字符串或原子:
is_dotted_quad(X) :- atom(X) , atom_chars(X,Cs) , is_dotted_quad(Cs) .
is_dotted_quad(X) :- string(X) , atom_chars(X,Cs) , is_dotted_quad(Cs) .
is_dotted_quad(Cs) :- phrase(dotted_quad, Cs).
将所有内容放在一起,您会得到:
is_dotted_quad(X) :- atom(X) , atom_chars(X,Cs) , is_dotted_quad(Cs).
is_dotted_quad(X) :- string(X) , string_chars(X,Cs) , is_dotted_quad(Cs).
is_dotted_quad(Cs) :- phrase(dotted_quad, Cs).
dotted_quad --> octet, dot, octet, dot, octet, dot, octet.
dot --> ['.'].
octet --> digit('0','9') .
octet --> digit('1','9') , digit('0','9') .
octet --> digit('1') , digit('0','9') , digit('0','9').
octet --> digit('2') , digit('0','4') , digit('0','9').
octet --> digit('2') , digit('5') , digit('0','5').
digit(V) --> [V].
digit(Min,Max) --> [D], { D @>= Min, D @=< Max }.
您可以 fiddle 在 https://swish.swi-prolog.org/p/dotted-quad-dcg.pl
使用它或者,如果您不想使用 DCG,那么 @Dudas 的回答的重复部分也可以——但应该知道这基本上就是上面的 DCG(DCG 本质上是一个简单的香草序言之上的一点语法糖),只是有点不那么明确的意图:
is_dotted_quad(X) :- atom(X) , atom_chars(X,Cs) , is_dotted_quad(Cs).
is_dotted_quad(X) :- string(X) , string_chars(X,Cs) , is_dotted_quad(Cs).
is_dotted_quad(Cs) :- octet(Cs,T1),
dot(T1,T2), octet(T2,T3),
dot(T3,T4), octet(T4,T5),
dot(T5,T6), octet(T6,[]).
chars( [] , [] ) .
chars( [C|Cs] , [C|Cs] ) .
chars( X , Cs ) :- atom(X) , atom_chars(X,Cs) .
chars( X , Cs ) :- string(X) , string_chars(X,Cs) .
dot(['.'|T],T).
octet( [ Z | T ], T ) :- range(Z,'0','9') .
octet( [ Y , Z | T ], T ) :- range(Y,'1','9') , range(Z,'0','9') .
octet( [ '1' , Y , Z | T ], T ) :- range(Y,'0','9') , range(Z,'0','9') .
octet( [ '2' , Y , Z | T ], T ) :- range(Y,'0','4') , range(Z,'0','9') .
octet( [ '2' , '5' , Z | T ], T ) :- range(Z,'0','5') .
range(X,Min,Max) :- X @>= Min, X @=< Max .