如何在序言中定义字符串连接运算符?
How to define a string concatenation operator in prolog?
我在 prolog 中操作字符串,我想避免在我的规则中使用太多的临时变量。
我想改造成这样:
process_str(Str, Next) :-
is_valid_pattern0(Pattern0),
concat(Pattern0, Tail0, Str),
concat("->", Tail1, Tail0),
is_valid_pattern1(Pattern1),
concat(Pattern1, Tail2, Tail1),
concat("|", Tail2, Next).
使用串联运算符定义,类似于:
process_str(Pattern0.."->"..Pattern1.."|"..Next, Next) :-
is_valid_pattern0(Pattern0),
is_valid_pattern1(Pattern1).
我认为这会更具可读性,但会根据运算符的定义方式增加一些操作。
我发现文档谈到 defining operators,但据我了解,只能定义谓词运算符,而不是可以“return 一个值”的函数运算符(例如+
运算符)。
请告诉我为什么我错了或者如何定义这样的连接运算符。
您可以在 ~/.swiplrc 中定义以下 对 运算符:
:- op(699,xfx,:=). % just below =
:- op(698,yfx,++). % just below :=
Out := Left ++ Right :-
flatten_expr_to_string(Left,LStrings),
flatten_expr_to_string(Right,RStrings),
atomics_to_string([LStrings,RStrings],Out).
flatten_expr_to_string(A++B,String) :-
String := A ++ B.
flatten_expr_to_string(Term,String) :-
maplist(integer,Term)
-> string_codes(String,Term)
; term_string(Term,String).
然后
?- X:=`foo`++bar++help.
X = "foobarhelp" .
?- X:=`foo`++123+bar++help.
X = "foo123+barhelp" .
请注意,关于整数列表存在 歧义(因为反引号字符串就是这样......)。希望你能忍受...
如果您使用 XPCE 编辑器,只需拉出菜单 [Edit\Prolog preferences] 并在其中添加片段,然后重新编译 (Ctrl+b)。这也适用于 Windows,其中 ~/.swiplrc
以另一种方式命名,更适合平台。
当您为字符串表达式定义了合适的迷你语言后,您可以探索 term rewriting, to enable passing expressions to your predicates, without introducing new variables. Beware that's rather difficult to debug... you can analyze lifter
in this repo 以获得一些提示
这是一个使用 DCG 和 term_expansion
的解决方案,适用于 SWI-Prolog。一、基础知识:
:- set_prolog_flag(double_quotes, chars).
这确保 "foo"
将被解释为三个字符的列表,而不是某些 non-standard 原子“字符串”对象。
然后,我们假设有效的 pattern0
和 pattern1
匹配只是字母列表。由您来填写详细信息。一些基本的 DCG:
letters -->
[].
letters -->
[Letter],
{ char_type(Letter, alpha) },
letters.
pattern0 -->
letters.
pattern1 -->
letters.
例如:
?- phrase(pattern0, Pattern0).
Pattern0 = [] ;
Pattern0 = ['A'] ;
Pattern0 = ['A', 'A'] ;
Pattern0 = ['A', 'A', 'A'] ;
Pattern0 = ['A', 'A', 'A', 'A'] ;
Pattern0 = ['A', 'A', 'A', 'A', 'A'] .
?- phrase(pattern0, "helloworld").
true.
此外,方便的 DCG 仅描述了一个列表:
list([]) -->
[].
list([X | Xs]) -->
[X],
list(Xs).
这个好像作用不大,不过一会儿就派上用场了:
?- phrase(list([a, b, c]), List).
List = [a, b, c].
?- phrase(list(List), [a, b, c]).
List = [a, b, c] ;
false.
现在,您想定义一个复合模式,如 Pattern0.."->"..Pattern1.."|"..Next
。我建议写得有点不同,即 sub-patterns: [pattern0, "->", pattern1, "|", Next]
的列表。这样的列表可能包含三种元素:
- DCG 规则名称
- 文字字符列表
- 可能绑定到字符列表的变量
然后我们可以编写一个匹配某些复合模式的 DCG:
composite([]) -->
[].
composite([Head | Tail]) -->
{ atom(Head) },
% Assume that this atom is the name of another DCG rule, and execute it.
call(Head),
composite(Tail).
composite([Head | Tail]) -->
list(Head),
composite(Tail).
这表示复合模式仅描述其 sub-patterns 所描述的序列。它只有两个处理 sub-patterns 的子句:一个用于 DCG 规则名称(由原子表示),一个用于字符列表。变量的大小写由字符列表子句自动处理!
我们可以使用这个定义来匹配像 "foo->bar|baz"
这样的字符列表与复合模式:
?- phrase(composite([pattern0, "->", pattern1, "|", Next]), "foo->bar|baz").
Next = [b, a, z] ;
false.
快完成了!我们可以将其封装在封装模式的定义中:
process_str(Sequence, Next) :-
phrase(composite([pattern0, "->", pattern1, "|", Next]), Sequence).
这是这样工作的:
?- process_str("foo->bar|baz", Next).
Next = [b, a, z] ;
false.
我觉得这个已经很不错了。但是如果你真的想要一种模式匹配语法,term_expansion
会有所帮助。它的使用(看似)简单:为 term_expansion(SomeTermPattern, SomeOtherTerm)
定义一个子句,每个匹配 SomeTermPattern
的子句定义都将被视为程序员编写了 SomeOtherTerm
。所以:
term_expansion(
% Replace every definition of this form:
patterned_process_str(Pattern, Next),
% by a replacement like this:
patterned_process_str(Sequence, Next) :-
phrase(composite(Pattern), Sequence)
).
patterned_process_str([pattern0, "->", pattern1, "|", Next], Next).
我们可以查看 patterned_process_str
源代码的 Prolog 内部表示,以确保它符合预期:
?- listing(patterned_process_str).
patterned_process_str(B, A) :-
phrase(composite([pattern0, [-, >], pattern1, ['|'], A]), B).
变量名丢失了,但我们对 patterned_process_str
的定义被扩展为我们想要的形式,即我们在上面为 process_str
编写的相同形式。这个定义和上面的 process_str
完全一样(因为它是等价的):
?- patterned_process_str("foo->bar|baz", Next).
Next = [b, a, z] ;
false.
练习: 为 ..
提供运算符定义。编写一个谓词 pattern_list
,在带有 ..
的“点模式”和“列表模式”之间进行转换,例如:pattern_list(A..B..C, [A, B, C])
应该会成功。然后,将上面的 term_expansion
规则扩展为允许您直接使用“点模式”语法编写 patterned_process_str
。
我在 prolog 中操作字符串,我想避免在我的规则中使用太多的临时变量。
我想改造成这样:
process_str(Str, Next) :-
is_valid_pattern0(Pattern0),
concat(Pattern0, Tail0, Str),
concat("->", Tail1, Tail0),
is_valid_pattern1(Pattern1),
concat(Pattern1, Tail2, Tail1),
concat("|", Tail2, Next).
使用串联运算符定义,类似于:
process_str(Pattern0.."->"..Pattern1.."|"..Next, Next) :-
is_valid_pattern0(Pattern0),
is_valid_pattern1(Pattern1).
我认为这会更具可读性,但会根据运算符的定义方式增加一些操作。
我发现文档谈到 defining operators,但据我了解,只能定义谓词运算符,而不是可以“return 一个值”的函数运算符(例如+
运算符)。
请告诉我为什么我错了或者如何定义这样的连接运算符。
您可以在 ~/.swiplrc 中定义以下 对 运算符:
:- op(699,xfx,:=). % just below =
:- op(698,yfx,++). % just below :=
Out := Left ++ Right :-
flatten_expr_to_string(Left,LStrings),
flatten_expr_to_string(Right,RStrings),
atomics_to_string([LStrings,RStrings],Out).
flatten_expr_to_string(A++B,String) :-
String := A ++ B.
flatten_expr_to_string(Term,String) :-
maplist(integer,Term)
-> string_codes(String,Term)
; term_string(Term,String).
然后
?- X:=`foo`++bar++help.
X = "foobarhelp" .
?- X:=`foo`++123+bar++help.
X = "foo123+barhelp" .
请注意,关于整数列表存在 歧义(因为反引号字符串就是这样......)。希望你能忍受...
如果您使用 XPCE 编辑器,只需拉出菜单 [Edit\Prolog preferences] 并在其中添加片段,然后重新编译 (Ctrl+b)。这也适用于 Windows,其中 ~/.swiplrc
以另一种方式命名,更适合平台。
当您为字符串表达式定义了合适的迷你语言后,您可以探索 term rewriting, to enable passing expressions to your predicates, without introducing new variables. Beware that's rather difficult to debug... you can analyze lifter
in this repo 以获得一些提示
这是一个使用 DCG 和 term_expansion
的解决方案,适用于 SWI-Prolog。一、基础知识:
:- set_prolog_flag(double_quotes, chars).
这确保 "foo"
将被解释为三个字符的列表,而不是某些 non-standard 原子“字符串”对象。
然后,我们假设有效的 pattern0
和 pattern1
匹配只是字母列表。由您来填写详细信息。一些基本的 DCG:
letters -->
[].
letters -->
[Letter],
{ char_type(Letter, alpha) },
letters.
pattern0 -->
letters.
pattern1 -->
letters.
例如:
?- phrase(pattern0, Pattern0).
Pattern0 = [] ;
Pattern0 = ['A'] ;
Pattern0 = ['A', 'A'] ;
Pattern0 = ['A', 'A', 'A'] ;
Pattern0 = ['A', 'A', 'A', 'A'] ;
Pattern0 = ['A', 'A', 'A', 'A', 'A'] .
?- phrase(pattern0, "helloworld").
true.
此外,方便的 DCG 仅描述了一个列表:
list([]) -->
[].
list([X | Xs]) -->
[X],
list(Xs).
这个好像作用不大,不过一会儿就派上用场了:
?- phrase(list([a, b, c]), List).
List = [a, b, c].
?- phrase(list(List), [a, b, c]).
List = [a, b, c] ;
false.
现在,您想定义一个复合模式,如 Pattern0.."->"..Pattern1.."|"..Next
。我建议写得有点不同,即 sub-patterns: [pattern0, "->", pattern1, "|", Next]
的列表。这样的列表可能包含三种元素:
- DCG 规则名称
- 文字字符列表
- 可能绑定到字符列表的变量
然后我们可以编写一个匹配某些复合模式的 DCG:
composite([]) -->
[].
composite([Head | Tail]) -->
{ atom(Head) },
% Assume that this atom is the name of another DCG rule, and execute it.
call(Head),
composite(Tail).
composite([Head | Tail]) -->
list(Head),
composite(Tail).
这表示复合模式仅描述其 sub-patterns 所描述的序列。它只有两个处理 sub-patterns 的子句:一个用于 DCG 规则名称(由原子表示),一个用于字符列表。变量的大小写由字符列表子句自动处理!
我们可以使用这个定义来匹配像 "foo->bar|baz"
这样的字符列表与复合模式:
?- phrase(composite([pattern0, "->", pattern1, "|", Next]), "foo->bar|baz").
Next = [b, a, z] ;
false.
快完成了!我们可以将其封装在封装模式的定义中:
process_str(Sequence, Next) :-
phrase(composite([pattern0, "->", pattern1, "|", Next]), Sequence).
这是这样工作的:
?- process_str("foo->bar|baz", Next).
Next = [b, a, z] ;
false.
我觉得这个已经很不错了。但是如果你真的想要一种模式匹配语法,term_expansion
会有所帮助。它的使用(看似)简单:为 term_expansion(SomeTermPattern, SomeOtherTerm)
定义一个子句,每个匹配 SomeTermPattern
的子句定义都将被视为程序员编写了 SomeOtherTerm
。所以:
term_expansion(
% Replace every definition of this form:
patterned_process_str(Pattern, Next),
% by a replacement like this:
patterned_process_str(Sequence, Next) :-
phrase(composite(Pattern), Sequence)
).
patterned_process_str([pattern0, "->", pattern1, "|", Next], Next).
我们可以查看 patterned_process_str
源代码的 Prolog 内部表示,以确保它符合预期:
?- listing(patterned_process_str).
patterned_process_str(B, A) :-
phrase(composite([pattern0, [-, >], pattern1, ['|'], A]), B).
变量名丢失了,但我们对 patterned_process_str
的定义被扩展为我们想要的形式,即我们在上面为 process_str
编写的相同形式。这个定义和上面的 process_str
完全一样(因为它是等价的):
?- patterned_process_str("foo->bar|baz", Next).
Next = [b, a, z] ;
false.
练习: 为 ..
提供运算符定义。编写一个谓词 pattern_list
,在带有 ..
的“点模式”和“列表模式”之间进行转换,例如:pattern_list(A..B..C, [A, B, C])
应该会成功。然后,将上面的 term_expansion
规则扩展为允许您直接使用“点模式”语法编写 patterned_process_str
。