Qual é a diferença entre um 'encerramento' e um 'lambda'?


811

Alguém poderia explicar? Eu entendo os conceitos básicos por trás deles, mas geralmente os vejo usados ​​de forma intercambiável e fico confuso.

E agora que estamos aqui, como eles diferem de uma função regular?


85
Lambdas são uma construção de linguagem (funções anônimas), fechamentos são uma técnica de implementação para implementar funções de primeira classe (anônimas ou não). Infelizmente, isso geralmente é confundido por muitas pessoas.
Andreas Rossberg



Para fechamentos do PHP, consulte php.net/manual/en/class.closure.php . Não é o que um programador JavaScript esperaria.
21468 Paul H

A resposta da SasQ é excelente. IMHO esta pergunta seria mais útil para os usuários de SO se orientasse os espectadores para essa resposta.
AmigoNico 15/07/19

Respostas:


703

Um lambda é apenas uma função anônima - uma função definida sem nome. Em alguns idiomas, como Scheme, eles são equivalentes a funções nomeadas. De fato, a definição da função é reescrita como vinculando um lambda a uma variável internamente. Em outras linguagens, como Python, existem algumas distinções (desnecessárias) entre elas, mas elas se comportam da mesma maneira.

Um fechamento é qualquer função que se fecha sobre o ambiente em que foi definido. Isso significa que ele pode acessar variáveis ​​que não estão em sua lista de parâmetros. Exemplos:

def func(): return h
def anotherfunc(h):
   return func()

Isso causará um erro, porque funcnão fecha sobre o ambiente em anotherfunc- hé indefinido. funcsomente fecha sobre o ambiente global. Isso funcionará:

def anotherfunc(h):
    def func(): return h
    return func()

Como aqui, funcé definido no anotherfuncpython 2.3 e superior (ou algum número como este) quando eles quase acertaram os fechamentos (a mutação ainda não funciona), isso significa que ele fecha anotherfunc o ambiente do servidor e pode acessar variáveis ​​dentro dele. isto. No Python 3.1+, a mutação também funciona ao usar a nonlocalpalavra - chave .

Outro ponto importante - funccontinuará a fechar anotherfunco ambiente, mesmo quando não estiver mais sendo avaliado anotherfunc. Este código também funcionará:

def anotherfunc(h):
    def func(): return h
    return func

print anotherfunc(10)()

Isso imprimirá 10.

Como você percebe, isso não tem nada a ver com lambda s - eles são dois conceitos diferentes (embora relacionados).


1
Claudiu, para meu conhecimento incerto, o python nunca conseguiu os fechamentos corretos. Eles resolveram o problema da mutabilidade enquanto eu não estava olhando? Muito possível ...
simon

simon - você está certo, eles ainda não estão totalmente corretos. Eu acho que python 3.0 irá adicionar um hack para contornar isso. (algo como um identificador 'não local'). Eu vou mudar meu nome para refletir isso.
Claudiu

2
@AlexanderOrlov: Ambos são lambdas e encerramentos. O Java já havia encerrado antes via classes internas anônimas. Agora que a funcionalidade foi facilitada sintaticamente por meio de expressões lambda. Portanto, provavelmente o aspecto mais relevante do novo recurso é que agora existem lambdas. Não é incorreto chamá-los de lambdas, eles são de fato lambdas. Por que os autores do Java 8 podem optar por não destacar o fato de serem encerramentos não é algo que eu saiba.
Claudiu 16/02

2
@AlexanderOrlov, porque os lambdas do Java 8 não são verdadeiros fechamentos, são simulações de fechamentos. Eles são mais semelhantes aos fechamentos do Python 2.3 (sem mutabilidade, portanto, o requisito para as variáveis ​​referenciadas serem 'efetivamente finais') e compilam internamente as funções de não fechamento que tomam todas as variáveis ​​mencionadas no escopo como parâmetros ocultos.
Logan Captura

7
@ Claudiu Acho que a referência a uma implementação de linguagem específica (Python) pode complicar demais a resposta. A questão é completamente independente do idioma (e também não possui tags específicas do idioma).
Matthew

