Que sont les invariants, comment peuvent-ils être utilisés et l’avez-vous déjà utilisé dans votre programme?

48

Je lis Coders at Work et on y parle beaucoup d'invariants. Pour autant que je sache, un invariant est une condition qui tient à la fois avant et après une expression. Ils sont, entre autres, utiles pour prouver que la boucle est correcte, si je me souviens bien de mon cours de logique.

Ma description est-elle correcte ou ai-je oublié quelque chose? Les avez-vous déjà utilisés dans votre programme? Et si oui, comment ont-ils bénéficié?

Gablin
la source
@ Robert Harvey: Oui, je viens de lire cela en fait. Mais il me semble que les invariants ne sont utiles que lorsque vous essayez de prouver quelque chose. Est-ce correct (sans jeu de mots)?
Gablin
C'est ce que j'ai compris. lorsque vous essayez de raisonner sur votre programme, vous devez prouver son exactitude.
Robert Harvey
3
@ user9094: une assertion est une déclaration selon laquelle quelque chose est vrai à un moment donné de l'exécution et est représentée dans le code. Un invariant est une déclaration (on espère être fondée) qui sera toujours vraie chaque fois qu'il s'applique et qui n'est pas représentée dans le code lui-même.
David Thornley
1
Les invariants sont en effet utiles pour prouver la correction, mais ils ne se limitent pas à ce cas. Ils sont également utiles pour la programmation défensive et lors du débogage. Ils ne font pas que prouver que votre code est correct, ils aident à raisonner sur le code et à localiser les bugs proches des origines.
Oddthinking

Réponses:

41

Dans OOP, un invariant est un ensemble d’assertions qui doivent toujours être vraies pendant la vie d’un objet pour que le programme soit valide. Il doit rester vrai de la fin du constructeur au début du destructeur lorsque l'objet n'exécute pas actuellement une méthode qui modifie son état.

Un exemple d'invariant pourrait être que exactement l'une des deux variables membres devrait être nulle. Ou que si l'un a une valeur donnée, l'ensemble des valeurs autorisées pour l'autre est ceci ou cela ...

J'utilise parfois une fonction membre de l'objet pour vérifier que l'invariant tient. Si ce n'est pas le cas, une affirmation est soulevée. Et la méthode est appelée au début et à la sortie de chaque méthode qui modifie l'objet (en C ++, ce n'est qu'une ligne ...)

Xavier Nodet
la source
11
+1 pour mentionner les invariants n'a pas besoin d'être vrai au milieu d'une méthode en cours d'exécution.
Oddthinking
1
@Oddthinking Il vaut mieux éviter cela lorsque cela est possible. Il serait facile d'entrer dans un état d'invalidation invariable et d'oublier de tout restaurer correctement avant de revenir. Les exceptions peuvent également vous causer des ennuis.
Alexander
3
@Alexander: Pour les invariants non triviaux, il est presque impossible à éviter. Si vous devez mettre à jour plusieurs variables dans une méthode, comme indiqué dans la réponse, il existe un point où une seule a été mise à jour et que l'invariant est false. Il y a suffisamment de contraintes pour écrire un bon code sans en ajouter de nouvelles.
Bizarre pensées le
@ Oddthinking Oui, c'est souvent inévitable. Mais par exemple, s'il existe un groupe de variables logiquement liées (par exemple, un tableau et l'index d'un élément "sélectionné" dans le tableau), il est probablement utile de les extraire dans un type. À partir de là, les mutations du tableau ou du type peuvent être exprimées sous la forme d'une seule affectation d'une nouvelle instance de ce type
Alexander
13

Bien, tout ce que je vois dans ce fil est génial, mais j'ai une définition du terme «invariant» qui m'a été extrêmement utile au travail.

Un invariant est une règle logique à respecter tout au long de l'exécution de votre programme qui peut être communiquée à un humain, mais pas à votre compilateur.

Cette définition est utile car elle divise les conditions en deux groupes: ceux que le compilateur peut faire confiance et ceux qui doivent être documentés, discutés, commentés ou autrement communiqués aux contributeurs pour qu'ils puissent interagir avec la base de code sans introduire de bogues. .

