|
| 1 | +# 1.3.2 Construindo Funções Usando Expressões Lambda |
| 2 | + |
| 3 | +import CodePlayground from '@site/src/components/CodePlayground'; |
| 4 | + |
| 5 | +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 |
| 6 | + |
| 7 | +<CodePlayground |
| 8 | + code={`x => x + 4`} |
| 9 | + height={100} |
| 10 | + showLineNumbers={false} |
| 11 | +/> |
| 12 | + |
| 13 | +e |
| 14 | + |
| 15 | +<CodePlayground |
| 16 | + code={`x => 1 / (x * (x + 2))`} |
| 17 | + height={100} |
| 18 | + showLineNumbers={false} |
| 19 | +/> |
| 20 | + |
| 21 | +Então podemos expressar nossa função `pi_sum` sem declarar nenhuma função auxiliar: |
| 22 | + |
| 23 | +<CodePlayground |
| 24 | + code={`function pi_sum(a, b) { |
| 25 | + return sum(x => 1 / (x * (x + 2)), |
| 26 | + a, |
| 27 | + x => x + 4, |
| 28 | + b); |
| 29 | +}`} |
| 30 | + hiddenCode={`function sum(term, a, next, b) { |
| 31 | + return a > b |
| 32 | + ? 0 |
| 33 | + : term(a) + sum(term, next(a), next, b); |
| 34 | +}`} |
| 35 | + height={250} |
| 36 | + showLineNumbers={false} |
| 37 | +/> |
| 38 | + |
| 39 | +Novamente, usando uma expressão lambda, podemos escrever a função `integral` sem ter que declarar a função auxiliar `add_dx`: |
| 40 | + |
| 41 | +<CodePlayground |
| 42 | + code={`function integral(f, a, b, dx) { |
| 43 | + return sum(f, |
| 44 | + a + dx / 2, |
| 45 | + x => x + dx, |
| 46 | + b) |
| 47 | + * |
| 48 | + dx; |
| 49 | +}`} |
| 50 | + hiddenCode={`function sum(term, a, next, b) { |
| 51 | + return a > b |
| 52 | + ? 0 |
| 53 | + : term(a) + sum(term, next(a), next, b); |
| 54 | +}`} |
| 55 | + height={300} |
| 56 | + showLineNumbers={false} |
| 57 | +/> |
| 58 | + |
| 59 | +<a name="footnote-link-1"></a> |
| 60 | +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).[<sup>1</sup>](#footnote-1) |
| 61 | + |
| 62 | +```javascript |
| 63 | +(parâmetros) => expressão |
| 64 | +``` |
| 65 | + |
| 66 | +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 |
| 67 | + |
| 68 | +<CodePlayground |
| 69 | + code={`function plus4(x) { |
| 70 | + return x + 4; |
| 71 | +}`} |
| 72 | + height={150} |
| 73 | + showLineNumbers={false} |
| 74 | +/> |
| 75 | + |
| 76 | +<a name="footnote-link-2"></a> |
| 77 | +como sendo equivalente a[<sup>2</sup>](#footnote-2) |
| 78 | + |
| 79 | +<CodePlayground |
| 80 | + code={`const plus4 = x => x + 4;`} |
| 81 | + height={100} |
| 82 | + showLineNumbers={false} |
| 83 | +/> |
| 84 | + |
| 85 | +Podemos ler uma expressão lambda da seguinte forma: |
| 86 | + |
| 87 | +```text |
| 88 | + x => x + 4 |
| 89 | +// ^ ^ ^ ^ |
| 90 | +// parâmetro x que retorna x mais 4 |
| 91 | +``` |
| 92 | + |
| 93 | +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 |
| 94 | + |
| 95 | +<CodePlayground |
| 96 | + code={`((x, y, z) => x + y + square(z))(1, 2, 3);`} |
| 97 | + hiddenCode={`function square(x) { |
| 98 | + return x * x; |
| 99 | +}`} |
| 100 | + height={100} |
| 101 | + showLineNumbers={false} |
| 102 | +/> |
| 103 | + |
| 104 | +ou, de forma mais geral, em qualquer contexto onde normalmente usaríamos um nome de função.[<sup>3</sup>](#footnote-3) |
| 105 | + |
| 106 | +<a name="footnote-link-4"></a> <a name="footnote-link-5"></a> |
| 107 | +## Usando `const` para criar nomes locais |
| 108 | + |
| 109 | +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 |
| 110 | + |
| 111 | +$$ |
| 112 | +f(x,y) = x(1+xy)^2 + y(1-y) + (1+xy)(1-y) |
| 113 | +$$ |
| 114 | + |
| 115 | +que também poderíamos expressar como |
| 116 | + |
| 117 | +$$ |
| 118 | +\begin{array}{lll} |
| 119 | +a &=& 1+xy\\ |
| 120 | +b &=& 1-y\\ |
| 121 | +f(x,y) &=& xa^2+yb+ab |
| 122 | +\end{array} |
| 123 | +$$ |
| 124 | + |
| 125 | +Ao escrever uma função para calcular $f$, gostaríamos de incluir como nomes locais não apenas $x$ e $y$ mas também os nomes de quantidades intermediárias como $a$ e $b$. Uma maneira conveniente de declarar nomes locais é usando `const` dentro do corpo da função. Usando `const`, a função pode ser escrita como |
| 126 | + |
| 127 | +<CodePlayground |
| 128 | + code={`function f(x, y) { |
| 129 | + const a = 1 + x * y; |
| 130 | + const b = 1 - y; |
| 131 | + return x * square(a) + y * b + a * b; |
| 132 | +}`} |
| 133 | + hiddenCode={`function square(x) { |
| 134 | + return x * x; |
| 135 | +}`} |
| 136 | + height={250} |
| 137 | + showLineNumbers={false} |
| 138 | +/> |
| 139 | + |
| 140 | +Nomes que são declarados com `const` dentro de um bloco têm o corpo do bloco imediatamente envolvente como seu escopo.[<sup>4</sup>](#footnote-4)[<sup>5</sup>](#footnote-5) |
| 141 | + |
| 142 | +## Instruções condicionais |
| 143 | + |
| 144 | +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. |
| 145 | + |
| 146 | +<CodePlayground |
| 147 | + code={`function expmod(base, exp, m) { |
| 148 | + return exp === 0 |
| 149 | + ? 1 |
| 150 | + : is_even(exp) |
| 151 | + ? ( expmod(base, exp / 2, m) |
| 152 | + * expmod(base, exp / 2, m)) % m |
| 153 | + : (base * expmod(base, exp - 1, m)) % m; |
| 154 | +}`} |
| 155 | + hiddenCode={`function is_even(n) { |
| 156 | + return n % 2 === 0; |
| 157 | +}`} |
| 158 | + height={300} |
| 159 | + showLineNumbers={false} |
| 160 | +/> |
| 161 | + |
| 162 | +Esta função é desnecessariamente ineficiente, porque contém duas chamadas idênticas: |
| 163 | + |
| 164 | +```javascript |
| 165 | +expmod(base, exp / 2, m); |
| 166 | +``` |
| 167 | + |
| 168 | +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: |
| 169 | + |
| 170 | +```javascript |
| 171 | +function expmod(base, exp, m) { |
| 172 | + const half_exp = expmod(base, exp / 2, m); |
| 173 | + return exp === 0 |
| 174 | + ? 1 |
| 175 | + : is_even(exp) |
| 176 | + ? (half_exp * half_exp) % m |
| 177 | + : (base * expmod(base, exp - 1, m)) % m; |
| 178 | +} |
| 179 | +``` |
| 180 | + |
| 181 | +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: |
| 182 | + |
| 183 | +<CodePlayground |
| 184 | + code={`function expmod(base, exp, m) { |
| 185 | + if (exp === 0) { |
| 186 | + return 1; |
| 187 | + } else { |
| 188 | + if (is_even(exp)) { |
| 189 | + const half_exp = expmod(base, exp / 2, m); |
| 190 | + return (half_exp * half_exp) % m; |
| 191 | + } else { |
| 192 | + return (base * expmod(base, exp - 1, m)) % m; |
| 193 | + } |
| 194 | + } |
| 195 | +}`} |
| 196 | + hiddenCode={`function is_even(n) { |
| 197 | + return n % 2 === 0; |
| 198 | +}`} |
| 199 | + height={450} |
| 200 | + showLineNumbers={false} |
| 201 | +/> |
| 202 | + |
| 203 | +A forma geral de uma instrução condicional é |
| 204 | + |
| 205 | +```javascript |
| 206 | +if (predicado) { bloco-consequente } else { bloco-alternativo } |
| 207 | +``` |
| 208 | + |
| 209 | +<a name="footnote-link-6"></a> |
| 210 | +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`).[<sup>6</sup>](#footnote-6) |
| 211 | + |
| 212 | +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á. |
| 213 | + |
| 214 | +## Exercício 1.34 |
| 215 | + |
| 216 | +<a name="ex-1-34"></a> |
| 217 | +Suponha que declaramos |
| 218 | + |
| 219 | +<CodePlayground |
| 220 | + code={`function f(g) { |
| 221 | + return g(2); |
| 222 | +}`} |
| 223 | + height={150} |
| 224 | + showLineNumbers={false} |
| 225 | +/> |
| 226 | + |
| 227 | +Então temos |
| 228 | + |
| 229 | +<CodePlayground |
| 230 | + code={`f(square);`} |
| 231 | + hiddenCode={`function square(x) { |
| 232 | + return x * x; |
| 233 | +} |
| 234 | +function f(g) { |
| 235 | + return g(2); |
| 236 | +}`} |
| 237 | + height={100} |
| 238 | + showLineNumbers={false} |
| 239 | +/> |
| 240 | + |
| 241 | +<CodePlayground |
| 242 | + code={`f(z => z * (z + 1));`} |
| 243 | + hiddenCode={`function f(g) { |
| 244 | + return g(2); |
| 245 | +}`} |
| 246 | + height={100} |
| 247 | + showLineNumbers={false} |
| 248 | +/> |
| 249 | + |
| 250 | +O que acontece se (perversamente) pedirmos ao interpretador para avaliar a aplicação `f(f)`? Explique. |
| 251 | + |
| 252 | +--- |
| 253 | + |
| 254 | +## Notas de Rodapé |
| 255 | + |
| 256 | +<a name="footnote-1"></a> |
| 257 | +**[1](#footnote-link-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. |
| 258 | + |
| 259 | +<a name="footnote-2"></a> |
| 260 | +**[2](#footnote-link-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. |
| 261 | + |
| 262 | +<a name="footnote-3"></a> |
| 263 | +**[3](#footnote-link-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. |
| 264 | + |
| 265 | +<a name="footnote-4"></a> |
| 266 | +**[4](#footnote-link-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. |
| 267 | + |
| 268 | +```javascript |
| 269 | +const a = 1; |
| 270 | +function f(x) { |
| 271 | + const b = a + x; |
| 272 | + const a = 5; |
| 273 | + return a + b; |
| 274 | +} |
| 275 | +f(10); |
| 276 | +``` |
| 277 | + |
| 278 | +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. |
| 279 | + |
| 280 | +<a name="footnote-5"></a> |
| 281 | +**[5](#footnote-link-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. |
| 282 | + |
| 283 | +<a name="footnote-6"></a> |
| 284 | +**[6](#footnote-link-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. |
0 commit comments