318

Há muita confusão em torno de lambdas e fechamentos, mesmo nas respostas a esta pergunta do StackOverflow aqui. Em vez de perguntar a programadores aleatórios que aprenderam sobre fechamentos da prática com certas linguagens de programação ou outros programadores sem noção, viaje até a fonte (onde tudo começou). E como as lambdas e fechamentos vêm do Lambda Calculus inventado pela Alonzo Church nos anos 30 antes da existência dos primeiros computadores eletrônicos, é dessa fonte que estou falando.

O Lambda Calculus é a linguagem de programação mais simples do mundo. As únicas coisas que você pode fazer nele: ►

  • APLICAÇÃO: Aplicando uma expressão a outra, denotada f x.
    (Pense nisso como uma chamada de função , onde festá a função e xé seu único parâmetro)
  • ABSTRACTION: Vincula um símbolo que ocorre em uma expressão para marcar que esse símbolo é apenas um "slot", uma caixa em branco esperando para ser preenchida com valor, uma "variável" por assim dizer. Isso é feito anexando uma letra grega λ(lambda), o nome simbólico (por exemplo x) e um ponto .antes da expressão. Isso então converte a expressão em uma função esperando um parâmetro .
    Por exemplo: λx.x+2pega a expressão x+2e informa que o símbolo xnessa expressão é uma variável vinculada - ele pode ser substituído por um valor que você fornece como parâmetro.
    Observe que a função definida dessa maneira é anônima- ele não tem um nome, então você não pode se referir a ele ainda, mas você pode imediatamente chamar -lo (lembre-se da aplicação?), Fornecendo-se o parâmetro que está esperando, como este: (λx.x+2) 7. Em seguida, a expressão (neste caso, um valor literal) 7é substituída como xna subexpressão x+2do lambda aplicado, para que você obtenha 7+2, o qual será reduzido 9por regras aritméticas comuns.

Então resolvemos um dos mistérios:
lambda é a função anônima do exemplo acima λx.x+2,.


Em diferentes linguagens de programação, a sintaxe para abstração funcional (lambda) pode ser diferente. Por exemplo, no JavaScript, fica assim:

function(x) { return x+2; }

e você pode aplicá-lo imediatamente a algum parâmetro como este:

(function(x) { return x+2; })(7)

ou você pode armazenar esta função anônima (lambda) em alguma variável:

var f = function(x) { return x+2; }

que efetivamente lhe dá um nome f, permitindo que você se refira a ele e o chame várias vezes depois, por exemplo:

alert(  f(7) + f(10)  );   // should print 21 in the message box

Mas você não precisava dar um nome. Você pode chamá-lo imediatamente:

alert(  function(x) { return x+2; } (7)  );  // should print 9 in the message box

No LISP, as lambdas são feitas assim:

(lambda (x) (+ x 2))

e você pode chamar esse lambda aplicando-o imediatamente a um parâmetro:

(  (lambda (x) (+ x 2))  7  )


OK, agora é hora de resolver o outro mistério: o que é um fechamento . Para fazer isso, vamos falar sobre símbolos ( variáveis ) nas expressões lambda.

Como eu disse, o que a abstração lambda faz é vincular um símbolo em sua subexpressão, para que ela se torne um parâmetro substituível . Esse símbolo é chamado de vinculado . Mas e se houver outros símbolos na expressão? Por exemplo: λx.x/y+2. Nesta expressão, o símbolo xé vinculado pela abstração lambda que o λx.precede. Mas o outro símbolo y,, não está vinculado - é gratuito . Não sabemos o que é e de onde vem; portanto, não sabemos o que significa e que valor representa; portanto, não podemos avaliar essa expressão até descobrirmos o que ysignifica.

De fato, o mesmo acontece com os outros dois símbolos, 2e +. É que estamos tão familiarizados com esses dois símbolos que geralmente esquecemos que o computador não os conhece e precisamos dizer o que eles significam definindo-os em algum lugar, por exemplo, em uma biblioteca ou na própria linguagem.