En outre, cette définition est utile car elle vous permet d’utiliser la généralisation "Les invariants sont mauvais".

Par exemple, le levier de vitesses d'une voiture à transmission manuelle est conçu pour éviter les invariants. Si je le voulais, je pourrais construire une transmission avec un levier pour chaque vitesse. Ce levier peut être en avant ("engagé") ou en arrière ("désengagé"). Dans un tel système, j'ai créé un "invariant", qui pourrait être documenté comme tel:

"Il est essentiel que le rapport actuellement engagé soit désengagé avant qu'un autre rapport soit engagé. Engager deux rapports simultanément en même temps entraînera des contraintes mécaniques qui déchireront la transmission. Désengagez toujours le rapport actuellement engagé avant d'en engager un autre."

Et ainsi, on pourrait blâmer les transmissions cassées sur une conduite bâclée. Cependant, les voitures modernes utilisent un seul bâton qui pivote entre les engrenages. Il est conçu de telle sorte que, sur une voiture moderne à levier de vitesse, il ne soit pas possible d'engager deux vitesses en même temps.

De cette manière, on pourrait dire que la transmission a été conçue pour «supprimer l'invariant», car elle ne se permet pas d'être configurée de manière mécanique d'une manière non conforme à la règle logique.

Chaque invariant de ce type que vous supprimez de votre code constitue une amélioration, car il réduit la charge cognitive liée à son utilisation.

Daniel Burbank
la source
1
Si un invariant est une règle logique à respecter tout au long de l'exécution de votre programme et que votre règle logique est de ne pas engager deux engrenages en même temps, alors l'invariant n'est-il pas que deux engrenages ne peuvent être engagés en même temps temps? Sans cet invariant, votre transmission pourrait être à deux vitesses en même temps et se déchirer ainsi. Premièrement, un seul levier de vitesse ne fait-il pas appliquer cet invariant? Deuxièmement, pourquoi un invariant serait-il intrinsèquement bon ou mauvais?
Dustin Cleveland
1
La comparaison avec la vitesse de la voiture est très claire pour moi. Merci!
Marecky
"Un invariant est une règle logique à laquelle on doit obéir tout au long de l'exécution de votre programme et qui peut être communiquée à un humain, mais pas à votre compilateur." - J'aime bien ça, concis et facile à retenir.
ZeroKnight
@DustinCleveland, je pense que dans cet exemple, les mécanismes derrière le changement de bâton sont le "compilateur" qui "applique" les règles, alors que le conducteur qui pourrait autrement causer un incident est l'un des nombreux clients qui doivent consommer et se souvenir des informations été "documentée, discutée, commentée ou autrement communiquée".
ebernard le
Brillante explication! Je comprends vraiment maintenant le raisonnement sur la raison pour laquelle avoir des invariants dans votre code est une mauvaise pratique.
Ben C Wang
3

Un invariant (dans le sens commun) signifie certaines conditions qui doivent être vérifiées à un moment donné ou même toujours pendant l'exécution de votre programme. Par exemple, les pré-conditions et les post-conditions peuvent être utilisées pour affirmer certaines conditions qui doivent être vraies quand une fonction est appelée et quand elle revient. Les invariants d'objet peuvent être utilisés pour affirmer qu'un objet doit avoir un état valide tout au long de son existence. C’est le principe de la conception par contrat.
J'ai utilisé des invariants de manière informelle à l'aide de vérifications dans le code. Mais plus récemment, je joue avec la bibliothèque de contrats de code pour .Net qui prend directement en charge les invariants.

Softveda
la source
3

Basé sur la citation suivante de Coders At Work ...

Mais une fois que vous connaissez l'invariant qu'il maintient, vous pouvez voir, ah, si nous maintenons cet invariant, nous aurons le temps de recherche du journal.

... j'imagine "invariant" = "condition que vous souhaitez conserver pour assurer l'effet désiré".

