如何使用Prolog在两个字符之间找到子字符串?

时间:2016-04-27 17:32:55

标签: string list prolog swi-prolog

我有一个变量被传递给谓词,这是一个字符串列表。

从列表中的每个字符串,我想提取最深的括号集之间的子字符串,并创建所有这些子字符串的列表。

例如:

  • 输入:

    ["Canidae(Canis(C. lupus(C. l. familiaris)))", "Felidae(Felinae(Felis(F. catus)))", "Equidae(Equus(E. ferus(E. f. caballus)))"]
    
  • 输出继电器:

    ["C. l. familiaris", "F. catus", "E. f. caballus"]
    

(我使用生物分类等级作为一个例子,因为它们在结构上与我的实际数据相似)

最后,每组括号的深度是未知的,最深的子串始终是开括号和闭括号之间的唯一子串。

感谢您对此的帮助,我是Prolog的新手,所以思维方式略有不同。我一直试图解决这个问题一段时间,但我无法解决这个问题。

2 个答案:

答案 0 :(得分:4)

我建议使用DCG,并查找/ 3:

par(L, Content, R) -->
  left(L), inner(L, Content, R).

left(P) --> P.
left(P) --> [_], left(P).

inner(_, [], R) --> R.
inner(L, [C|Cs], R) -->
  \+ L, \+ R, [C], inner(L, Cs, R).

?- findall(A,(phrase(par("[",C,"]"),`[a[b][cd]]e`,_),atom_codes(A,C)),L).
L = [b, cd].

请注意_作为短语/ 3的最后一个参数。它通过findall / 3启用列表构建。

您可以使用任何字符串作为左/右括号。

答案 1 :(得分:1)

我们最好先解决单个字符串的问题,而不是解决列表问题。为了计算最深的子串,我们可以 - 如果我正确理解了规范 - 计算最后一个左括号的位置。我们假设您已使用例如string_codes/2将字符串转换为ASCII代码列表。此处的左括号包含代码40

last_opening(L,X) :-
    last_opening(L,0,0,X).

last_opening([],J,_,J).
last_opening([40|T],_,I,X) :-
    !,
    I1 is I+1,
    last_opening(T,I1,I1,X).
last_opening([_|T],J,I,X) :-
    I1 is I+1,
    last_opening(T,J,I1,X).

例如,您的第一个例子:

?- string_codes("Canidae(Canis(C. lupus(C. l. familiaris)))",L),last_opening(L,X).
L = [67, 97, 110, 105, 100, 97, 101, 40, 67|...],
X = 23.

它说我们必须从位置23开始提取:

Canidae(Canis(C. lupus(C. l. familiaris)))
                       ^ here

一旦我们知道最深的子字符串的起始位置,我们就可以提取字符串:我们只需要在列表的末尾停止,或者在代码41处停止,无论是什么先出现:

extract_substring(L,0,S) :-
    !,
    extract_substring2(L,S).
extract_substring([_|T],N,S) :-
    N1 is N-1,
    extract_substring(T,N1,S).

extract_substring2([],[]).
extract_substring2([41|_],[]) :-
    !.
extract_substring2([L|T],[L|U]) :-
    extract_substring2(T,U).

例如:

?- string_codes("Canidae(Canis(C. lupus(C. l. familiaris)))",L),last_opening(L,X),extract_substring(L,X,T),string_codes(St,T).
L = [67, 97, 110, 105, 100, 97, 101, 40, 67|...],
X = 23,
T = [67, 46, 32, 108, 46, 32, 102, 97, 109|...],
St = "C. l. familiaris".

现在我们可以编写一个自动调用string_codes的谓词:

deepest_string(S,T) :-
    string_codes(S,CS),
    last_opening(CS,X),
    extract_substring(CS,X,CT),
    string_codes(T,CT).

例如:

?- deepest_string("Canidae(Canis(C. lupus(C. l. familiaris)))",L).
L = "C. l. familiaris".

总之,我们只需要在列表上实现该功能:

deepest_string_list([],[]).
deepest_string_list([S|ST],[T|TT]) :-
    deepest_string(S,T),
    deepest_string_list(ST,TT).

导致:

?- deepest_string_list(["Canidae(Canis(C. lupus(C. l. familiaris)))", "Felidae(Felinae(Felis(F. catus)))", "Equidae(Equus(E. ferus(E. f. caballus)))"],T).
T = ["C. l. familiaris", "F. catus", "E. f. caballus"].

如果你想改变字符,你可以简单地查找它们的ASCII等价物,而不是4041