Você pode pensar nos símbolos livres como definidos em outro lugar, fora da expressão, em seu "contexto circundante", que é chamado de ambiente . O ambiente pode ser uma expressão maior da qual essa expressão faz parte (como Qui-Gon Jinn disse: "Sempre há um peixe maior";)), ou em alguma biblioteca, ou na própria linguagem (como primitiva ).

Isso nos permite dividir expressões lambda em duas categorias:

  • Expressões FECHADAS: todo símbolo que ocorre nessas expressões é vinculado por alguma abstração lambda. Em outras palavras, eles são independentes ; eles não exigem que nenhum contexto circundante seja avaliado. Eles também são chamados de combinadores .
  • Expressões OPEN: alguns símbolos nessas expressões não são vinculados - ou seja, alguns dos símbolos que ocorrem nelas são gratuitos e requerem algumas informações externas e, portanto, não podem ser avaliados até que você forneça as definições desses símbolos.

Você pode FECHAR uma expressão lambda aberta fornecendo o ambiente , que define todos esses símbolos livres vinculando-os a alguns valores (que podem ser números, seqüências de caracteres, funções anônimas, também conhecidas como lambdas, o que seja ...).

E aqui vem a parte do fechamento :
o fechamento de uma expressão lambda é esse conjunto específico de símbolos definidos no contexto externo (ambiente) que atribui valores aos símbolos livres nessa expressão, tornando-os não livres mais. Ele transforma uma expressão lambda aberta , que ainda contém alguns símbolos livres "indefinidos", em uma expressão fechada , que não possui mais símbolos livres.

Por exemplo, se você tiver a seguinte expressão lambda:, λx.x/y+2o símbolo xé vinculado, enquanto o símbolo yé livre, portanto, a expressão é opene não pode ser avaliada, a menos que você diga o que ysignifica (e o mesmo com +e 2, que também é gratuito). Mas suponha que você também tenha um ambiente como este:

{  y: 3,
+: [built-in addition],
2: [built-in number],
q: 42,
w: 5  }

Este ambiente fornece definições para todos os símbolos "Undefined" (gratuito) de nossa expressão lambda ( y, +, 2), e vários símbolos extras ( q, w). Os símbolos que precisamos definir são esse subconjunto do ambiente:

{  y: 3,
+: [built-in addition],
2: [built-in number]  }

e este é precisamente o fechamento da nossa expressão lambda:>

Em outras palavras, fecha uma expressão lambda aberta. É daí que o fechamento do nome veio, em primeiro lugar, e é por isso que as respostas de tantas pessoas neste segmento não são muito corretas: P


Então, por que eles estão enganados? Por que muitos deles dizem que fechamentos são algumas estruturas de dados na memória, ou alguns recursos das linguagens que eles usam, ou por que confundem fechamentos com lambdas? : P

