应该编写文本处理 DCG 来处理代码还是字符?或两者?

Dav*_*fer 6 prolog dcg

在 Prolog 中,传统上有两种表示字符序列的方法:

  • 作为字符列表,它们是长度为 1 的原子。
  • 作为代码列表,它们只是整数。整数将被解释为代码点,但未指定要应用的约定。作为一个(非常理智的)例子,在 SWI-Prolog 中,代码点的空间是Unicode(因此,大致上,代码点整数范围从 0 到 0x10FFFF)。

DCG 是一种编写从左到右列表处理代码的符号方式,旨在对“分解文本列表”进行解析。根据偏好,待处理的列表可以是字符列表或代码列表。但是,在写下常量时,字符/代码处理的表示法有所不同。通常以“字符样式”或“代码样式”编写 DCG 吗?或者甚至在模块导出 DCG 非终端的情况下采用字符/代码样式以实现可移植性?

有些研究

以下符号可用于表示 DCG 中的常数

  • 'a': 一个字符(像往常一样:单引号表示一个原子,如果标记以小写字母开头,它们可以被省略。)
  • 0'a:该代码a
  • ['a','b']: 一个字符列表。
  • [ 0'a, 0'b ]:列表,即编码ab(这样就可以避免在实际的代码点值打字)。
  • "a"代码列表。传统上,双引号字符串被分解为代码列表,这种表示法在 DCG 上下文中也适用于 SWI-Prolog,即使 SWI-Prolog 将“双引号字符串”映射到特殊的字符串数据类型。
  • `0123`. 传统上,反引号内的文本被映射到一个原子(我认为,95 ISO 标准只是避免对反引号字符串的含义进行具体说明。“这将是 ISO/IEC 13211 的这一部分的有效扩展到将反引号字符串定义为表示字符串常量。" )。在 SWI-Prolog 中,反引号内的文本被分解为代码列表,除非该标志back_quotes已设置为要求不同的行为。

例子

字符样式

尝试识别“字符样式”中的“任何数字”并使其“字符表示”在C以下位置可用:

zero(C) --> [C],{C = '0'}. 

nonzero(C) --> [C],{member(C,['1','2','3','4','5','6','7','8','9'])}.

any_digit(C) --> zero(C).
any_digit(C) --> nonzero(C).
Run Code Online (Sandbox Code Playgroud)

代码风格

尝试识别“代码样式”中的“任何数字”:

zero(C) --> [C],{C = 0'0}.

nonzero(C) --> [C],{member(C,[0'1,0'2,0'3,0'4,0'5,0'6,0'7,0'8,0'9])}.

any_digit(C) --> zero(C).
any_digit(C) --> nonzero(C).
Run Code Online (Sandbox Code Playgroud)

字符/代码透明样式

DCG 可以通过复制涉及常量的规则编写为“字符/代码透明样式”。在上面的例子中:

zero(C) --> [C],{C = '0'}. 
zero(C) --> [C],{C = 0'0}.

nonzero(C) --> [C],{member(C,['1','2','3','4','5','6','7','8','9'])}.
nonzero(C) --> [C],{member(C,[0'1,0'2,0'3,0'4,0'5,0'6,0'7,0'8,0'9])}.

any_digit(C) --> zero(C).
any_digit(C) --> nonzero(C).
Run Code Online (Sandbox Code Playgroud)

上面还接受一系列交替的代码和字符(因为不能输入内容列表)。这可能不是问题)。当生成,一会就搞定任意字符/代码混合这是不必要的,然后需要添加削减。

带附加Mode指示器的字符/代码透明样式

另一种方法是明确指示模式。看起来干净:

zero(C,chars) --> [C],{C = '0'}. 
zero(C,codes) --> [C],{C = 0'0}.

nonzero(C,chars) --> [C],{member(C,['1','2','3','4','5','6','7','8','9'])}.
nonzero(C,codes) --> [C],{member(C,[0'1,0'2,0'3,0'4,0'5,0'6,0'7,0'8,0'9])}.

any_digit(C,Mode) --> zero(C,Mode).
any_digit(C,Mode) --> nonzero(C,Mode).
Run Code Online (Sandbox Code Playgroud)

使用方言特征的字符/代码透明样式

或者,可以使用 Prolog 方言的特性来实现字符/代码透明度。在 SWI-Prolog 中,有code_type/2, 它实际上适用于代码和字符(有一个相应的,char_type/2但恕我直言,chary_type/2在任何情况下都应该只适用于字符和代码)和“数字类”代码和字符产生化合物digit(X)

?- code_type(0'9,digit(X)).
X = 9.

?- code_type('9',digit(X)).
X = 9.

?- findall(W,code_type('9',W),B).
B = [alnum,csym,prolog_identifier_continue,ascii,
     digit,graph,to_lower(57),to_upper(57),
     digit(9),xdigit(9)].
Run Code Online (Sandbox Code Playgroud)

因此,可以编写此代码以实现清晰的字符/代码透明度:

?- code_type(0'9,digit(X)).
X = 9.

?- code_type('9',digit(X)).
X = 9.

?- findall(W,code_type('9',W),B).
B = [alnum,csym,prolog_identifier_continue,ascii,
     digit,graph,to_lower(57),to_upper(57),
     digit(9),xdigit(9)].
Run Code Online (Sandbox Code Playgroud)

特别是在 SWI-Prolog 中

默认情况下,SWI-Prolog 更喜欢代码。尝试这个:

旗帜

影响的解释"string",并`string`在“标准代码”。默认情况下"string"被解释为原子“字符串”,而`string`被解释为“代码列表”。

在 DCG 之外,以下在 SWI-Prolog 中保持不变,所有标志都为默认值:

zero(C) --> [C],{code_type(C,digit(0)}. 

nonzero(C) --> [C],{code_type(C,digit(X),X>0}.

any_digit(C) --> zero(C).
any_digit(C) --> nonzero(C).
Run Code Online (Sandbox Code Playgroud)

但是,在 DCG 中,默认情况下,"string"`string`都被解释为“代码”。

没有更改任何设置,请考虑此 DCG:

?- string("foo"),\+atom("foo"),\+is_list("foo").
true.

?- L=`foo`.
L = [102,111,111].
Run Code Online (Sandbox Code Playgroud)

以上哪一个匹配代码?

representation(double_quotes)    --> "bar".            % SWI-Prolog decomposes this into CODES 
representation(back_quotes)      --> `bar`.            % SWI-Prolog decomposes this into CODES
representation(explicit_codes_1) --> [98,97,114].      % explicit CODES (as obtained via atom_codes(bar,Codes))
representation(explicit_codes_2) --> [0'b,0'a,0'r].    % explicit CODES 
representation(explicit_chars)   --> ['b','a','r'].    % explicit CHARS
Run Code Online (Sandbox Code Playgroud)

以上哪个匹配字符?

?- 
findall(X,
   (atom_codes(bar,Codes),
    phrase(representation(X),Codes,[])),
   Reps).

Reps = [double_quotes,back_quotes,explicit_codes_1,explicit_codes_2].
Run Code Online (Sandbox Code Playgroud)

当用swipl --traditional反引号表示开始 swipl 时被拒绝Syntax error: Operator expected ,否则没有任何变化。

Isa*_*bie 6

Prolog 标准 (6.3.7) 说:

双引号列表是原子(6.3.1.3)或列表(6.3.5)。

因此,以下应该成功:

Welcome to SWI-Prolog (threaded, 64 bits, version 7.6.4)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?- Foo = "foo", (atom(Foo) ; Foo = [F, O, O]).
false.
Run Code Online (Sandbox Code Playgroud)

所以 SWI-Prolog 默认不是 Prolog。没关系,但是如果您想了解 SWI-Prolog 的非 Prolog 行为,请调整问题上的标签。

根据定义,即使在符合 Prolog的情况下,默认情况下双引号列表也是完全无用的:它们可能表示原子,因此无论字符/代码的区别如何,您甚至无法知道双引号列表实际上是一个列表。如果“列表”实际上是一个原子,即使 DCG 只关心“文本”的结构属性(例如,它是否是回文)也是无用的。

因此,想要使用 DCG 处理文本的 Prolog 程序必须在启动时将double_quotes标志强制为它想要的值。您可以在代码和字符之间进行选择。代码与字符相比没有任何优势,但它们在可读性和可键入性方面确实存在劣势。因此:

答案:使用字符。double_quotes明确设置标志。

  • *你甚至不知道双引号列表实际上是一个不正确的列表*。标志“double_quotes”的默认值是**实现定义的**。因此,在更改标志之前,您确实知道“abc”的含义。您需要先阅读随附的文档。这也适用于许多其他方面。比如支持的处理器字符集,或者支持的整数类型等。 (3认同)