Il semble que l'invariant a deux sens qui diffèrent de manière subtile:

  1. Quelque chose qui reste le même.
  2. Quelque chose que vous essayez de garder identique afin d'atteindre l'objectif X (tel qu'un "temps de recherche de journal" ci-dessus).

Donc, 1 est comme une affirmation; 2 est comme un outil permettant de prouver l'exactitude, la performance ou d'autres propriétés - je pense. Voir l'article Wikipedia pour un exemple de 2 (prouvant l'exactitude de la solution du casse-tête MU).

En fait, un 3ème sens d'invariant est:

.3. Ce que le programme (ou module ou fonction) est censé faire; en d'autres termes, son but.

De la même interview de Coders At Work:

Mais ce qui rend un gros logiciel gérable, c'est d'avoir des invariants globaux ou des déclarations globales sur ce qu'il est censé faire et ce qui est censé être vrai.

Jonathan Aquino
la source
1

Un invariant est comme une règle ou une hypothèse qui peut être utilisée pour dicter la logique de votre programme.

Par exemple, supposons que vous ayez une application logicielle qui garde une trace des comptes d'utilisateurs. Supposons également que l'utilisateur puisse avoir plusieurs comptes, mais pour une raison quelconque, vous devez faire la différence entre le compte principal d'un utilisateur et les comptes "alias".

Cela peut être un enregistrement de base de données ou autre chose, mais pour le moment supposons que chaque compte utilisateur est représenté par un objet de classe.

class userAccount {private char * pUserName; caractère privé * pParentAccountUserName;

...}

Un invariant peut être la supposition que si pParentAccountUserName est NULL ou vide, cet objet est le compte parent. Vous pouvez utiliser cet invariant pour distinguer différents types de compte. Il existe probablement de meilleures méthodes pour distinguer différents types de comptes d'utilisateur. N'oubliez donc pas qu'il s'agit d'un exemple illustrant l'utilisation possible d'un invariant.

Pemdas
la source
Les invariants vérifient l'état d'un programme. Ce ne sont pas des décisions de conception.
Xavier Nodet
3
Les invariants ne vérifient rien. Vous pouvez vérifier l'état du programme pour voir si un invariant est VRAI ou FAUX, mais les invariants eux-mêmes ne "font" rien.
Pemdas
2
En général, en C ++, vous verrez une sorte d'invariance de classe telle que le membre x doit être inférieur à 25 et supérieur à 0. C'est l'invariant. Toutes les vérifications effectuées sur cet invariant sont des assertions. Dans l'exemple ci-dessus, mon invariant est que si pParentAccountUserName est NULL ou vide, il s'agit d'un compte parent. Les invariants sont des décisions conçues.
Pemdas
Comment vérifier que si pParentAccountUserName est NULL ou vide, cet objet est le compte parent? Votre déclaration définit uniquement ce qu'une valeur nulle / vide est supposée représenter. L'invariant est que le système s'y conforme, c'est-à-dire que pParentAccountUserName ne peut être nul ou vide que s'il s'agit d'un compte parent. C'est une distinction subtile.
Cameron
1

Venant de la physique, nous avons en physique des invariants, qui sont essentiellement des quantités qui ne varient pas d’un calcul à l’autre. Par exemple, en physique, l'énergie totale est conservée pour un système fermé. Ou encore en physique, si deux particules entrent en collision, les fragments résultants doivent contenir exactement l'énergie avec laquelle ils ont commencé et le même moment (une quantité vectorielle). Habituellement, il n'y a pas assez d'invariants pour spécifier totalement le résultat. Par exemple, dans la collision à 2 particules, nous avons quatre invariants, trois composantes de moment et une composante d'énergie, mais le système a six degrés de liberté (six nombres pour décrire son état). Les invariants doivent être conservés avec une erreur d'arrondi proche, mais leur conservation ne prouve pas que la solution est correcte.

En règle générale, ces éléments sont importants en tant que contrôles de cohérence, mais ils ne peuvent à eux seuls prouver qu'ils sont corrects.

Omega Centauri
la source
1
-1 Les invariants en physique sont différents. Calculer une solution n'est pas la même chose que prouver qu'un algorithme est correct. Pour ces derniers, les invariants peuvent prouver l'exactitude.
aaronasterling