Bem, os marketoids corporativos da Sun / Oracle, Microsoft, Google etc. são os culpados, porque foi assim que eles chamaram essas construções em suas linguagens (Java, C #, Go etc.). Eles costumam chamar de "fechamento" o que deveriam ser apenas lambdas. Ou eles chamam de "fechamento" uma técnica específica usada para implementar o escopo lexical, ou seja, o fato de uma função poder acessar as variáveis ​​que foram definidas em seu escopo externo no momento de sua definição. Eles costumam dizer que a função "encerra" essas variáveis, ou seja, as captura em alguma estrutura de dados para evitar que sejam destruídas após a execução da função externa. Mas isso é apenas "etimologia folclórica" ​​e marketing pós-factum , o que só torna as coisas mais confusas,

E é ainda pior pelo fato de sempre haver um pouco de verdade no que eles dizem, o que não permite que você a descarte facilmente como falsa: P Deixe-me explicar:

Se você deseja implementar uma linguagem que usa lambdas como cidadãos de primeira classe, é necessário permitir que eles usem símbolos definidos em seu contexto circundante (ou seja, use variáveis ​​livres em suas lambdas). E esses símbolos devem estar lá mesmo quando a função circundante retornar. O problema é que esses símbolos estão vinculados a algum armazenamento local da função (geralmente na pilha de chamadas), que não estará mais lá quando a função retornar. Portanto, para que um lambda funcione da maneira esperada, você precisa "capturar" todas essas variáveis ​​livres de seu contexto externo e salvá-las para mais tarde, mesmo quando o contexto externo desaparecer. Ou seja, você precisa encontrar o fechamentodo seu lambda (todas essas variáveis ​​externas que ele usa) e armazene-o em outro lugar (fazendo uma cópia ou preparando espaço para eles antecipadamente, em outro lugar que não na pilha). O método real que você usa para atingir esse objetivo é um "detalhe da implementação" do seu idioma. O importante aqui é o fechamento , que é o conjunto de variáveis ​​livres do ambiente do seu lambda que precisam ser salvas em algum lugar.

Não demorou muito tempo para as pessoas começarem a chamar a estrutura de dados real usada nas implementações de seus idiomas para implementar o fechamento como o próprio "fechamento". A estrutura geralmente se parece com isso:

Closure {
   [pointer to the lambda function's machine code],
   [pointer to the lambda function's environment]
}

e essas estruturas de dados estão sendo passadas como parâmetros para outras funções, retornadas das funções e armazenadas em variáveis, para representar lambdas e permitindo que elas acessem seu ambiente envolvente, bem como o código da máquina para executar nesse contexto. Mas é apenas uma maneira (uma de muitas) de implementar o fechamento, não o fechamento em si.

Como expliquei acima, o fechamento de uma expressão lambda é o subconjunto de definições em seu ambiente que atribui valores às variáveis ​​livres contidas nessa expressão lambda, efetivamente fechando a expressão (transformando uma expressão lambda aberta , que ainda não pode ser avaliada ainda, em uma expressão lambda fechada , que pode ser avaliada, já que todos os símbolos contidos nela estão definidos).

Qualquer outra coisa é apenas um "culto à carga" e a "mágica do vôo-doo" de programadores e fornecedores de idiomas que desconhecem as raízes reais dessas noções.

Espero que responda às suas perguntas. Mas se você tiver alguma dúvida, sinta-se à vontade para perguntar nos comentários, e tentarei explicar melhor.


50
Melhor resposta explicando as coisas genericamente em vez de linguagem específica
Shishir Arora

39
eu amo esse tipo de abordagem ao explicar as coisas. Começando desde o início, explicando como as coisas funcionam e, em seguida, como os equívocos atuais foram criados. Esta resposta precisa ir para o topo.
Sharky

1
@SasQ> o fechamento de uma expressão lambda é o subconjunto de definições em seu ambiente que atribui valores às variáveis ​​livres contidas nessa expressão lambda <Então, em sua estrutura de dados de exemplo, isso significa que é mais apropriado dizer o "ponteiro para o ambiente da função lambda "é o fechamento?
Jeff M

3
Embora o cálculo do Lambda pareça linguagem de máquina para mim, devo concordar que é uma linguagem "encontrada" em contraste com uma linguagem "feita". E, portanto, muito menos sujeito a convenções arbitrárias e muito mais adequado para capturar a estrutura subjacente da realidade. Podemos encontrar detalhes em Linq, JavaScript, F # mais acessíveis / acessíveis, mas o cálculo do Lambda chega ao cerne da questão sem distração.
precisa saber é o seguinte

1
Compreendo que você reiterou seu argumento várias vezes, com redações ligeiramente diferentes a cada vez. Ajuda a reforçar o conceito. Eu gostaria que mais pessoas fizessem isso.
johnklawlor 19/02

175

Quando a maioria das pessoas pensa em funções , elas pensam em funções nomeadas :

function foo() { return "This string is returned from the 'foo' function"; }

Estes são chamados pelo nome, é claro:

foo(); //returns the string above

Com expressões lambda , você pode ter funções anônimas :

 @foo = lambda() {return "This is returned from a function without a name";}

Com o exemplo acima, você pode chamar o lambda através da variável à qual foi atribuído:

foo();

Mais útil do que atribuir funções anônimas a variáveis, no entanto, são passá-las para ou de funções de ordem superior, ou seja, funções que aceitam / retornam outras funções. Em muitos desses casos, nomear uma função é desnecessário:

function filter(list, predicate) 
 { @filteredList = [];
   for-each (@x in list) if (predicate(x)) filteredList.add(x);
   return filteredList;
 }

//filter for even numbers
filter([0,1,2,3,4,5,6], lambda(x) {return (x mod 2 == 0)}); 

Um fechamento pode ser uma função nomeada ou anônima, mas é conhecido como tal quando "fecha sobre" variáveis ​​no escopo em que a função é definida, ou seja, o fechamento ainda se refere ao ambiente com quaisquer variáveis ​​externas usadas no fechamento próprio. Aqui está um fechamento nomeado:

@x = 0;

function incrementX() { x = x + 1;}

incrementX(); // x now equals 1

Isso não parece muito, mas e se tudo isso estivesse em outra função e você passasse incrementXpara uma função externa?

function foo()
 { @x = 0;

   function incrementX() 
    { x = x + 1;
      return x;
    }

   return incrementX;
 }

@y = foo(); // y = closure of incrementX over foo.x
y(); //returns 1 (y.x == 0 + 1)
y(); //returns 2 (y.x == 1 + 1)

É assim que você obtém objetos com estado na programação funcional. Como não é necessário nomear "incrementX", você pode usar um lambda neste caso:

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }

14
que idioma você está usando aqui?
Claudiu

7
É basicamente pseudocódigo. Há algum lisp e JavaScript nele, bem como uma linguagem que estou projetando chamada "@" ("at"), nomeada após o operador de declaração de variável.
Mark Cidade

3
@ MarkCidade, então onde é esse idioma @? Existe uma documentação e uma descarga?
Pacerier 27/08

5
Por que não usar o Javascript e adicionar uma restrição de declarar variáveis ​​com o sinal @ inicial? Isso economizaria tempo um pouco :)
Nemoden

5
@Pacerier: Comecei a implementar o idioma: github.com/marxidad/At2015
Mark Cidade

54

Nem todos os fechamentos são lambdas e nem todos os lambdas são fechamentos. Ambas são funções, mas não necessariamente da maneira que estamos acostumados a conhecer.

Um lambda é essencialmente uma função definida em linha, e não o método padrão de declaração de funções. Lambdas podem frequentemente ser passadas como objetos.

Um fechamento é uma função que encerra seu estado circundante ao referenciar campos externos ao seu corpo. O estado fechado permanece através das invocações do fechamento.

Em uma linguagem orientada a objetos, os fechamentos são normalmente fornecidos por meio de objetos. No entanto, algumas linguagens OO (por exemplo, C #) implementam funcionalidades especiais mais próximas da definição de fechamentos fornecidos por linguagens puramente funcionais (como lisp) que não possuem objetos para incluir o estado.

O interessante é que a introdução de Lambdas e Closures em C # aproxima a programação funcional do uso convencional.


Então, poderíamos dizer que fechamentos são um subconjunto de lambdas e lambdas são um subconjunto de funções?
sker 21/10/08

Os fechamentos são um subconjunto de lambdas ... mas as lambdas são mais especiais que as funções normais. Como eu disse, lambdas são definidas em linha. Essencialmente, não há como referenciá-los, a menos que sejam passados ​​para outra função ou retornados como um valor de retorno.
Michael Brown

19
Lambdas e fechamentos são, cada um, um subconjunto de todas as funções, mas existe apenas uma interseção entre lambdas e fechamentos, onde a parte não interceptada dos fechamentos seria denominada funções que são fechamentos e lamdas sem interseção são funções independentes com variáveis ​​vinculadas.
Mark Cidade

Na minha opinião, lambdas são conceitos mais fundamentais que funções. Realmente depende da linguagem de programação.
Wei Qiu

15

É simples assim: lambda é uma construção de linguagem, ou seja, simplesmente sintaxe para funções anônimas; um fechamento é uma técnica para implementá-lo - ou quaisquer funções de primeira classe, nomeadas ou anônimas.

Mais precisamente, um fechamento é como uma função de primeira classe é representada em tempo de execução, como um par de seu "código" e um ambiente "fechado" sobre todas as variáveis ​​não locais usadas nesse código. Dessa forma, essas variáveis ​​ainda estão acessíveis, mesmo quando os escopos externos de onde se originam já foram encerrados.

Infelizmente, existem muitos idiomas por aí que não suportam funções como valores de primeira classe ou apenas os suportam na forma aleijada. Portanto, as pessoas costumam usar o termo "fechamento" para distinguir "a coisa real".


12

Do ponto de vista das linguagens de programação, elas são completamente duas coisas diferentes.

Basicamente, para uma linguagem completa de Turing, precisamos apenas de elementos muito limitados, por exemplo, abstração, aplicação e redução. A abstração e o aplicativo fornecem a maneira de criar a expressão lamdba e a redução prejudica o significado da expressão lambda.

O Lambda fornece uma maneira de abstrair o processo de computação. por exemplo, para calcular a soma de dois números, um processo que usa dois parâmetros x, y e retorna x + y pode ser abstraído. No esquema, você pode escrevê-lo como

(lambda (x y) (+ x y))

Você pode renomear os parâmetros, mas a tarefa que ele conclui não muda. Em quase todas as linguagens de programação, você pode dar um nome à expressão lambda, que são denominadas funções. Mas não há muita diferença, eles podem ser considerados conceitualmente apenas como açúcar de sintaxe.

OK, agora imagine como isso pode ser implementado. Sempre que aplicamos a expressão lambda em algumas expressões, por exemplo,

((lambda (x y) (+ x y)) 2 3)

Podemos simplesmente substituir os parâmetros pela expressão a ser avaliada. Este modelo já é muito poderoso. Mas esse modelo não nos permite alterar os valores dos símbolos, por exemplo, não podemos imitar a mudança de status. Portanto, precisamos de um modelo mais complexo. Para encurtar, sempre que queremos calcular o significado da expressão lambda, colocamos o par de símbolo e o valor correspondente em um ambiente (ou tabela). O restante (+ xy) é avaliado pesquisando os símbolos correspondentes na tabela. Agora, se fornecermos algumas primitivas para operar diretamente no ambiente, podemos modelar as mudanças de status!

Com este fundo, verifique esta função:

(lambda (x y) (+ x y z))

Sabemos que quando avaliarmos a expressão lambda, xy será vinculado a uma nova tabela. Mas como e onde podemos procurar? Na verdade, z é chamado de variável livre. Deve haver um ambiente externo que contenha z. Caso contrário, o significado da expressão não poderá ser determinado apenas pela ligação x e y. Para deixar isso claro, você pode escrever algo da seguinte maneira no esquema:

((lambda (z) (lambda (x y) (+ x y z))) 1)

Portanto, z seria vinculado a 1 em uma tabela externa. Ainda temos uma função que aceita dois parâmetros, mas o significado real dela também depende do ambiente externo. Em outras palavras, o ambiente externo se fecha sobre as variáveis ​​livres. Com a ajuda de set !, podemos tornar a função com estado, ou seja, não é uma função no sentido de matemática. O que ele retorna não depende apenas da entrada, mas também de z.

Isso é algo que você já conhece muito bem, um método de objetos quase sempre depende do estado dos objetos. É por isso que algumas pessoas dizem que "fechamentos são objetos de homens pobres". Mas também podemos considerar objetos como fechamentos de homens pobres, pois realmente gostamos de funções de primeira classe.

Eu uso o esquema para ilustrar as idéias, devido a esse esquema, é uma das línguas mais antigas que possui fechamentos reais. Todos os materiais aqui são apresentados muito melhor no capítulo 3 do SICP.

Em resumo, lambda e fechamento são conceitos realmente diferentes. Um lambda é uma função. Um fechamento é um par de lambda e o ambiente correspondente que fecha o lambda.


Então, pode-se substituir todos os fechamentos por lambdas aninhadas até que não haja mais variáveis ​​livres? Nesse caso, eu diria que os fechamentos podem ser vistos como um tipo especial de lambdas.
Trilarion

8

O conceito é o mesmo que o descrito acima, mas se você é do fundo do PHP, isso explica mais detalhadamente o uso do código PHP.

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, function ($v) { return $v > 2; });

função ($ v) {return $ v> 2; } é a definição da função lambda. Podemos até armazená-lo em uma variável, para que possa ser reutilizável:

$max = function ($v) { return $v > 2; };

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max);

