Skip to content

Commit c0e89c8

Browse files
committed
✨ feat: adicionar tradução da seção 1.3.2 - Construindo Funções Usando Expressões Lambda
- Tradução completa de "Constructing Functions Using Lambda Expressions" - 39 exemplos de código com CodePlayground - Introdução às expressões lambda (arrow functions) - Declarações const para nomes locais - Instruções condicionais (if-else) - seção específica do JavaScript - 1 exercício traduzido (1.34) - 6 notas de rodapé traduzidas - Conceitos: lambda expressions, funções anônimas, escopo de blocos - Comparação entre function declarations e lambda com const
1 parent f6643a9 commit c0e89c8

File tree

1 file changed

+284
-0
lines changed

1 file changed

+284
-0
lines changed

docs/chapter-1/1.3.2.mdx

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
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

Comments
 (0)