A abstração de dados é uma forma de aumentar a modularidade. Se decidirmos implementar números racionais, teremos de pensar em combinar dois números--o numerador e o denominador, e de os tratar como um todo. Se não fosse possível considerar aquela combinação de números como uma abstração (um racional), toda a sua utilização seria extremamente difícil. Por exemplo, para se somar dois números racionais, seria necessário usar uma operação para o cálculo do númerador, e outra operação para o cálculo do denominador, em vez de se pensar numa operação genérica, soma-racional, que receberia dois argumentos-- dois racionais--e calcularia um terceiro número--um racional.
Para nos abstrairmos da complexidade de um número racional, devemos definir funções que os manipulam internamente. Podemos começar por definir uma função que constroi um número racional a partir do numerador e do denominador.
(defun racional (numerador denominador) (cons numerador denominador))
Para sabermos qual é o numerador ou o denominador de um dado número racional podemos definir:
(defun numerador (racional) (car racional))(defun denominador (racional) (cdr racional))
Assim, já já podemos escrever a função
que calcula a soma de dois racionais, usando a fórmula
.
(defun +racional (r1 r2) (racional (+ (* (numerador r1) (denominador r2)) (* (numerador r2) (denominador r1))) (* (denominador r1) (denominador r2))))
Para simplificar a escrita de racionais, podemos definir uma função que escreve um racional de acordo com uma convenção qualquer.
(defun escreve-racional (racional) (format t " a/ a" (numerador racional) (denominador racional)))
Agora, já podemos calcular a seguinte expressão:
> (escreve-racional (+racional (racional 1 2) (racional 1 3))) 5/6
Exercício 34
Defina as restantes funções do tipo abstracto de informação racional: -racional, *racional, e /racional.
Como se vê, tratamos um número racional como um só objecto, e separamos a parte do programa que usa racionais da parte que os implementa como pares de inteiros. Esta técnica designa-se por abstração de dados.
A abstração é a melhor maneira de lidar com a complexidade. A abstração de dados permite-nos isolar a utilização dos dados do modo como eles estão implementados, através da utilização de barreiras de abstração. Essas barreiras consistem em limitar a utilização dos dados a um pequeno conjunto de funções (racional, numerador e denominador) que escondem a maneira como eles estão implementados. Ao utilizador de um dado tipo de dados, apenas se diz quais as funções que ele pode usar para os manipular, e não qual o funcionamento das funções que implementam aquele tipo de dados.
Seguindo esta metodologia, se precisarmos de testar a igualdade de racionais, devemos escrever uma função que o faça usando apenas as funções de manipulação de racionais, i.e., racional, numerador e denominador:
(defun =racional (r1 r2) (and (= (numerador r1) (numerador r2)) (= (denominador r1) (denominador r2))))
Exercício 35
A função que compara dois racionais não funciona correctamente para todos os casos. Assim,
> (=racional (racional 4 6) (racional 4 6)) t > (=racional (racional 4 6) (racional 2 3)) nilQual é o problema? Como é que se pode resolver?
Exercício 36
Escreva uma função que calcule o maior divisor comum entre dois números. Para isso, use o algoritmo de Euclides que diz que se r é o resto da divisão de a por b, então o maior divisor comum entre a e b é também o maior divisor comum entre b e r: mdc(a,b)=mdc(b,r). Como é natural, quando o resto é zero, o maior divisor comum é o próprio b.
Exercício 37
Empregue o método de Euclides para reescrever a função racional de modo a só construir números na forma reduzida.