Agora, e se você quiser alterar o número máximo permitido na matriz filtrada? Você precisaria escrever outra função lambda ou criar um fechamento (PHP 5.3):

$max_comp = function ($max) {
  return function ($v) use ($max) { return $v > $max; };
};

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max_comp(2));

Um fechamento é uma função avaliada em seu próprio ambiente, que possui uma ou mais variáveis ​​associadas que podem ser acessadas quando a função é chamada. Eles vêm do mundo da programação funcional, onde existem vários conceitos em jogo. Os fechamentos são como funções lambda, mas mais inteligentes no sentido de que eles têm a capacidade de interagir com variáveis ​​do ambiente externo de onde o fechamento é definido.

Aqui está um exemplo mais simples de fechamento do PHP:

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

$closure();

Bem explicado neste artigo.


7

Esta pergunta é antiga e tem muitas respostas.
Agora, com o Java 8 e o Official Lambda, que são projetos não oficiais de fechamento, ele revive a questão.

A resposta no contexto Java (via Lambdas e fechamentos - qual é a diferença? ):

"Um fechamento é uma expressão lambda emparelhada com um ambiente que vincula cada uma de suas variáveis ​​livres a um valor. Em Java, as expressões lambda serão implementadas por meio de fechamentos, portanto os dois termos passaram a ser usados ​​de forma intercambiável na comunidade".


