使用 Prolog 检查 IP 地址列表

Checking IP like address list with Prolog

我正在尝试构建一个给定列表(例如 ['1', '2', '3', '.', ... , '2'])的谓词,它必须将其识别为 (NNN.NNN.NNN.NNN) 格式的 IP 字符串,其中 N 是一个数字(在 09 之间)它 return 如果格式正确则为真。它还必须接受 IP 仅为(N.N.N.NNNN.NNN.N.N 和其他组合)的情况。

这是我目前所做的

test([X,Y,Z,D | String]) :- digit(X), digit(Y), digit(Z), dot(D), test(String).

其中数字是一个事实列表,例如 digit('0').09;点是定义为 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:
    一位小数 09
  • 10-99:
    两位小数,第一个在域19,第二个在域09.
  • 100-199:
    三位小数。第一个是 1,第二个和第三个在域 09.
  • 200-249:
    三位小数。第一个是 2,第二个的域是 04,第三个是 09.
  • 250–255:
    三位小数。第一个是2,第二个是5,第三个被限制在05.

我们可以编写很多特殊规则来解决这个问题,或者为十进制数字的规则添加一些上下文。让我们为该规则添加一些上下文(因为为什么输入的内容比您需要的多):

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 .