web-dev-qa-db-fra.com

Somme des éléments de la liste dans Prolog

list_sum([], 0).
list_sum([Head | Tail], TotalSum) :-
    list_sum(Tail, Sum1),
    Total = Head + Sum1.

Ce code retourne true. Si je remplace Total = Head + Sum1 par Total is Head + Sum1, la valeur sera renvoyée. Mais ce que je devrais le remplacer pour obtenir le résultat comme ceci:

?- list_sum([1,2,0,3], Sum).
Sum = 1+2+0+3 ; % not to return value 6!!!
9
user721588

Notez que dans la deuxième clause de votre procédure, TotalSum n'est jamais instancié. Vous devriez avoir reçu un avertissement de l'interprète lors de la consultation de votre code.

Voici ma suggestion:

list_sum([Item], Item).
list_sum([Item1,Item2 | Tail], Total) :-
    list_sum([Item1+Item2|Tail], Total).

La première clause traite du cas de base, lorsqu'il ne reste qu'un élément dans la liste, c'est votre résultat.

La deuxième clause traite de l'étape de récursivité. Il récupère les deux premiers éléments de la liste et effectue un appel récursif en remplaçant ces deux éléments par un nouveau terme Item1 + Item2.

8
gusbro

La réponse est simple:

sum_list([], 0).
sum_list([H|T], Sum) :-
   sum_list(T, Rest),
   Sum is H + Rest.

Ce code ne fonctionne que dans un sens - cela signifie - il ne vous permet pas de générer des listes avec cette somme spécifique. Mais puisque l'ensemble de ces listes est infini, ce ne serait de toute façon pas pratique.

21
Rok Kralj

Dans Prolog, (+)/2 est un opérateur infix binaire. Cela nous permet d'écrire A+B au lieu de +(A,B).

? - current_op (_,yfx,+). % opérateur infixe binaire associatif gauche 
 vrai. 

(+)/2 s'associe à gauche, donc 1+2+3 est un raccourci pour (1+2)+3.

(.)/2 s'associe au right , donc [1,2,3] est l'abréviation de .(1,.(2,.(3,[]))).

Pour que la parenthèse soit correcte, nous utilisons un prédicat auxiliaire avec un argument supplémentaire "accumulator":

list_sum([X|Xs],S) :-
   list_sum0_sum(Xs,X,S).

list_sum0_sum([],    S ,S).
list_sum0_sum([X|Xs],S0,S) :-
   list_sum0_sum(Xs,S0+X,S).

Exemple de requête:

?- list_sum([1,2,0,3],S).
S = 1+2+0+3.
2
repeat

Le programme est

list_sum([],0).

list_sum([Head|Tail], TotalSum):-
list_sum(Tail, Sum1),
TotalSum is Head+Sum1.

maintenant si la requête est 

?- list_sum([1,2,3,4], Sum).

la réponse est

Sum = 10
1
Rohan

Si vous voulez transformer une liste de nombres en une expression additive, à partir de

[1,2,3]

à

1 + 2 + 3

vous pourriez faire quelque chose comme ça, en utilisant quelque chose comme une liste de différences :

list_to_additive_expr( [] , 0 ).
list_to_additive_expr( [X|Xs] , X + RHS ) :-
  sum_of( Xs , RHS ).

Vous pouvez également utiliser un accumulateur :

list_to_additive_expr( Xs , Expr ) :-
  list_to_additive_expr( Xs , 0 , Expr )
  .

list_to_additive_expr( []     , Expr , Expr ) .
list_to_additive_expr( [X|Xs] , RHS , Expr ) :-
  sum_of( Xs , X + RHS , Expr )
  .

Je pense que vous constaterez que le premier style n'est pas correctement tail récursif et ne sera donc pas optimisé dans une boucle via tail récursion optimisation (TRO) - et ainsi de suite, si la liste est suffisamment long, aura un débordement de pile. La deuxième approche devrait avoir TRO appliqué et devrait fonctionner pour des listes de n'importe quelle longueur.

Qu'est-ce que TRO, pourriez-vous demander? Voici Wikipedia avec une réponse pour vous :

En informatique, un appel final est un appel de sous-programme qui se produit dans une autre procédure Et qui produit une valeur de retour, qui est ensuite renvoyée immédiatement par la procédure appelante . On dit alors que le site d’appel est en queue de ligne, c’est-à-dire à la fin de la procédure d’appel. Si un sous-programme effectue un appel final à lui-même, il est appelé Tail-récursif. C'est un cas particulier de récursivité.

Les appels différés sont importants car ils peuvent être mis en œuvre sans ajouter de nouvelle trame de pile À la pile d'appels. La plupart de la trame de la procédure actuelle n'est plus nécessaire, elle peut être remplacée par la trame de l'appel final, modifiée selon les besoins (Similaire à la superposition pour les processus, mais pour la fonction appels). Le programme peut alors passer à Le sous-programme appelé. La production d'un tel code au lieu d'une séquence d'appels standard s'appelle Élimination des appels de fin, ou optimisation des appels de fin.

1
Nicholas Carey