1.3.1 Funções como Argumentos
Considere as três funções a seguir. A primeira calcula a soma dos inteiros de a até b:
A segunda calcula a soma dos cubos dos inteiros no intervalo dado:
A terceira calcula a soma de uma sequência de termos na série
que converge para (muito lentamente):1
Estas três funções claramente compartilham um padrão subjacente comum. Elas são, em sua maior parte, idênticas, diferindo apenas no nome da função, na função de a usada para calcular o termo a ser adicionado, e na função que fornece o próximo valor de a. Poderíamos gerar cada uma das funções preenchendo os espaços no mesmo template:
function nome(a, b) {
return a > b
? 0
: termo(a) + nome(proximo(a), b);
}
A presença de tal padrão comum é forte evidência de que existe uma abstração útil esperando para ser trazida à superfície. De fato, matemáticos há muito tempo identificaram a abstração de somatório de uma série e inventaram a "notação sigma", por exemplo
para expressar este conceito. O poder da notação sigma é que ela permite aos matemáticos lidar com o conceito de somatório em si, em vez de apenas com somas particulares — por exemplo, para formular resultados gerais sobre somas que são independentes da série particular sendo somada.
Da mesma forma, como designers de programas, gostaríamos que nossa linguagem fosse poderosa o suficiente para que pudéssemos escrever uma função que expressa o conceito de somatório em si, em vez de apenas funções que calculam somas particulares. Podemos fazer isso prontamente em nossa linguagem funcional pegando o template comum mostrado acima e transformando os "espaços" em parâmetros:
Observe que sum recebe como seus argumentos os limites inferior e superior a e b juntamente com as funções term e next. Podemos usar sum assim como usaríamos qualquer função. Por exemplo, podemos usá-la (junto com uma função inc que incrementa seu argumento em 1) para definir sum_cubes:
Usando isso, podemos calcular a soma dos cubos dos inteiros de 1 a 10:
Com a ajuda de uma função identidade para calcular o termo, podemos definir sum_integers em termos de sum:
Então podemos somar os inteiros de 1 a 10:
Também podemos definir pi_sum da mesma forma:2
Usando essas funções, podemos calcular uma aproximação de :
Uma vez que temos sum, podemos usá-la como um bloco de construção na formulação de conceitos adicionais. Por exemplo, a integral definida de uma função entre os limites e pode ser aproximada numericamente usando a fórmula
para valores pequenos de . Podemos expressar isso diretamente como uma função:
(O valor exato da integral do cubo entre 0 e 1 é 1/4.)
Exercício 1.29
A Regra de Simpson é um método mais preciso de integração numérica do que o método ilustrado acima. Usando a Regra de Simpson, a integral de uma função entre e é aproximada como
onde , para algum inteiro par , e . (Aumentar aumenta a precisão da aproximação.) Declare uma função que recebe como argumentos , , e e retorna o valor da integral, calculado usando a Regra de Simpson. Use sua função para integrar cube entre 0 e 1 (com e ), e compare os resultados com os da função integral mostrada acima.
💡 Mostrar solução — tente primeiro!💡 Esconder solução
Ideia. O coeficiente de é 1 nas pontas, 4 nos índices ímpares e 2 nos pares — então o termo da soma pode decidir o coeficiente olhando a paridade de , e sum faz o resto.
Comparação. O valor exato de é . A integral do texto com dá ; a Regra de Simpson dá exatamente 0,25 já com — para polinômios de grau até 3, Simpson não é só mais precisa, é exata (a menos de erros de arredondamento de ponto flutuante).
Exercício 1.30
A função sum acima gera uma recursão linear. A função pode ser reescrita de forma que a soma seja realizada iterativamente. Mostre como fazer isso preenchendo as expressões faltantes no corpo da função a seguir:
function sum(term, a, next, b) {
function iter(a, result) {
return ⟨??⟩
? ⟨??⟩
: iter(⟨??⟩, ⟨??⟩);
}
return iter(⟨??⟩, ⟨??⟩);
}
💡 Mostrar solução — tente primeiro!💡 Esconder solução
Por que funciona. result acumula a soma dos termos já visitados — o invariante é result + soma-dos-termos-restantes = resposta. Quando a > b não restam termos e result é a resposta. Todo o estado vive nos argumentos de iter: processo iterativo, espaço constante.
Exercício 1.31
a. A função sum é apenas a mais simples de um vasto número de abstrações similares que podem ser capturadas como funções de ordem superior.3 Escreva uma função análoga chamada product que retorna o produto dos valores de uma função nos pontos em um dado intervalo. Mostre como definir factorial em termos de product. Além disso, use product para calcular aproximações de usando a fórmula4
b. Se sua função product gera um processo recursivo, escreva uma que gere um processo iterativo. Se ela gera um processo iterativo, escreva uma que gere um processo recursivo.
💡 Mostrar solução — tente primeiro!💡 Esconder solução
a. Versão recursiva, factorial e a fórmula de Wallis:
O termo de índice é para par e para ímpar — exatamente o padrão . Com , wallis_pi dá — a série converge devagar.
b. A versão acima é recursiva; a iterativa segue o molde do exercício 1.30:
Exercício 1.32
a. Mostre que sum e product (exercício 1.31) são ambos casos especiais de uma noção ainda mais geral chamada accumulate que combina uma coleção de termos, usando alguma função geral de acumulação:
accumulate(combiner, null_value, term, a, next, b)
A função accumulate recebe como argumentos as mesmas especificações de termo e intervalo de sum e product, juntamente com uma função combiner (de dois argumentos) que especifica como o termo atual deve ser combinado com a acumulação dos termos anteriores e um null_value que especifica qual valor base usar quando os termos se esgotam. Escreva accumulate e mostre como sum e product podem ser definidos como simples chamadas a accumulate.
b. Se sua função accumulate gera um processo recursivo, escreva uma que gere um processo iterativo. Se ela gera um processo iterativo, escreva uma que gere um processo recursivo.
💡 Mostrar solução — tente primeiro!💡 Esconder solução
a. A única diferença entre sum e product é como combinar (+ vs *) e o valor neutro (0 vs 1). Abstraia os dois:
b. A versão iterativa acumula no argumento:
(Para combinadores associativos como + e * as duas versões coincidem; com combinadores não associativos a ordem de combinação difere entre elas — vale experimentar com (x, y) => x - y.)
Exercício 1.33
Você pode obter uma versão ainda mais geral de accumulate (exercício 1.32) introduzindo a noção de um filtro nos termos a serem combinados. Ou seja, combine apenas aqueles termos derivados de valores no intervalo que satisfaçam uma condição especificada. A abstração resultante filtered_accumulate recebe os mesmos argumentos que accumulate, juntamente com um predicado adicional de um argumento que especifica o filtro. Escreva filtered_accumulate como uma função. Mostre como expressar o seguinte usando filtered_accumulate:
a. a soma dos quadrados dos números primos no intervalo de a (assumindo que você tenha uma função is_prime já escrita)
b. o produto de todos os inteiros positivos menores que que são relativamente primos a (isto é, todos os inteiros positivos tais que ).
💡 Mostrar solução — tente primeiro!💡 Esconder solução
A abstração: igual a accumulate, mas termos reprovados no filtro entram como se não existissem.
Verificação. sum_of_prime_squares(2, 10) = ; product_of_coprimes(10) = .
Notas de Rodapé
1 Esta série, geralmente escrita na forma equivalente , é devida a Leibniz. Veremos como usar isso como base para alguns truques numéricos sofisticados na seção 3.5.3.
2 Observe que usamos estrutura de blocos (seção 1.1.8) para incorporar as declarações de pi_next e pi_term dentro de pi_sum, uma vez que essas funções são improváveis de serem úteis para qualquer outro propósito.
3 A intenção dos exercícios 1.31-1.33 é demonstrar o poder expressivo que é alcançado pela abstração apropriada. Os exercícios foram projetados para que você experimente diferentes maneiras de expressar esses conceitos e se convença de que as abstrações discutidas são as ferramentas certas para alcançar essa expressividade.
4 Esta fórmula foi descoberta pelo matemático e capelão inglês do século XVII John Wallis.
📝 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
❓ 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
💡 Tem uma sugestão de melhoria?
Se você quer sugerir:
- Melhoria na explicação
- Exemplo adicional
- Recurso visual (diagrama, ilustração)
- Qualquer outra ideia
🌍 Quer discutir a tradução?
Se você quer debater:
- Escolha de tradução de algum termo
- Consistência de terminologia
- Nuances do português
Obrigado por ajudar a melhorar o SICP.js PT-BR! ✨