Vimos que as funções permitem-nos dar
um nome a um conjunto de operações e tratá-lo como
um todo. Muitas vezes, porém, há um padrão que se
repete, variando apenas uma ou outra operação. Por exemplo,
consideremos uma função que soma os quadrados de
todos os inteiros entre a e b, .
(defun soma-quadrados (a b) (if (> a b) 0 (+ (quadrado a) (soma-quadrados (1+ a) b))))> (soma-quadrados 1 4) 30
Consideremos agora uma outra função que soma as raizes
quadradas de todos os inteiros entre a e b,
.
(defun soma-raizes (a b) (if (> a b) 0 (+ (sqrt a) (soma-raizes (1+ a) b))))> (soma-raizes 1 4) 6.146264369941973
Em ambas as funções existe uma
soma de expressões matemáticas entre dois limites, i.e.,
existe um somatório . O somatório é uma abstracção
matemática para uma soma de números. Dentro do somatório
é possível colocar qualquer operação matemática
relativa ao índice do somatório. Esse índice varia
desde o limite inferior até ao limite superior.
Para que se possa definir o processo do somatório na nossa linguagem de programação ela deve ser capaz de fazer abstração sobre as próprias operações a realizar, e deve poder usá-las como se de parâmetros do processo se tratasse. O padrão a executar seria qualquer coisa do estilo:
(defun soma-??? (a b) (if (> a b) 0 (+ (aplica-??? a) (soma-??? (1+ a) b))))
O símbolo ??? representa a operação a realizar dentro do somatório, e que pretendemos transformar num parâmetro.
Em Lisp, para se aplicar uma função que é o valor de um argumento, usa-se a função funcall, que recebe essa função e os seus parâmetros actuais. Para se indicar que pretendemos a função associada a um determinado símbolo, usa-se a forma especial function.
> (function 1+) #> (funcall (function 1+) 9) 10 > (defun teste (f x y) (funcall f x y)) teste > (teste (function +) 1 2) 3
Deste modo já podemos escrever a implementação do nosso somatório:
(defun somatorio (func a b) (if (> a b) 0 (+ (funcall func a) (somatorio func (1+ a) b))))
Podemos testar a função para o exemplo anterior.
> (somatorio (function quadrado) 1 4) 30
Como se vê, a função somatorio representa a abstracção associada ao somatório matemático. Para isso, ela recebe uma função como argumento e aplica-a aos sucessivos inteiros incluídos no somatório.
As funções que recebem e manipulam outras funções são designadas funções de ordem superior.
Exercício 19
Repare-se que, tal como a função somatório, podemos escrever a abstracção correspondente ao produtório (também designado piatório). Esta abstracção corresponde ao produto dos valores de uma determinada expressão para todos os inteiros de um intervalo. Escreva uma função Lisp que a implemente.
Exercício 20
Escreva a função factorial usando o produtório.
Exercício 21
Quer o somatório, quer o produtório podem ser vistos como casos especiais de uma outra abstracção ainda mais genérica, designada acumulatório. Nesta abstracção, quer a operação de combinação dos elementos, quer a função a aplicar a cada um, quer o valor inicial, quer o limite inferior, quer a passagem para o elemento seguinte (designado o sucessor), quer o limite superior são parâmetros. Defina esta função. Defina o somatório e o produtório em termos de acumulatório.