1
votes

Est-il possible d'incrémenter / compter de 0 à n sans accumulateur avec SWI-Prolog?

J'essaie de compter de 0 à N, une valeur retournée à la fois, plutôt qu'une liste comme avec numlist / 3. Je pense qu'il est possible d'utiliser la récursivité avec deux déclarations de prédicat, mais j'ai commencé à trouver une condition d'arrêt qui fonctionne de la manière que je recherche:

iterate(Y,N) â‹€ (Y < N) â‹€ (X is Y + 1)

Avec ma compréhension actuelle, le premier prédicat renverra toujours 0 comme première valeur lors de l'appel de iterate (X, N), et le second itérera à travers des appels récursifs donnant la valeur suivante.

Le problème que j'ai rencontré est qu'il compte correctement jusqu'à N, mais atteint ensuite la limite de pile. Je pense que cela est dû au fait de faire l'appel récursif au début du prédicat et de vérifier le résultat du retour par la suite.

La principale déconnexion semble être ma compréhension de la façon dont Prolog traite les prédicats. D'après ce que j'ai lu, il semble que Prolog gère le deuxième appel comme suit:

iterate(0,_).
iterate(X,N) :- iterate(Y,N), Y < N, X is Y + 1.

Je pensais que cela signifierait que lorsque Y <N retourne false, l'appel récursif s'arrêterait, ce qui n'est pas le cas. À ce stade, j'ai essayé quelques variantes de l'emplacement d'appel récursif dans le prédicat, toujours avec la valeur finale de X déclarée à la fin, car il semble que c'est là que la déclaration de valeur doit aller.

J'ai également vu qu'ISO-Prolog a (;) / 2 (if-then-else), mais je n'ai rien trouvé de similaire qui puisse être utile dans SWI-Prolog qui pourrait s'appliquer à ce cas.

Ai-je tort de penser qu'il est possible de faire cela avec deux déclarations de prédicat dans SWI-Prolog?

Edit: J'ai l'intention de faire cela sans accumulateur pour un défi supplémentaire.


3 commentaires

Quel est le problème avec l'utilisation d'un accumulateur?


Notez que le ; n'est pas le si-alors-sinon mais le "OU". Le "si-alors-autre" est le ->


Correct. Dans SWI-Prolog ; est "OR", mais dans ISO-Prolog c'est l'instruction "if-then-else". Merci de m'avoir dirigé vers l'opérateur -> dans SWI, cela m'a aidé à résoudre mon problème!


5 Réponses :


0
votes

Voici ma démarche: -

printSeries(_,0):-!.
printSeries(S,C):-
    S1 is S+1,
    C1 is C-1,
    writeln(S),
    printSeries(S1,C1). 

?-printSeries(1,10).
1
2
3
4
5
6
7
8
9
10
1true

?-printSeries(15,10).
15
16
17
18
19
20
21
22
23
24
1true

?-printSeries(0,10).
0
1
2
3
4
5
6
7
8
9
1true



?-printSeries(0,5).
0
1
2
3
4
1true


1 commentaires

Cela "fonctionne", mais imprimer les réponses au lieu de les produire en liant des variables n'est pas la manière habituelle de Prolog. Vraisemblablement, la personne qui pose la question voudra faire quelque chose avec les nombres énumérés. C'est difficile à faire quand ils n'existent pas en tant que données à l'intérieur du programme, uniquement en tant que pixels sur un écran.



1
votes

Je pensais que cela signifierait que lorsque Y < N renvoie false, l'appel récursif s'arrêterait, ce qui n'est pas le cas.

Prolog évalue de gauche à droite (ou bien de haut en bas), donc il fera d' abord un appel récursif, et quand cet appel est réussi, alors il vérifiera la partie suivante Y < N , donc en l'utilisant de cette façon, il sera en effet rester coincé dans une boucle infinie, car il continuera à faire plus d'appels récursifs qui finiront par échouer au test Y < N , mais rien n'empêche Prolog de faire un nouvel appel récursif.

Vous pouvez ainsi échanger la commande contre:

?- iterate(10, 15, R).
R = 10 ;
R = 11 ;
R = 12 ;
R = 13 ;
R = 14 ;
R = 15 ;
false.

Cela nous donne alors:

iterate(I, J, I) :-
    I =< J.
