O que são invariantes, como eles podem ser usados, e você já o usou em seu programa?


48

Estou lendo Coders at Work , e há muita conversa sobre invariantes. Até onde eu entendi, uma invariante é uma condição que vale antes e depois de uma expressão. Eles são, entre outras coisas, úteis para provar que o loop está correto, se me lembro corretamente do meu curso de lógica.

Minha descrição está correta ou perdi alguma coisa? Você já os usou em seu programa? E se sim, como eles se beneficiaram?



@ Robert Harvey: Sim, acabei de ler isso na verdade. Mas parece-me que os invariantes são úteis apenas quando você tenta provar alguma coisa. Isso está correto (sem trocadilhos)?
gablin

Essa é a minha compreensão; quando você está tentando argumentar sobre seu programa, para provar sua correção.
Robert Harvey

3
@ user9094: Uma asserção é uma declaração de que algo é verdadeiro em um ponto específico do tempo de execução e está representado no código. Uma invariante é uma afirmação (espera-se fundamentada) que sempre será verdadeira sempre que aplicável, e não está representada no próprio código.
David Thornley

1
Os invariantes são realmente úteis para provar a correção, mas não se limitam a esse caso. Eles também são úteis para programação defensiva e durante a depuração. Eles não apenas ajudam a provar que seu código está correto, mas também ajudam a raciocinar sobre o código e encontrar a localização dos bugs próximos às origens.
Oddthinking 31/12/10

Respostas:


41

No OOP, invariável é um conjunto de asserções que sempre devem ser verdadeiras durante a vida de um objeto para que o programa seja válido. Ele deve ser verdadeiro desde o final do construtor até o início do destruidor sempre que o objeto não estiver executando atualmente um método que altera seu estado.

Um exemplo de invariante pode ser que exatamente uma das duas variáveis ​​de membro seja nula. Ou que, se um tiver um determinado valor, o conjunto de valores permitidos para o outro é este ou aquele ...

Em algum momento, uso uma função de membro do objeto para verificar se o invariante é válido. Se não for esse o caso, é feita uma afirmação. E o método é chamado no início e na saída de cada método que altera o objeto (em C ++, essa é apenas uma linha ...)


11
O +1 para mencionar invariantes não precisa ser verdadeiro no meio de um método de execução.
Oddthinking 31/12/10

1
Melhor pensar em evitar isso quando possível. Seria fácil entrar em um estado invariável e esquecer de restaurar tudo corretamente antes de retornar. Exceções também podem causar problemas.
Alexander Alexander

3
@ Alexander: Para invariantes não triviais, é quase impossível evitar. Se você precisar atualizar mais de uma variável em um método, conforme descrito na resposta, há um ponto em que apenas uma foi atualizada e o invariante é falso. Existem restrições suficientes para escrever um bom código sem adicionar novos.
Oddthinking

@ Pensando bem Sim, muitas vezes é inevitável. Mas, por exemplo, se houver um grupo de variáveis ​​que pertencem logicamente (por exemplo, uma matriz e o índice de um item "selecionado" na matriz), provavelmente vale a pena extraí-las para um tipo. A partir daí, as mutações da matriz ou do tipo podem ser expressas como uma única atribuição de uma nova instância desse tipo.
Alexander

13

Bem, as coisas que estou vendo neste tópico são ótimas, mas tenho uma definição de 'invariável' que foi tremendamente útil para mim no trabalho.

Invariável é qualquer regra lógica que deve ser obedecida durante a execução do seu programa que pode ser comunicada a um ser humano, mas não ao seu compilador.

Essa definição é útil porque separa as condições em dois grupos: aquelas em que o compilador pode confiar, e aquelas que devem ser documentadas, discutidas, comentadas ou comunicadas aos colaboradores para que eles interajam com a base de código sem introduzir bugs. .

Além disso, essa definição é útil porque permite que você use a generalização "Invariantes são ruins".

Como exemplo, o câmbio em um carro de transmissão manual é projetado para evitar uma invariância. Se eu quisesse, eu poderia construir uma transmissão com uma alavanca para cada marcha. Essa alavanca pode estar para a frente ("engatada") ou para trás ("desengatada"). Nesse sistema, criei um "invariável", que pode ser documentado da seguinte maneira:

"É fundamental que a engrenagem atualmente engatada seja desengatada antes de uma engrenagem diferente ser engatada. O engate de duas engrenagens ao mesmo tempo causará estresse mecânico que rasgará a transmissão. Sempre desengate a engrenagem atualmente engatada antes de engatar outra."

E assim, pode-se culpar transmissões quebradas em direção desleixada. Os carros modernos, no entanto, usam um único manípulo que gira entre as engrenagens. Ele foi projetado de tal maneira que, em um carro moderno, não é possível engatar duas marchas ao mesmo tempo.

Dessa maneira, poderíamos dizer que a transmissão foi projetada para 'remover o invariante', porque não se permite ser mecanicamente configurada de maneira a violar a regra lógica.

Cada invariante desse tipo que você remove do seu código é uma melhoria, pois diminui a carga cognitiva de trabalhar com ele.


1
Se uma invariável é qualquer regra lógica que deve ser obedecida durante a execução do seu programa, e sua regra lógica é que não há duas engrenagens engatadas ao mesmo tempo, não é invariável que nenhuma engrenagem possa ser engatada ao mesmo tempo Tempo? Sem esse invariante, sua transmissão poderia estar em duas marchas ao mesmo tempo e, assim, se separar. Primeiro, um único manípulo de mudança não está realmente impondo esse invariante? Segundo, por que um invariante seria intrinsecamente bom ou ruim?
Dustin Cleveland

