【问题标题】:Checking IP like address list with Prolog使用 Prolog 检查 IP 地址列表
【发布时间】:2021-12-06 09:32:16
【问题描述】:

我正在尝试构建一个谓词,给定一个列表(例如['1', '2', '3', '.', ... , '2']),它必须将其识别为(NNN.NNN.NNN.NNN) 格式的IP 字符串,其中N 是一个数字(在09) 如果格式正确,则返回 true。它还必须接受只有 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 是从09digit('0'). 等事实列表;点是定义为dot('.') 的事实。我的代码缺乏模块化(它无法识别单个N)。你能帮我实现吗?

谢谢!

【问题讨论】:

  • 对于其他发现此问题的人。虽然问题要求 IP(暗示 IPv4),但它没有指定真正的 IPv4 地址。换句话说,如果您在生产代码中为此使用答案,您可能会感到非常抱歉。此外,它不会检查 IPv6,这要困难得多,因为小节可能会被完全删除并且仍然有效。

标签: list parsing prolog swi-prolog


【解决方案1】:

欢迎来到 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 或一个元素是否为数字。列表的其余部分也与第二个参数统一,使其成为数字测试和其余列表的生成器/测试。 digits123/2 单独会是错误的,因为三位数字可能被误认为是 2 或一位数字。所以这个调用使它变得特别:首先在输入列表In 中调用digits123/2,返回带有剩余元素的列表。但是剩下的列表必须以点开头(['.'|R1])。通过这样写,R1 是没有点作为分隔符的剩余列表。再重复两次以检查第 2 块和第 3 块。最后一个块不同:它有 1-3 位数字,但之后没有点(或其他任何东西)。换句话说:不应该留下任何元素(空列表,[])。

使用 SWISH 测试:

?- test(['1', '2', '3', '.', '2', '3', '.', '2', '.' , '2']).
true;
false.

?- test(['1', '2', '3', '.', '2', '3', '.', '2', '.' ]).
false.

【讨论】:

    【解决方案2】:

    最简单的方法是使用 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 }.
    

    您可以在https://swish.swi-prolog.org/p/dotted-quad-dcg.pl 使用它

    或者,如果您不想使用 DCG,那么 @Dudas 的答案的这种即兴表演也可以 - 但应该知道这本质上就是上述 DCG 的内容(DCG 本质上是一个简单的vanilla prolog 之上的语法糖),只是对意图的声明少了一点:

    
    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 .
    
    

    【讨论】:

      猜你喜欢
      • 2021-04-27
      • 2012-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多