iterate(I, J, R) :-
    I < J,
    I1 is I + 1,
    iterate(I1, J, R).

Cela signifie donc que dans un intervalle [I .. J] , I est un membre de cet intervalle (première clause), et si I < J , alors les éléments de [I+1 .. J] sont également membres de cet intervalle (deuxième clause).


0 commentaires

0
votes

C'est beaucoup de questions en une.

Commençons par un programme:

Vous voulez donc compter de 0 au maximum, ici donné par N

une valeur renvoyée à la fois

Il est difficile de juger ce que cela signifie que prédicats Prolog ne sont pas « les valeurs de retour » - ils ne réussissent ou échouent (ou jettent une exception) , alors que peut - être des variables de liaison aux valeurs pour le prochain prédicat à utiliser après la , .

Mais supposons que nous voulions simplement imprimer la séquence de nombres. Ensuite:

?- print_them(12).
I'm at 0
I'm at 1
I'm at 2
I'm at 3
I'm at 4
I'm at 5
I'm at 6
I'm at 7
I'm at 8
I'm at 9
I'm at 10
I'm at 11
I'm at max: 12
true ;              <--- success but maybe there are other solutions?
false.              <--- nah, actually not

Ce qui précède est-il plus clair?

Et donc:

print_them(Max) :-               % Predicate to "count up to Max"
   pth2(Max,0).                  % It calls a a helper predicate that counts ...
                                 % ... up to "Max" starting at 0

% ---
% pth2(+Max,+C)                  % Predicate: "Count to Max starting at C"
% ---

pth2(Max,Max) :-                     % We will stop with Max == C
   format("I'm at max: ~d\n",[Max]). % Just print something in that case.

pth2(Max,C) :-                       % Case of Max not necessarily being C
   C < Max,                          % "guard": only proceed "rightwards" if C < Max
   format("I'm at ~d\n",[C]),        % and then some printing
   Cp is C + 1,                      % Evaluation: C is known, so compute a Cp
   pth2(Max,Cp).                     % ...and then count to Max from Cp.

pth2(Max,C) :-                       % Case of Max not necessarily being C, again
   C > Max,                          % "guard": only proceed "rightwards" if C > Max
   throw("C exceeds Max").           % And then throw an exception


1 commentaires

Cela résout définitivement la question que j'ai posée, merci David d'avoir aidé avec ma question! J'ai voté pour.



0
votes

Ce dont vous semblez vraiment avoir besoin se situe between/3 . Comme ça:

?- between(1, 9, X), succ(X0, X), forall(between(2, X0, Y), X rem Y =\= 0).
X = 1 ;
X = 2 ;
X = 3 ;
X = 5 ;
X = 7 ;
false.

between/3 est un fichier intégré. Il est relativement difficile de programmer soi-même et de couvrir tous les cas limites.

Notez que vous pouvez également filtrer les résultats, par exemple uniquement les nombres impairs:

?- between(0, 9, X), X rem 2 =:= 1.
X = 1 ;
X = 3 ;
X = 5 ;
X = 7 ;
X = 9.

Ou n'imprimez naïvement que les nombres premiers:

?- between(0, 4, X).
X = 0 ;
X = 1 ;
X = 2 ;
X = 3 ;
X = 4.

?- forall(between(0, 4, X), format("~w~n", [X])).
0
1
2
3
4
true.


0 commentaires

0
votes

J'ai fini par trouver un moyen de compter de 0 à n sans accumulateur en modifiant mon extrait de code initial. Alors que David Tonhofer a résolu le problème posé, j'aimerais publier ma solution qui résout mon objectif de défi supplémentaire.

?- iterate(X,10).
X = 0 ;
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
X = 5 ;
X = 6 ;
X = 7 ;
X = 8 ;
X = 9 ;
X = 10 ;
true.

Cet extrait de code permet d'appeler iterate (X, N), avec N étant un nombre réel et X une variable, et de faire parcourir à X toutes les valeurs de 0 à N inclusivement. Cela permet de tester chaque valeur entière de 0 à N par rapport aux équations et de trouver des solutions.

Il renverra les valeurs ci-dessous lorsque iterate (X, N) est appelé:

iterate(0,_).
iterate(X,N) :- iterate(Y,N), (Y < N -> X is Y + 1; !).


0 commentaires