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:
Carregando playground de código...
A segunda calcula a soma dos cubos dos inteiros no intervalo dado:
Carregando playground de código...
A terceira calcula a soma de uma sequência de termos na série
que converge para (muito lentamente):1
Carregando playground de código...
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:
Carregando playground de código...
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:
Carregando playground de código...
Usando isso, podemos calcular a soma dos cubos dos inteiros de 1 a 10:
Carregando playground de código...
Com a ajuda de uma função identidade para calcular o termo, podemos definir sum_integers em termos de sum:
Carregando playground de código...
Então podemos somar os inteiros de 1 a 10:
Carregando playground de código...
Também podemos definir pi_sum da mesma forma:2
Carregando playground de código...
Usando essas funções, podemos calcular uma aproximação de :
Carregando playground de código...
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:
Carregando playground de código...
Carregando playground de código...
Carregando playground de código...
(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.
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(⟨??⟩, ⟨??⟩);
}