Обратимые предикаты и строки в SWI-Prolog

append/3 это очень мощный предикат. Предположим, я хочу предикат, который работает так же, но для строк SWI-Prolog.

Самый простой подход, который я вижу, - преобразовать эти строки в списки с string_codes/2, а затем применить append/3затем используйте string_codes/2 назад. Большая проблема с этим подходом заключается в том, что string_codes/2 не работает, если обе переменные не объединены.

Вот очень уродливое решение, которое я придумал, который проверяет, какие строки объединены для применения string_codes/2 при необходимости:

append_strings(S1, S2, S3) :-
    nonvar(S1),
    nonvar(S2),!,
    string_codes(S1, A),
    string_codes(S2, B),
    append(A,B,C),
    string_codes(S3, C).

append_strings(S1, S2, S3) :-
    nonvar(S1),
    nonvar(S3),!,
    string_codes(S1, A),
    string_codes(S3, C),
    append(A,B,C),
    string_codes(S2, B).

append_strings(S1, S2, S3) :-
    nonvar(S2),
    nonvar(S3),!,
    string_codes(S2, B),
    string_codes(S3, C),
    append(A,B,C),
    string_codes(S1, A).

append_strings(S1, S2, S3) :-
    nonvar(S3),
    string_codes(S3, C),
    append(A,B,C),
    string_codes(S1, A),
    string_codes(S2, B).

Это дает правильные результаты для следующих случаев:

?- append_strings("test","auie","testauie").
true.

?- append_strings("test",A,"testauie").
A = "auie".

?- append_strings(A,"auie","testauie").
A = "test" ;
false.

?- append_strings(A,B,"testauie").
A = "",
B = "testauie" ;
A = "t",
B = "estauie" ;
A = "te",
B = "stauie" ;
A = "tes",
B = "tauie" ;
A = "test",
B = "auie" ;
A = "testa",
B = "uie" ;
A = "testau",
B = "ie" ;
A = "testaui",
B = "e" ;
A = "testauie",
B = "" ;
false.

Неужели нет способа упростить ситуацию? Предположим, я хочу создать целую кучу предикатов, которые будут работать со строками так же, как со списками: очевидно, я не хочу писать то, что я сделал для append/3 для всех них. Но я также не хочу работать со строками кода, потому что тогда у меня нет возможности узнать, манипулирую ли я обычным списком или действительно строкой.

4 ответа

Решение

Просто используйте string_concat/3. Как и ISO atom_concat/3, он может использоваться во многих режимах, включая (-,-,+).

Поскольку предикат работает со списками, мне кажется заманчивым использовать DCG. Для начала давайте заметим, что строки в Прологе - это действительно списки кодов символов:

   ?- X="test".
X = [116,101,115,116]

Конечно, это не очень читабельно, поэтому давайте посмотрим сами символы на их коды:

   ?- set_prolog_flag(double_quotes,chars).
yes
   ?- X="test".
X = [t,e,s,t]

Так-то лучше. Размышляя об отношении, которое должен описывать предикат, я выбираю описательное имя, например list_list_appended/3. Этот предикат имеет одну цель: правило dcg, назовем его list_list//2, которое использует другой dcg, назовем его list // 2, чтобы фактически написать списки:

list_list_appended(L1,L2,L3) :-
    phrase(list_list(L1,L2),L3).   % L3 is L1+L2

list([]) -->                       % if the list is empty ...
    [].                            % ... there's nothing in the list
list([X|Xs]) -->                   % if there's a head element ...
    [X],                           % ... it's in the list
    list(Xs).                      % the tail is also a list

list_list(L1,L2) -->               % the list consists of ...
    list(L1),                      % ... L1 followed by ...
    list(L2).                      % L2

Ваш пример запросов:

   ?- list_list_appended("test","auie","testauie").
yes
   ?- list_list_appended(L1,"auie","testauie").
L1 = [t,e,s,t] ? ;
no
   ?- list_list_appended("test",L2,"testauie").
L2 = [a,u,i,e] ? ;
no
   ?- list_list_appended("test","auie",L3).
L3 = [t,e,s,t,a,u,i,e]
   ?- list_list_appended(L1,L2,"testauie").
L1 = [],
L2 = [t,e,s,t,a,u,i,e] ? ;
L1 = [t],
L2 = [e,s,t,a,u,i,e] ? ;
L1 = [t,e],
L2 = [s,t,a,u,i,e] ? ;
L1 = [t,e,s],
L2 = [t,a,u,i,e] ? ;
L1 = [t,e,s,t],
L2 = [a,u,i,e] ? ;
L1 = [t,e,s,t,a],
L2 = [u,i,e] ? ;
L1 = [t,e,s,t,a,u],
L2 = [i,e] ? ;
L1 = [t,e,s,t,a,u,i],
L2 = [e] ? ;
L1 = [t,e,s,t,a,u,i,e],
L2 = [] ? ;
no

Как пользователь SWI вы также можете использовать эту библиотеку в сочетании с set_prolog_flag(double_quotes,chars). чтобы получить вывод в желаемой форме. Обратитесь к этому ответу для деталей.

Был подобный вопрос некоторое время назад, я покажу свое предложение, пересмотренный

:- meta_predicate when_(0).
when_(P) :-
    strip_module(P,_,Q), Q =.. [_|As],
    or_list(As, Exp), % hurry debugging :-) display(Exp),
    when(Exp, P).

or_list([A], ground(A)) :- !.
or_list([A|As], (ground(A);Exp)) :- or_list(As, Exp).

append_strings(S1, S2, S3) :-
    maplist(when_, [string_codes(S1, A), string_codes(S2, B), append(A,B,C), string_codes(S3, C)]).

Если вам интересно, я могу добавить оператор, чтобы скрыть детали синтаксиса, чтобы получить что-то вроде

append_strings(S1, S2, S3) -:-
    string_codes(S1, A), string_codes(S2, B), append(A,B,C), string_codes(S3, C).

Это более компактное определение:

append_strings(S1, S2, S3):-
  append_strings1(S1, L1, [1]-[], N1),
  append_strings1(S2, L2, [1|N1]-N1, N2),
  append_strings1(S3, L3, [1,1|N2]-N2, N3),
  (N3\=[_,_|_] ->instantiation_error(append_strings/3); true),
  append(L1, L2, L3),
  (ground(S1)->true;string_codes(S1, L1)),
  (ground(S2)->true;string_codes(S2, L2)),
  (ground(S3)->true;string_codes(S3, L3)).

append_strings1(S, L, G-NG, N):-
  (ground(S) -> (string_codes(S, L), N=G) ; N=NG).

Он проверяет, является ли каждый аргумент заземленным, и пытается преобразовать его в коды, затем проверяет, является ли третий аргумент заземленным, или два других аргумента, и выдает ошибку создания экземпляра, если условия не выполняются.

После добавления он преобразует обратно в строковые аргументы, которые не основаны.

Другие вопросы по тегам