3
votes

Prolog convertit les minutes en heures

Voici le code que j'ai créé.

ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:    [9] _3198 is _3204+1
ERROR:    [8] mins_to_hours(80,_3232,_3234) at c:/.../xyz.pl:11
ERROR:    [7] <user>

Cela fonctionne bien lorsque les minutes sont inférieures à 60, par exemple

?- mins_to_hours(80,H,M).

Cependant en essayant de l'exécuter pendant plus de 60 minutes

?- mins_to_hours(20,H,M).
H = 0,
M = 20 ;
false.

il génère une exception

mins_to_hours(In, H, M):-
  In < 60,
  H = 0,
  M is In.
mins_to_hours(In, H, M):-
  In >= 60,
  H is H1+1,
  In1 is In-60,
  mins_to_hours(In1, H1, M).

à la position H est H1 + 1, .

Des idées pour résoudre ce problème?


0 commentaires

3 Réponses :


3
votes

Voici la version corrigée de votre code.

?- mins_to_hours(Total, H, 1), Total = 61, H = 1.
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:   [11] _6584<60
ERROR:   [10] mins_to_hours_helper(0,_6612,_6614,1) at c:/XYZ.pl:23
ERROR:    [8] '<meta-call>'(user:(...,...)) <foreign>
ERROR:    [7] <user>
ERROR: 
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.

Les principaux changements sont:

  1. Pour éviter le message d'erreur (les arguments ne sont pas suffisamment instanciés) puisque votre code est récursif, il a besoin de variables entrantes et sortantes séparées, c'est-à-dire H0 avec H1 , et M0 avec M1 .
  2. Pour pouvoir utiliser les variables supplémentaires, il est nécessaire d'ajouter un prédicat d'assistance, c'est-à-dire mins_to_hours_helper .
  3. Les minutes dans et la variable des minutes de début sont en fait les mêmes dans le prédicat d'assistance.
  4. Étant récursif, le code crée des points de choix, mais la réponse devrait être déterministe. Ceci est résolu par une coupure (!) Dans le cas de base.

Voici quelques cas de test (Utilise SWI-Prolog):

?- run_tests.
% PL-Unit: mins_to_hours ....... done
% All 7 tests passed
true.

Pour exécuter les cas de test:

mins_to_hours(Minutes_in, H, M) :-
    mins_to_hours_helper(0, Minutes_in, H, M).

mins_to_hours_helper(H0, M0, H1, M1):-
  M0 < 60, !,
  H0 = H1,
  M0 = M1.
mins_to_hours_helper(H0, M0, H, M):-
  M0 >= 60,
  H1 is H0+1,
  M1 is M0-60,
  mins_to_hours_helper(H1, M1, H, M).


7 commentaires

Merci, c'est une aide énorme! :)


mins_to_hours (Total, H, 1), Total = 61, H = 1 échoue, mais Total = 61, H = 1, mins_to_hours (Total, H, 1) , réussit.


Vous coupez une unification. Mais les coupes ne fonctionnent proprement qu'avec des tests arithmétiques sûrs.


... ou d'autres prédicats qui produisent une erreur d'instanciation propre en cas d'objectifs trop généraux


Vous n'avez pas besoin de gérer tous les différents modes. Il suffit de produire des erreurs d'instanciation ce qui est assez simple dans ce cas: retarder l'unification après la coupure.


A la place de mins_to_hours_helper (H0, M0, H0, M0): - M0 <60,!. écrivez plutôt mins_to_hours_helper (H0, M0, H1, M1): - M0 <60,! , H0 = H1, M0 = M1.


La déclaration mins_to_hours (-Minutes_in: int, -H: int, + M: int) est det. est incorrecte. Il existe de nombreuses solutions pour M = 23 : H in 0..sup sont toutes des solutions.



3
votes

Une autre version qui pourrait être intéressante serait celle utilisant clpfd:

?- min_hours(600, Hours, Minutes).
Hours = 10,
Minutes = 0.

?- min_hours(TotalHours, 10, 30).
TotalHours = 630.

Cela a l'avantage de fonctionner avec différentes instanciations:

:- use_module(library(clpfd)).

min_hours(TotalMinutes, Hours, Minutes) :- 
    [TotalMinutes, Hours] ins 0..sup, 
    Minutes in 0..59, 
    TotalMinutes #= Hours*60 + Minutes.


3 commentaires

Est-il prévu que vous utilisiez (in) / 2 pour les trois arguments et non (# = <) / 2 et autres? Autrement dit, votre solution est parfaite alors que les prédicats de comparaison ont ici une certaine faiblesse.


@false en tant qu'utilisateur assez récent de clfpd, je suis heureux d'éviter un piège mais je ne peux pas dire que j'avais l'intention de le faire. Quelles sortes de faiblesses ont-ils? Je voudrais ajouter cela à ma réponse.


Lorsque vous utilisez 0 # = & ct, des expressions (c'est-à-dire des foncteurs évaluables) seraient acceptées comme: min_hours (0,0,0 * 0) réussit, alors que min_hours (0,0, M), M = 0 * 0 échoue. Vous n'êtes pas tombé dans ce piège. Il existe une solution générale: (#) / 1 < / code> Il nous manque encore une bonne déclaration d'opérateur (préfixe) pour cela! On pourrait alors écrire #TotalMinutes # = # Hours * 60 + #Minutes



0
votes

Tellement de code que j'ai le vertige.

Pour SWI-Prolog:

mins_to_hours(Min, H, M) :-
    H is Min div 60,
    M is Min mod 60.

Pour tout Prolog:

mins_to_hours(Min, H, M) :-
    divmod(Min, 60, H, M).


0 commentaires