2016-04-15 2 views
4

append/3 est un prédicat très puissant. Supposons que je veuille un prédicat qui fonctionne de la même manière mais pour les chaînes de SWI-Prolog.Prédicats et chaînes réversibles dans SWI-Prolog

L'approche la plus simple que je vois est de transformer ces chaînes en listes avec string_codes/2, puis d'appliquer append/3, puis d'utiliser string_codes/2 retour. Le gros problème de cette approche est que string_codes/2 ne fonctionne pas si les deux variables ne sont pas unifiées.

Voici une solution extrêmement laide je suis venu avec, qui vérifie que les chaînes sont unifiés à appliquer string_codes/2 en cas de besoin:

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). 

Cela donne des résultats corrects pour les cas suivants:

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

N'y a-t-il vraiment aucun moyen de simplifier les choses? Supposons que je veux faire un tas de prédicats qui fonctionnent avec des chaînes comme ils le feraient avec des listes: je ne veux évidemment pas avoir à écrire ce que j'ai fait pour append/3 pour chacun d'entre eux. Mais je ne veux pas non plus travailler avec des chaînes de code parce que je n'ai aucun moyen de savoir si je manipule une liste normale ou vraiment une chaîne.

+1

La méthode la plus simple consiste à ne pas utiliser les chaînes directement. Au lieu de cela, utilisez [chars] (http://stackoverflow.com/a/36645725/772868). – false

+1

Votre définition échoue pour 'append_strings (Xs, Ys, Zs)'. Il devrait plutôt produire une erreur d'instanciation. – false

+0

@false Mais alors je ne peux pas faire la différence entre une liste et une chaîne. Supposons que je souhaite une transposition de prédicat qui transpose une liste, mais pour une chaîne, elle transpose selon les sauts de ligne. Avec une liste de caractères, je n'ai aucun moyen de savoir si je travaille avec une chaîne ou non. – Fatalize

Répondre

3

Utilisez simplement string_concat/3. Comme ISO atom_concat/3, il peut être utilisé dans de nombreux modes, y compris (-, -, +).

1

il y a eu un similar question il y a quelque temps, je montrerai ma proposition, révisée

:- 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)]). 

Si vous êtes intéressé, je peux ajouter un opérateur pour cacher les détails de la syntaxe, pour obtenir quelque chose comme

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

Ceci est une définition plus compacte:

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). 

Il vérifie si chaque argument est broyé et essaie de convertir en codes, puis vérifie si le troisième argument est au sol ou les deux autres sont, et lance une erreur d'instanciation si les conditions ne sont pas remplies.

Après l'ajout, il est converti en arguments de chaîne qui n'ont pas été mis à la masse.

4

Puisque le prédicat travaille sur des listes, il me semble tentant d'utiliser des DCG. Tout d'abord Observons que les chaînes en Prolog sont vraiment des listes de codes de caractères:

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

Bien sûr, ce n'est pas très lisible, donc nous allons voir les personnages eux-mêmes intead de leurs codes:

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

Voilà qui est mieux. En pensant à la relation que le prédicat devrait décrire, j'opte pour un nom descriptif comme list_list_appended/3. Ce prédicat a un but: une dcg règle, appelons-le list_list // 2, qui utilise un autre dcg, nous allons l'appeler la liste // 2, d'écrire effectivement les listes:

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 

Vos exemples de requêtes:

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

en tant qu'utilisateur SWI, vous pouvez également utiliser this library en combinaison avec set_prolog_flag(double_quotes,chars). pour obtenir la sortie sous forme souhaitée. Reportez-vous au this answer pour plus de détails.