应该编写文本处理DCG来处理代码还是字符?或两者?
在 Prolog 中,传统上有两种表示字符序列的方法:
- 作为字符列表,它们是长度为 1 的原子。
- 作为代码列表,它们只是整数。整数将被解释为代码点,但未指定要应用的约定。作为一个(非常理智的)例子,在 SWI-Prolog 中,代码点的空间是Unicode(因此,大致上,代码点整数范围从 0 到 0x10FFFF)。
DCG 是一种编写从左到右列表处理代码的符号方式,旨在对“分解文本列表”进行解析。根据偏好,待处理的列表可以是字符列表或代码列表。但是,在写下常量时,字符/代码处理的表示法有所不同。通常以“字符样式”或“代码样式”编写 DCG 吗?或者甚至在模块导出 DCG 非终端的情况下采用字符/代码样式以实现可移植性?
有些研究
以下符号可用于表示 DCG 中的常数
'a': 一个字符(像往常一样:单引号表示一个原子,如果标记以小写字母开头,它们可以被省略。)0'a:该代码的a。['a','b']: 一个字符列表。[ 0'a, 0'b ]:列表码,即编码a和b(这样就可以避免在实际的代码点值打字)。"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).
代码风格
尝试识别“代码样式”中的“任何数字”:
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).
字符/代码透明样式
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).
上面还接受一系列交替的代码和字符(因为不能输入内容列表)。这可能不是问题)。当生成,一会就搞定任意字符/代码混合这是不必要的,然后需要添加削减。
带附加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).
使用方言特征的字符/代码透明样式
或者,可以使用 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)].
?- 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)].
因此,可以编写此代码以实现清晰的字符/代码透明度:
特别是在 SWI-Prolog 中
默认情况下,SWI-Prolog 更喜欢代码。尝试这个:
旗帜
back_quotesdouble_quotes
影响的解释"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).
但是,在 DCG 中,默认情况下,"string"和`string`都被解释为“代码”。
没有更改任何设置,请考虑此 DCG:
?- string("foo"),+atom("foo"),+is_list("foo").
true.
?- L=`foo`.
L = [102,111,111].
以上哪一个匹配代码?
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
以上哪个匹配字符?
?-
findall(X,
(atom_codes(bar,Codes),
phrase(representation(X),Codes,[])),
Reps).
Reps = [double_quotes,back_quotes,explicit_codes_1,explicit_codes_2].
当用swipl --traditional反引号表示开始 swipl 时被拒绝Syntax error: Operator expected ,否则没有任何变化。
回答
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.
所以 SWI-Prolog 默认不是 Prolog。没关系,但是如果您想了解 SWI-Prolog 的非 Prolog 行为,请调整问题上的标签。
根据定义,即使在符合 Prolog的情况下,默认情况下双引号列表也是完全无用的:它们可能表示原子,因此无论字符/代码的区别如何,您甚至无法知道双引号列表实际上是一个列表。如果“列表”实际上是一个原子,即使 DCG 只关心“文本”的结构属性(例如,它是否是回文)也是无用的。
因此,想要使用 DCG 处理文本的 Prolog 程序必须在启动时将double_quotes标志强制为它想要的值。您可以在代码和字符之间进行选择。代码与字符相比没有任何优势,但它们在可读性和可键入性方面确实存在劣势。因此:
答案:使用字符。double_quotes明确设置标志。
- *you can't even know that the double quoted list is actually a list* that is incorrect. The default value for the flag `double_quotes` is **implementation defined**. So you do know what `"abc"` means prior to changing the flag. You need to read the accompanying documentation first. That holds also for many other aspects. Like the supported processor character set, or the kind of integers supported etc.