在 Prolog 中,传统上有两种表示字符序列的方法:
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).
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 更喜欢代码。尝试这个:
旗帜
影响的解释"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
,否则没有任何变化。
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
明确设置标志。