Seguinte: Repetição Acima: Programação Imperativa Anterior: Sequenciação
Índice remissivo

Alteração de Dados

A atribuição não está restricta a variáveis. É também possível alterar o conteúdo da maioria dos tipos de dados Lisp. Uma célula cons, por exemplo, pode ser alterada com as funções rplaca e rplacd, significando, respectivamente ``replace-car'' e ``replace-cdr''.

> (let ((x (cons 1 2)))
    (rplaca x 3)
    x)
(3 . 2)

> (let ((x (cons 1 2))) (rplacd x 3) x) (1 . 3)

Note-se que estas funções são uma forma de atribuição e, como tal, destroem o conteúdo anterior da estrutura a que se aplicam. Por este motivo, este género de funções diz-se destrutivo. Na sequência lógica da convenção usual em Lisp para a definição de reconhecedores, que terminam sempre com um ponto de interrogação (ou com a letra ``p'' de predicado), deve-se colocar um ponto de exclamação no fim do nome das funções destrutivas para salientar o seu carácter imperativo.

Exercício 58

Escreva uma função junta! (note-se o ponto de exclamação) que recebe duas listas como argumento e devolve uma lista que é o resultado de as juntar destrutivamente uma à frente da outra, i.e., faz a cauda da primeira lista apontar para a segunda.

Resposta

Exercício 59

Analise o seguinte exemplo funcional e imperativo:

> (let ((x '(1 2 3)))
    (junta x x))
(1 2 3 1 2 3)

> (let ((x '(1 2 3))) (junta! x x)) (1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 ...

Resposta

Exercício 60

Escreva uma função muda-n-esimo! (note-se o ponto de exclamação) que recebe um número n, uma lista e um elemento, e substitui o n-ésimo elemento da lista por aquele elemento. Note que o primeiro elemento da lista corresponde a n igual a zero.

Resposta

Exercício 61

Reescreva as operações do tipo abstracto de informação automóvel que alteravam as características de um elemento do tipo de forma a torná-las destrutivas.

Resposta

Quando as operações de um tipo abstracto alteram um elemento do tipo, essas operações são classificadas como modificadores do tipo abstracto. Os modificadores, como caso especial da atribuição, são muito empregues em programação imperativa. Note-se que os modificadores possuem todos os problemas da atribuição simples, nomeadamente a alteração ser destrutiva. Isto levanta problemas quando se testa igualdade em presença de modificação.

> (let ((x (novo-automovel 'honda 'civic 2)))
    (let ((y (muda-automovel-portas x 4)))
      (eql x y)))
NIL

> (let ((x (novo-automovel 'honda 'civic 2))) (let ((y (muda-automovel-portas! x 4))) (eql x y))) T

Repare-se que no primeiro exemplo (funcional), o automóvel modificado é, logicamente, diferente do automóvel original. x e y representam automóveis diferentes. No segundo exemplo (imperativo), a modificação do automóvel que x representa é realizada sobre esse próprio automóvel, de modo que x e y acabam por representar um mesmo automóvel (modificado). Muito embora esta situação possa ter vantagens, ela permite também a introdução de erros muito subtis e extremamente difíceis de tirar. Para dar apenas um pequeno exemplo, repare-se que o automóvel que x representa passou a ter quatro portas embora uma leitura superficial do código sugira que ele foi criado com apenas duas.


Seguinte: Repetição Acima: Programação Imperativa Anterior: Sequenciação
Índice remissivo

Copyright António Leitão, 1995