Pular para o conteúdo principal

2.1.1 Exemplo: Operações Aritméticas para Números Racionais

Suponha que queremos fazer aritmética com números racionais. Queremos ser capazes de adicioná-los, subtraí-los, multiplicá-los e dividi-los, e testar se dois números racionais são iguais.

Vamos começar assumindo que já temos uma maneira de construir um número racional a partir de um numerador e um denominador. Também assumimos que, dado um número racional, temos uma maneira de extrair (ou selecionar) seu numerador e seu denominador. Vamos supor ainda que o construtor e os seletores estão disponíveis como funções:

  • make_rat(n, d) retorna o número racional cujo numerador é o inteiro n e cujo denominador é o inteiro d.
  • numer(x) retorna o numerador do número racional x.
  • denom(x) retorna o denominador do número racional x.

Estamos usando aqui uma estratégia poderosa de síntese: wishful thinking (pensamento positivo). Ainda não dissemos como um número racional é representado, ou como as funções numer, denom e make_rat devem ser implementadas. Mesmo assim, se tivéssemos essas três funções, poderíamos então adicionar, subtrair, multiplicar, dividir e testar a igualdade usando as seguintes relações:

n1d1+n2d2=n1d2+n2d1d1d2n1d1n2d2=n1d2n2d1d1d2n1d1n2d2=n1n2d1d2n1/d1n2/d2=n1d2d1n2n1d1=n2d2 se e somente se   n1d2 = n2d1\begin{array}{rll} \dfrac{n_{1}}{d_{1}}+\dfrac{n_{2}}{d_{2}} &=&\dfrac{n_{1}d_{2}+n_{2}d_{1}}{d_{1}d_{2}}\\[15pt] \dfrac{n_{1}}{d_{1}}-\dfrac{n_{2}}{d_{2}} &=&\dfrac{n_{1}d_{2}-n_{2}d_{1}}{d_{1}d_{2}}\\[15pt] \dfrac{n_{1}}{d_{1}}\cdot\dfrac{n_{2}}{d_{2}} &=&\dfrac{n_{1}n_{2}}{d_{1}d_{2}}\\[15pt] \dfrac{n_{1}/d_{1}}{n_{2}/d_{2}} &=&\dfrac{n_{1}d_{2}}{d_{1}n_{2}}\\[15pt] \dfrac{n_{1}}{d_{1}} &=&\dfrac{n_{2}}{d_{2}}\ \quad \textrm{se e somente se}\ \ \ n_{1}d_{2}\ =\ n_{2}d_{1} \end{array}

Podemos expressar essas regras como funções:

Exemplo de Código
⛓ sessão — este bloco compartilha o ambiente da página: as declarações dos blocos anteriores são avaliadas antes deleCódigo editável · Ctrl+Enter executa · Esc solta o foco

Agora temos as operações em números racionais definidas em termos das funções seletoras e construtoras numer, denom e make_rat. Mas ainda não definimos essas funções. O que precisamos é de alguma maneira de colar um numerador e um denominador para formar um número racional.

Pares

Para nos permitir implementar o nível concreto de nossa abstração de dados, nosso ambiente JavaScript fornece uma estrutura composta chamada par (pair), que pode ser construída com a função primitiva pair. Esta função recebe dois argumentos e retorna um objeto de dados composto que contém os dois argumentos como partes. Dado um par, podemos extrair as partes usando as funções primitivas head e tail. Assim, podemos usar pair, head e tail da seguinte forma:

Exemplo de Código
⛓ sessão — este bloco compartilha o ambiente da página: as declarações dos blocos anteriores são avaliadas antes deleCódigo editável · Ctrl+Enter executa · Esc solta o foco
Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco

Observe que um par é um objeto de dados que pode receber um nome e ser manipulado, assim como um objeto de dados primitivo. Além disso, pair pode ser usado para formar pares cujos elementos são pares, e assim por diante:

Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco
Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco

Na seção 2.2 veremos como essa capacidade de combinar pares significa que os pares podem ser usados como blocos de construção de propósito geral para criar todos os tipos de estruturas de dados complexas. O único primitivo de dados compostos par, implementado pelas funções pair, head e tail, é a única cola que precisamos. Objetos de dados construídos a partir de pares são chamados de dados estruturados em lista.

Representando números racionais

Os pares oferecem uma maneira natural de completar o sistema de números racionais. Simplesmente representamos um número racional como um par de dois inteiros: um numerador e um denominador. Então make_rat, numer e denom são prontamente implementados da seguinte forma:

Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco

Além disso, para exibir os resultados de nossos cálculos, podemos imprimir números racionais imprimindo o numerador, uma barra e o denominador. Usamos a função primitiva stringify para transformar qualquer valor (aqui um número) em uma string. O operador + em JavaScript é sobrecarregado; ele pode ser aplicado a dois números ou a duas strings, e no último caso ele retorna o resultado de concatenar as duas strings.

Exemplo de Código
⛓ sessão — este bloco compartilha o ambiente da página: as declarações dos blocos anteriores são avaliadas antes deleCódigo editável · Ctrl+Enter executa · Esc solta o foco

Agora podemos experimentar nossas funções de números racionais:

Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco
Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco
Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco

Como o exemplo final mostra, nossa implementação de números racionais não reduz os números racionais aos menores termos. Podemos remediar isso mudando make_rat. Se tivermos uma função gcd como a da seção 1.2.5 que produz o maior divisor comum de dois inteiros, podemos usar gcd para reduzir o numerador e o denominador aos menores termos antes de construir o par:

Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco

Agora temos o resultado desejado. Esta modificação foi realizada mudando o construtor make_rat sem mudar nenhuma das funções (como add_rat e mul_rat) que implementam as operações reais.

Exercício 2.1

Defina uma versão melhor de make_rat que lida com argumentos positivos e negativos. A função make_rat deve normalizar o sinal de modo que, se o número racional for positivo, tanto o numerador quanto o denominador sejam positivos, e se o número racional for negativo, apenas o numerador seja negativo.

Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco
💡 Mostrar solução — tente primeiro!💡 Esconder solução

Ideia. Normalize tudo de uma vez: reduza pelo gcd e force o sinal para o numerador, testando só o denominador.

Exemplo de Código
Código editável · Ctrl+Enter executa · Esc solta o foco

Por que funciona. Se d < 0, negamos os dois termos — isso cobre tanto 6/-9 (vira -2/3) quanto -6/-9 (vira 2/3); se d > 0, o sinal de n já está certo. Como o gcd recebe valores absolutos, a redução não interfere na normalização.

📝 Encontrou algo errado nesta página?

Sua ajuda é muito importante para melhorar a qualidade da tradução!

🐛 Encontrou um erro?

Se você encontrou:

  • Erro de tradução (palavra incorreta, termo técnico errado)
  • Erro de ortografia ou gramática
  • Link quebrado
  • Código de exemplo que não funciona
  • Problema de formatação

Reporte um bug →

❓ Tem uma dúvida?

Se você tem:

  • Dúvida sobre o conteúdo desta seção
  • Pergunta sobre um conceito do SICP
  • Dificuldade em entender algum exemplo
  • Questão sobre a tradução de algum termo

Inicie uma discussão →

💡 Tem uma sugestão de melhoria?

Se você quer sugerir:

  • Melhoria na explicação
  • Exemplo adicional
  • Recurso visual (diagrama, ilustração)
  • Qualquer outra ideia

Sugira uma melhoria →

🌍 Quer discutir a tradução?

Se você quer debater:

  • Escolha de tradução de algum termo
  • Consistência de terminologia
  • Nuances do português

Discussão de tradução →

Obrigado por ajudar a melhorar o SICP.js PT-BR! ✨