【问题标题】:How to define a string concatenation operator in prolog?如何在 prolog 中定义字符串连接运算符?
【发布时间】:2020-09-21 20:00:55
【问题描述】:

我在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,但据我了解,只能定义谓词运算符,而不是可以“返回值”的函数运算符(例如+ 运算符)。

请告诉我为什么我错了或如何定义这样的连接运算符。

【问题讨论】:

  • 考虑dcg!不过,它仅适用于字符列表而不是字符串。

标签: prolog operators string-concatenation


【解决方案1】:

这是一个使用 DCG 和 term_expansion 的解决方案,适用于 SWI-Prolog。一、基础:

:- set_prolog_flag(double_quotes, chars).

这确保"foo" 将被解释为三个字符的列表,而不是一些非标准的原子“字符串”对象。

然后,假设有效的pattern0pattern1 匹配只是字母列表。由您来填写详细信息。一些基本的 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 这样的复合模式。我建议以不同的方式编写它,即作为子模式列表:[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).

这表示复合模式仅描述其子模式所描述的任何内容的序列。它只有两个处理子模式的子句:一个用于 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).

我们可以查看 Prolog 对 patterned_process_str 源代码的内部表示,以确保它符合预期:

?- 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

【讨论】:

    【解决方案2】:

    您可以在 ~/.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] 并在那里添加 sn-p,然后重新编译 (Ctrl+b)。这也适用于 Windows,其中~/.swiplrc 以另一种方式命名,更适合该平台。

    当您为字符串表达式定义了适当的迷你语言后,您可以探索term rewriting,以便将表达式传递给您的谓词, 不会引入新变量。注意调试起来相当困难...您可以分析this repo 中的lifter 以获得一些提示

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-27
      • 1970-01-01
      相关资源
      最近更新 更多