Closures - fechamento de escopo léxico

Iniciado por adalberto, Maio 01, 2017, 04:09:36 PM

tópico anterior - próximo tópico

adalberto

O que é isso e para que serve?

Tentarei responder o mais claro possível.

Como Prisma, na verdade, é uma modificação dos fontes Lua 5.2, ela herda vários traços positivos e negativos da sua linguagem base. Um ponto positivo é a questão dos closures.

Lembra que em Prisma até mesmo as funções são variáveis e com elas podemos fazer praticamente o mesmo que fazemos com as outras variáveis: podemos passar funções como argumentos para outras funções, podemos reatribuir funções em outra variável, podemos retornar funções de dentro de outras funções. É esse último aspecto que quero ressaltar aqui.

Closure nada mais é do que você criar uma função dentro de outra função e assim a função interna mantém seu escopo léxico fechado. Isso significa que as variáveis locais sempre poderão ser acessadas pela função interna, mesmo fora da função externa.

Para entender melhor vejamos um simples exemplo, vamos criar um contador:


//usando closure

funcao contador()
  local i = 0;
  retorne funcao()
              i = i + 1;
              retorne i; //retorna valor atual de i;
           fim
fim

c1 = contador();
c2 = contador();

imprima('c1: '.. c1() , '\nc1: ' .. c1());

para i = 1 , 10 inicio
  imprima('c1: ' .. c1(),'    c2: ' .. c2());
fim


funcao contador()
Criamos a função externa: contador();

local i = 0;
Criamos a variável local i dentro da função contador();

retorne funcao()
              i = i + 1;
              retorne i; //retorna valor atual de i;
           fim

Retornamos da função contador uma outra função, note que não damos nome à função retornada, e nem precisa já que ela será atribuída a uma variável. A função sem nome é chamada de função anônima.

Note que realizamos operações com a variável i dentro da função retornada.

c1 = contador();
c2 = contador();


Retornamos a função closure para as variáveis c1 e c2, embora sejam a mesma implementação os retornos são duas closures diferentes com escopos léxicos independentes, sendo assim teremos a variável local 'i' em c1 e outra variável local 'i' em c2.

imprima('c1: '.. c1() , '\nc1: ' .. c1());

Note que a cada chamada de c1 o valor de 'i' é preservado internamente em seu escopo léxico fechado e incrementado o valor 1.

--> 1, 2, 3, 4 ....

para i = 1 , 10 inicio
  imprima('c1: ' .. c1(),'    c2: ' .. c2());
fim


Veja que realmente cada closure tem seu escopo léxico fechado independente, tanto que quando a função c2 é chamada pela primeira vez, ela começa do 1 e não da continuidade de c1;

Esse foi só um exemplo simples, mas com essa característica da linguagem você conseguirá fazer coisas poderosas como: percorrer e/ou analisar dados, contadores, temporizadores, callbacks, iteradores a serem usados no comando 'para' etc...

Bom, por enquanto é isso, até mais...