1
A comparação com as marchas do carro é muito clara para mim. Obrigado!
Marecky

"Invariável é qualquer regra lógica que deve ser obedecida durante a execução do seu programa que pode ser comunicada a um ser humano, mas não ao seu compilador." - Eu gosto disso, conciso e fácil de lembrar.
ZeroKnight 22/08/19

@DustinCleveland Acho que neste exemplo, os mecanismos por trás do deslocamento da vara são o 'compilador' que 'impõe' as regras, enquanto o driver que poderia causar um incidente é um dos muitos clientes que devem consumir e lembrar as informações contidas. foi "documentado, discutido, comentado ou comunicado de outra forma".
ebernard 16/07

Explicação brilhante! Eu realmente entendo agora o raciocínio sobre por que ter invariantes em seu código é uma prática ruim.
Ben C Wang

3

Um invariante (no senso comum) significa algumas condições que devem ser verdadeiras em algum momento ou mesmo sempre enquanto o programa está sendo executado. por exemplo, PreConditions e PostConditions podem ser usadas para afirmar algumas condições que devem ser verdadeiras quando uma função é chamada e quando ela retorna. Invariantes de objetos podem ser usados ​​para afirmar que um objeto deve ter um estado válido durante todo o tempo em que existe. Esse é o princípio do projeto por contrato.
Eu usei invariantes informalmente usando verificações no código. Mais recentemente, porém, estou brincando com a biblioteca de contratos de código para .Net que suporta diretamente invariantes.


3

Baseado na seguinte citação de Coders At Work ...

Mas depois que você souber o invariante que ele está mantendo, você poderá ver, ah, se mantivermos esse invariante, obteremos tempo de pesquisa de log.

... Eu acho que "invariável" = "condição que você deseja manter para garantir o efeito desejado".

Parece que invariante tem dois sentidos que diferem de maneira sutil:

  1. Algo que permanece o mesmo.
  2. Algo que você está tentando manter o mesmo, para atingir a meta X (como um "tempo de pesquisa de log" acima).

Então 1 é como uma afirmação; 2 é como uma ferramenta para provar a correção, desempenho ou outras propriedades - eu acho. Veja o artigo da Wikipedia para obter um exemplo de 2 (comprovando a correção da solução do quebra-cabeça da MU).

Na verdade, um terceiro sentido de invariante é:

.3 O que o programa (ou módulo ou função) deve fazer; em outras palavras, seu propósito.

Da mesma entrevista do Coders At Work:

Mas o que torna um grande software gerenciável é ter alguns invariantes globais ou declarações gerais sobre o que ele deve fazer e o que as coisas devem ser verdadeiras.


1

Um invariante é como uma regra ou suposição que pode ser usada para ditar a lógica do seu programa.

Por exemplo, suponha que você tenha algum aplicativo de software que rastreie as contas de usuário. Suponha também que o usuário possa ter várias contas, mas por qualquer motivo, você precisa diferenciar entre a conta principal de um usuário e as contas de "alias".

Pode ser um registro de banco de dados ou algo mais, mas, por enquanto, vamos supor que cada conta de usuário seja representada por um objeto de classe.

classe userAccount {private char * pUserName; char privado * pParentAccountUserName;

...}

Uma invariável pode ser a suposição de que, se pParentAccountUserName for NULL ou vazio, esse objeto será a conta pai. Você pode usar esse invariante para distinguir diferentes tipos de conta. Provavelmente, existem métodos melhores para distinguir diferentes tipos de contas de usuário; lembre-se de que este é apenas um exemplo para mostrar como uma invariável pode ser usada.


Invariantes verificam o estado de um programa. Eles não são decisões de design.
Xavier Nodet

3
Invariantes não conferem nada. Você pode verificar o estado do programa para ver se uma invariante é VERDADEIRA ou FALSA, mas os próprios invariantes "não fazem" nada.
Pemdas

2
Normalmente, em C ++ você veria algum tipo de invariância de classe, como o membro x deve ser menor que 25 e maior que 0. Essa é a invariável. Quaisquer verificações contra essa invariante são asserções. No exemplo que tenho acima, minha invariável é se pParentAccountUserName for NULL ou vazio, então é uma conta pai. Invariantes são decisões projetadas.
Pemdas

Como você verifica se pParentAccountUserName é NULL ou vazio, esse objeto é a conta pai? Sua declaração define apenas o que um valor nulo / vazio deve representar. O invariante é que o sistema esteja em conformidade com isso, ou seja, que pParentAccountUserName só pode ser nulo ou vazio se for uma conta pai. É uma distinção sutil.
Cameron

1

Vindo de uma formação em física, na física temos invariantes, que são essencialmente quantidades que não variam ao longo de uma computação / simulação inteira. Por exemplo, na física, para um sistema fechado, a energia total é conservada. Ou ainda na física, se duas partículas colidem, os fragmentos resultantes devem conter exatamente a energia com a qual começaram e exatamente o mesmo momento (uma quantidade vetorial). Geralmente, não há invariantes suficientes para especificar totalmente o resultado. Por exemplo, na colisão de duas partículas, temos quatro invariantes, três componentes de momento e um componente de energia, mas o sistema tem seis graus de liberdade (seis números para descrever seu estado). Os invariantes devem ser conservados dentro de um erro de arredondamento, mas sua conservação não prova que a solução esteja correta.

Normalmente, essas coisas são importantes como verificações de sanidade, mas elas mesmas não podem provar a correção.


1
-1 Invariantes em física são diferentes. Calcular uma solução não é o mesmo que provar que um algoritmo está correto. Para o último, invariantes podem provar a correção.
aaronasterling
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.