Como as Lamdas são implementadas pelo fechamento em Java? Isso significa que a expressão Lamdas é convertida em uma classe anônima de estilo antigo?
21716 hackjutsu

5

Simplificando, o fechamento é um truque sobre o escopo, o lambda é uma função anônima. Podemos realizar o fechamento com lambda de maneira mais elegante e o lambda é frequentemente usado como um parâmetro passado para uma função superior


4

Uma expressão Lambda é apenas uma função anônima. em java simples, por exemplo, você pode escrever assim:

Function<Person, Job> mapPersonToJob = new Function<Person, Job>() {
    public Job apply(Person person) {
        Job job = new Job(person.getPersonId(), person.getJobDescription());
        return job;
    }
};

onde a classe Function é construída apenas no código java. Agora você pode ligar mapPersonToJob.apply(person)para algum lugar para usá-lo. isso é apenas um exemplo. Isso é um lambda antes que houvesse sintaxe para ele. Lambdas é um atalho para isso.

Fecho:

um Lambda se torna um fechamento quando ele pode acessar as variáveis ​​fora deste escopo. Eu acho que você pode dizer sua mágica, ele pode envolver magicamente o ambiente em que foi criado e usar as variáveis ​​fora de seu escopo (escopo externo., para ficar claro, um fechamento significa que um lambda pode acessar seu ESCOPO EXTERIOR.

no Kotlin, um lambda sempre pode acessar seu fechamento (as variáveis ​​que estão em seu escopo externo)


0

Depende se uma função usa variável externa ou não para executar a operação.

Variáveis ​​externas - variáveis ​​definidas fora do escopo de uma função.

  • As expressões Lambda são sem estado porque dependem de parâmetros, variáveis ​​internas ou constantes para executar operações.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • Os fechamentos mantêm o estado porque usam variáveis ​​externas (isto é, variáveis ​​definidas fora do escopo do corpo da função) junto com parâmetros e constantes para executar operações.

    int n = 2
    
    Function<Integer,Integer> closure = t -> {
        return t * n 
    }
    

Quando o Java cria o fechamento, ele mantém a variável n com a função para que possa ser referenciada quando passada para outras funções ou usada em qualquer lugar.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.