1.3.2 Construindo Funções Usando Expressões Lambda
Ao usar sum como na seção 1.3.1, parece terrivelmente inconveniente ter que declarar funções triviais como pi_term e pi_next apenas para poder usá-las como argumentos para nossa função de ordem superior. Em vez de declarar pi_next e pi_term, seria mais conveniente ter uma forma de especificar diretamente "a função que retorna sua entrada incrementada em 4" e "a função que retorna o recíproco de sua entrada vezes sua entrada mais 2". Podemos fazer isso introduzindo a expressão lambda como uma forma sintática para criar funções. Usando expressões lambda, podemos descrever o que queremos como
Carregando playground de código...
e
Carregando playground de código...
Então podemos expressar nossa função pi_sum sem declarar nenhuma função auxiliar:
Carregando playground de código...
Novamente, usando uma expressão lambda, podemos escrever a função integral sem ter que declarar a função auxiliar add_dx:
Carregando playground de código...
Em geral, expressões lambda são usadas para criar funções da mesma forma que declarações de funções, exceto que nenhum nome é especificado para a função e a palavra-chave return e as chaves são omitidas (se houver apenas um parâmetro, os parênteses em torno da lista de parâmetros também podem ser omitidos, como nos exemplos que vimos).1
(parâmetros) => expressão
A função resultante é tão uma função quanto uma que é criada usando uma instrução de declaração de função. A única diferença é que ela não foi associada a nenhum nome no ambiente. Consideramos
Carregando playground de código...
como sendo equivalente a2
Carregando playground de código...
Podemos ler uma expressão lambda da seguinte forma:
x => x + 4
// ^ ^ ^ ^
// parâmetro x que retorna x mais 4
Como qualquer expressão que tem uma função como seu valor, uma expressão lambda pode ser usada como a expressão de função em uma aplicação, como em
Carregando playground de código...
ou, de forma mais geral, em qualquer contexto onde normalmente usaríamos um nome de função.3
Usando const para criar nomes locais
Outra aplicação de expressões lambda é na criação de nomes locais. Frequentemente precisamos de nomes locais em nossas funções além daqueles que foram vinculados como parâmetros. Por exemplo, suponha que desejamos calcular a função
que também poderíamos expressar como
Ao escrever uma função para calcular , gostaríamos de incluir como nomes locais não apenas e mas também os nomes de quantidades intermediárias como e . Uma maneira conveniente de declarar nomes locais é usando const dentro do corpo da função. Usando const, a função pode ser escrita como
Carregando playground de código...
Nomes que são declarados com const dentro de um bloco têm o corpo do bloco imediatamente envolvente como seu escopo.45
Instruções condicionais
Vimos que muitas vezes é útil declarar nomes que são locais para declarações de funções. Quando as funções ficam grandes, devemos manter o escopo dos nomes o mais restrito possível. Considere por exemplo expmod no exercício 1.26.
Carregando playground de código...
Esta função é desnecessariamente ineficiente, porque contém duas chamadas idênticas:
expmod(base, exp / 2, m);
Embora isso possa ser facilmente corrigido neste exemplo usando a função square, isso não é tão fácil em geral. Sem usar square, seríamos tentados a introduzir um nome local para a expressão da seguinte forma:
function expmod(base, exp, m) {
const half_exp = expmod(base, exp / 2, m);
return exp === 0
? 1
: is_even(exp)
? (half_exp * half_exp) % m
: (base * expmod(base, exp - 1, m)) % m;
}
Isso tornaria a função não apenas ineficiente, mas na verdade não terminante! O problema é que a declaração de constante aparece fora da expressão condicional, o que significa que ela é executada mesmo quando o caso base exp === 0 é atendido. Para evitar esta situação, fornecemos instruções condicionais e permitimos que instruções de retorno apareçam nos ramos da instrução. Usando uma instrução condicional, podemos escrever a função expmod da seguinte forma:
Carregando playground de código...
A forma geral de uma instrução condicional é
if (predicado) { bloco-consequente } else { bloco-alternativo }
Como nas expressões condicionais, o predicado é avaliado primeiro. Se ele for averdadeiro, o interpretador avalia o bloco-consequente (a sequência de instruções entre as chaves depois do predicado), e se ele for falso, o interpretador avalia o bloco-alternativo (a sequência de instruções entre as chaves depois de else).6
Observe que qualquer declaração de constante que ocorra em qualquer um dos blocos tem o respectivo bloco como seu escopo. Por exemplo, no expmod acima, o half_exp no consequente só é visível lá.
Exercício 1.34
Suponha que declaramos
Carregando playground de código...
Então temos
Carregando playground de código...
Carregando playground de código...
O que acontece se (perversamente) pedirmos ao interpretador para avaliar a aplicação f(f)? Explique.
Notas de Rodapé
1 Na seção 2.1.3, estenderemos a sintaxe de expressões lambda para permitir um bloco como corpo em vez de apenas uma expressão, como nas instruções de declaração de função.
2 Em JavaScript, existem diferenças sutis entre as duas versões: Uma instrução de declaração de função é automaticamente "içada" (hoisted) (movida) para o início do bloco envolvente ou para o início do programa se ocorrer fora de qualquer bloco, enquanto uma declaração de constante não é movida. Nomes declarados com declaração de função podem ser reatribuídos usando atribuição (seção 3.1.1), enquanto nomes declarados com declarações de constante não podem. Neste livro, evitamos esses recursos e tratamos uma declaração de função como equivalente à declaração de constante correspondente.
3 Entender definições internas bem o suficiente para ter certeza de que um programa significa o que pretendemos requer um modelo mais elaborado do processo de avaliação do que apresentamos neste capítulo. No entanto, as sutilezas não surgem com definições internas de funções. Voltaremos a este problema nas seções 3.2.4 e 4.1.6, depois de aprendermos mais sobre a avaliação de blocos.
4 Observe que um nome declarado em um bloco não pode ser usado antes que a declaração seja totalmente avaliada, independentemente de o mesmo nome ser declarado fora do bloco. Assim, no programa abaixo, a tentativa de usar o a declarado no nível superior para fornecer um valor para o cálculo do b declarado em f não pode funcionar.
const a = 1;
function f(x) {
const b = a + x;
const a = 5;
return a + b;
}
f(10);
O programa leva a um erro, porque o a em a + x é usado antes que sua declaração seja avaliada. Retornaremos a este programa na seção 4.1.6 (exercício 4.19), depois que aprendermos mais sobre avaliação.
5 O modelo de substituição pode ser expandido para dizer que, para uma declaração de constante, o valor da expressão depois de = é substituído pelo nome antes de = no resto do corpo do bloco (após a declaração), semelhante à substituição de argumentos por parâmetros na avaliação de uma aplicação de função.
6 O nome "lambda" é usado porque a notação de expressão lambda tem suas raízes no lambda calculus, uma linguagem formal matemática introduzida pelo lógico matemático Alonzo Church (1941). Church desenvolveu o lambda calculus para fornecer uma base matemática rigorosa para estudar as noções de função e aplicação de função. O lambda calculus se tornou uma ferramenta básica para pesquisa matemática em semântica de linguagens de programaçã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
❓ 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! ✨