Pergunta

Alguém poderia explicar? I compreender os conceitos básicos por trás deles, mas muitas vezes eu vê-los usados ??alternadamente e eu fico confuso.

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

Foi útil?

Solução

A lambda é apenas uma função anônima - uma função definida sem nome. Em algumas línguas, como o esquema, eles são equivalentes às funções nomeadas. De facto, a definição de função é re-escrita como uma ligação de lambda para uma variável internamente. Em outras linguagens, como Python, existem alguns (em vez desnecessárias) distinções entre eles, mas eles se comportam da mesma maneira o contrário.

A encerramento é qualquer função que se fecha sobre o ambiente na qual ele foi definido. Isso significa que ele pode acessar variáveis ??não em sua lista de parâmetros. Exemplos:

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

Isto irá causar um erro, porque func não close sobre o ambiente em anotherfunc - h é indefinido. func única fecha sobre o meio ambiente global. Isto irá funcionar:

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

Porque aqui, func é definida em anotherfunc, e em python 2.3 e superior (ou algum número como este) quando quase fechamentos acertou (mutação ainda não funciona), isso significa que it fecha sobre ambiente o anotherfunc e pode acessar as variáveis ??dentro do mesmo. Em Python 3.1 ou posterior, a mutação também funciona ao usar o nonlocal palavra-chave .

Outro ponto importante - func vai continuar a fechar sobre o ambiente de anotherfunc mesmo quando ele não é mais a ser avaliado em anotherfunc. Este código também funcionará:

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

print anotherfunc(10)()

Isto irá imprimir 10.

Isto, como você observar, não tem nada a ver com o lambda s -. Eles são dois conceitos diferentes (embora relacionadas)

Outras dicas

Há muita confusão em torno lambdas e encerramentos, mesmo nas respostas a esta pergunta StackOverflow aqui. Em vez de pedir programadores aleatórios que aprenderam sobre fechamentos da prática com certas linguagens de programação ou outros programadores nora, fazer uma viagem para a fonte de (onde tudo começou). E uma vez que lambdas e encerramentos vêm de Lambda Calculus inventado por volta Alonzo Church nos anos 30 antes primeiros computadores eletrônicos ainda existiam, esta é a source eu estou falando.

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 para outro, denotada f x
    (Pense nisso como um chamada de função , onde f é a função e x é seu único parâmetro)
  • ABSTRACTION: Vinculado um símbolo que ocorrem em uma expressão para marcar que esse símbolo é apenas uma "fenda", uma caixa em branco esperando para ser preenchido com o valor, uma "variável" por assim dizer. É feito, antecedendo uma carta λ grega (lambda), em seguida, o nome simbólico (por exemplo x), então um . ponto antes da expressão. Isso, então, converte a expressão em um função esperando um parâmetro
    Por exemplo:. λx.x+2 leva o x+2 expressão e diz que o x símbolo nessa expressão é uma variável ligada -. ele pode ser substituído por um valor fornecido como um parâmetro
    Note-se que a função definida desta forma é anonymous - ele não tem um nome, então você não pode se referir a ele ainda, mas você pode chame imediatamente -lo (lembre-se aplicação), fornecendo que o parâmetro está aguardando, como esta:? (λx.x+2) 7. Em seguida, a expressão (neste caso um valor literal) 7 é substituído como x na x+2 subexpression do lambda aplicada, de modo a obter 7+2, que então reduz a 9 por regras aritméticas comuns.

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


Em diferentes linguagens de programação, a sintaxe para a abstração funcional (lambda) podem ser diferentes. Por exemplo, em JavaScript parece que isso:

function(x) { return x+2; }

e você pode imediatamente aplicá-la 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 f nome, permitindo que você se referem a ele e chamá-lo várias vezes mais tarde, por exemplo:.

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

Mas você não tem que nomeá-lo. Você poderia chamá-lo imediatamente:

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

Em LISP, lambdas são feitas assim:

(lambda (x) (+ x 2))

e você pode chamar tal lambda por aplicá-lo imediatamente para um parâmetro:

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


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

Como eu disse, o que a abstração lambda faz é obrigatório um símbolo em sua subexpressão, para que se torne um substitutible parâmetro . símbolo tal é chamado obrigado . Mas o que se há outros símbolos na expressão? Por exemplo: λx.x/y+2. Nesta expressão, o símbolo x está ligada pela λx. lambda abstracção que o precede. Mas o outro símbolo, y, não está ligada - é livres . Nós não sabemos o que é e de onde vem, então não sei o que através e que valor que representa, e, portanto, não podemos avaliar essa expressão até a figura queo que significa y.

Na verdade, o mesmo acontece com os outros dois símbolos, 2 e +. É que estamos tão familiarizados com estes dois símbolos que normalmente nos esquecemos de que o computador não conhecê-los e precisamos dizer o que eles querem dizer com defini-los em algum lugar, por exemplo, em uma biblioteca ou a própria linguagem.

Você pode pensar em livres símbolos, tal como definido em outro lugar, fora da expressão, no seu "contexto envolvente", que é chamada de ambiente . O ambiente pode ser uma expressão maior do que essa expressão é uma parte (como Qui-Gon Jinn disse: "Há sempre um peixe maior";)), ou em alguma biblioteca, ou no próprio idioma (como um primitiva ).

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

  • expressões fechado: cada símbolo que ocorre nestas expressões é obrigado por alguma abstração lambda. Em outras palavras, eles são auto-suficiente ; eles não exigem qualquer contexto circundante para ser avaliado. Eles também são chamados de combinators .
  • expressões em aberto: alguns símbolos em essas expressões não são obrigado - isto é, alguns dos símbolos que ocorrem neles são livres e eles exigem alguma informação externa, e, assim, eles não podem ser avaliadas até que você fornecer as definições destes símbolos.

Você pode fechar um open expressão lambda, fornecendo o ambiente , que define todos esses símbolos livres ligando-os a alguns valores (que podem ser números, strings, anônimos funções aka lambdas, qualquer que seja ...).

E aqui vem a fechamento parte:
A encerramento de um expressão lambda é este conjunto particular de símbolos definidos no contexto exterior (ambiente) que dão valores para os símbolos livres neste expressão, tornando mais deles não-livre. Acontece um open expressão lambda, que ainda contém alguns "Undefined" símbolos livres, em um fechado um, que não tem nenhum símbolos livres mais.

Por exemplo, se você tem a seguinte expressão lambda: λx.x/y+2, o símbolo x está vinculado, enquanto o símbolo y é livre, portanto, a expressão é open e não pode ser avaliada a menos que você dizer o que significa y (e o mesmo com + e 2, que também são livres). Mas suponha que você também tem 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 precisam ser definidos 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, ele fecha uma expressão lambda aberto. Este é o lugar onde o fechamento nome veio em primeiro lugar, e é por isso que as respostas de tantas pessoas neste tópico não são completamente correto: P


Então, por que eles estão enganados? Por que assim que muitos deles dizem que os encerramentos são algumas estruturas de dados na memória, ou algumas características das línguas que usam, ou por que eles confundem fechamentos com lambdas? : P

Bem, os marketoids corporativos da Sun / Oracle, Microsoft, Google etc. são os culpados, porque é isso que eles chamaram essas construções em suas línguas (Java, C #, Go etc.). Eles costumam chamar de "fechamento" o que é suposto ser apenas lambdas. Ou eles chamam de "fechamento" uma técnica particular, usado para implementar o escopo lexical, ou seja, o fato de que uma função pode acessar as variáveis ??que foram definidas em seu escopo externo no momento da sua definição. Eles costumam dizer that A função "encerra" estas variáveis, ou seja, captura-los em algum estrutura de dados para salvá-los de ser destruído após a conclusão da função externa executoras. Mas este é apenas made-up post factum "folclore etimologia" e marketing, o que só torna as coisas mais confusas, porque cada fornecedor linguagem usa sua própria terminologia.

E é ainda pior devido ao fato de que há sempre um pouco de verdade no que eles dizem, que não permitem que você rejeitá-lo facilmente como falsa: P Deixe-me explicar:

Se você quiser implementar uma linguagem que usa lambdas como cidadãos de primeira classe, você precisa permitir que eles usem símbolos definidos em seu contexto circundante (isto é, para usar variáveis ??livres em seus lambdas). E esses símbolos devem estar lá, mesmo quando o ambiente retornos de função. O problema é que esses símbolos são obrigados a algum armazenamento local da função (geralmente na pilha de chamadas), que não será mais lá quando a função retorna. Portanto, para que um lambda para trabalhar da maneira que você espera, você precisa de alguma forma de "captura" todas estas variáveis ??livres de seu contexto externo e guardá-las para mais tarde, mesmo quando o contexto externo será ido. Ou seja, você precisa encontrar o fechamento do seu lambda (todas estas variáveis ??externas que ele usa) e armazená-lo em outro lugar (ou fazendo uma cópia, ou preparando espaço para eles antecipadamente, em algum outro lugar do que na pilha). O método real que você usa para atingir esse objetivo é um "detalhe de implementação" do seu idioma. O que é importante aqui é a fechamento , que é o conjunto de algum lugar variáveis ??livres do ambiente do seu lambda que precisam ser salvos.

Não demorou muito para que as pessoas começar a chamar a estrutura de dados reais que eles usam em implementações de sua linguagem para implementar o fechamento como o "fechamento" em si. A estrutura geralmente é algo como isto:

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

e essas estruturas de dados estão sendo repassados ??como parâmetros para outras funções, retornados de funções, e armazenados em variáveis, para representar lambdas, e permitindo-lhes acessar seu ambiente encerrando, assim como o código de máquina para executar nesse contexto. Mas é apenas uma forma (um dos muitos) para implementar de encerramento, não o em si fechamento.

Como explicado acima, o fecho de uma expressão lambda é o subconjunto de definições do seu ambiente que dão valores para as variáveis ??livres contidos em que a expressão lambda, eficazmente fechamento a expressão (transformando um < em> aberto expressão lambda, que não pode ser avaliada ainda, em um fechado expressão lambda, que pode então ser avaliado, uma vez que todos os símbolos contidos nele são agora definidos).

Tudo o resto é apenas um "culto à carga" e "voo-doo mágica" de programadores e linguagem vendedores desconhecem as raízes reais dessas noções.

Espero que responde às suas perguntas. Mas se você tinha alguma dúvida de acompanhamento, não hesite em perguntar-los nos comentários, e eu vou tentar explicá-lo melhor.

Quando a maioria das pessoas pensa em funções , eles pensam de 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 lambda expressões , 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 foi atribuído a:

foo();

Mais útil do que a atribuição de funções anônimas para variáveis, no entanto, estão passando-a ou de funções de ordem mais elevada, ou seja, funções que aceitam / voltar outras funções. Em muitos destes casos, nomeando 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)}); 

A fechamento pode ser um chamado ou função anônima, mas é conhecido como tal quando se "fecha sobre" variáveis ??no escopo em que a função é definida, ou seja, o fechamento vai ainda se referem ao ambiente com todas as variáveis ??externas que são usados ??no próprio fechamento. Aqui está um fecho chamado:

@x = 0;

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

incrementX(); // x now equals 1

Isso não parecer muito, mas o que se tudo isso foi em outra função e você passou incrementX para 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)

Isto é como você obter objetos stateful em programação funcional. Desde a nomeação "incrementX" não é necessário, você pode usar um lambda neste caso:

function foo()
 { @x = 0;

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

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

Um lambda é essencialmente uma função que é definida em linha em vez do método padrão de declarar funções. Lambdas podem frequentemente ser passado ao redor como objetos.

Um fechamento é uma função que envolve seu estado circundante, fazendo referência a campos externos ao corpo. Os restos fechados estaduais em todo invocações do encerramento.

Em uma linguagem orientada a objetos, fechamentos são normalmente fornecidos através de objetos. No entanto, alguns dos idiomas OO (por exemplo C #) implementar a funcionalidade especial que está mais perto da definição de fechos fornecida por puramente linguagens funcionais (como Lisp) que não tem objetos para estado enclose.

O que é interessante é que a introdução de lambdas e encerramentos em C # traz programação funcional mais perto de uso mainstream.

É tão simples como isto: lambda é um construtor 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, para que o assunto, conhecidos ou anônimos

Mais precisamente, um encerramento é como uma função primeira classe é representado em tempo de execução , como um par de seu "código" e um ambiente "fechar" sobre todas as variáveis ??não-locais usados ??nesse código. Desta forma, essas variáveis ??ainda são acessíveis, mesmo quando os âmbitos exteriores onde se originam já saíram.

Infelizmente, há muitas línguas lá fora, que não suportam funções como valores de primeira classe ou somente apoiá-los em forma aleijada. Então, as pessoas muitas vezes usam o termo "fechamento" para distinguir "a coisa real".

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

Basicamente, para uma linguagem completa Turing nós só precisa de elementos muito limitados, por exemplo, abstracção, a aplicação e redução. Abstração e aplicação fornece a maneira que você pode construir-se a expressão lamdba e redução dertermines o significado da expressão lambda.

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

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

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

OK, agora imaginar como isso pode ser implementado. Sempre que aplicar a expressão lambda para algumas expressões, por exemplo.

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

Nós podemos simplesmente substituir os parâmetros com a expressão a ser avaliada. Este modelo já é muito poderoso. Mas este modelo não nos permitem alterar os valores dos símbolos, por exemplo, Não podemos imitar a mudança de status. Assim, precisamos de um modelo mais complexo. Para ser breve, sempre que queremos calcular o significado da expressão lambda, vamos colocar o par de símbolo e o valor correspondente em um ambiente (ou tabela). Em seguida, o resto (+ x-y) é avaliada, observando-se os símbolos correspondentes na tabela. Agora, se nós fornecemos algumas primitivas para operar no ambiente diretamente, podemos modelar as mudanças de status!

Com este pano de fundo, verifique esta função:

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

Sabemos que quando avaliamos a expressão lambda, x y será ligado em uma nova tabela. Mas como e onde podemos olhar z up? Na verdade z é chamado uma variável livre. Deve haver uma externa um ambiente que contém z. Caso contrário, o significado da expressão não pode ser determinado apenas por ligação de x e y. Para tornar isso claro, você pode escrever algo como segue no esquema:

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

Então z seria ligado a um de uma mesa de exterior. Nós ainda obter 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 do conjunto !, nós podemos fazer a função de estado, ou seja, não é uma função no sentido de matemática. O que ele retorna não só depende da entrada, mas z também.

Este é algo que você já sabe muito bem, um método de objetos quase sempre depende do estado de objetos. É por isso que algumas pessoas dizem "fechamentos são objetos pobre homem." Mas também poderia considerar objetos como fechamentos pobre homem, uma vez que realmente gosto funções de primeira classe.

Eu uso o esquema para ilustrar as idéias devido a esse esquema é uma das línguas mais antiga que tem encerramentos reais. Todos os materiais aqui são muito melhor apresentado em SICP capítulo 3.

Para resumir, lambda e fechamento são realmente diferentes conceitos. Um lambda é uma função. Um fecho é um par de lambda e o ambiente correspondente, o qual fecha o lambda.

Concept é o mesmo como descrito acima, mas se você é PHP fundo, esta explicar melhor usando o código PHP.

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

function ($ v) {return $ v> 2; } É a definição da função lambda. Nós ainda pode armazená-lo em uma variável, para que ele 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ê teria que escrever uma outra função lambda ou criar um fecho (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 que é avaliado em seu próprio ambiente, que tem um ou mais encadernados variáveis ??que podem ser acessados ??quando a função é chamada. Eles vêm do mundo de programação funcional, onde há uma série de conceitos em jogo. Closures são como funções lambda, mas mais inteligente 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 PHP:

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

$closure();

Bem explicado neste artigo.

Esta questão é velho e tem muitas respostas. Agora com Java 8 e Lambda Oficial que são projetos de fechamento não oficiais que revive a questão.

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

"Um fechamento é uma expressão lambda emparelhado com um ambiente que se liga cada uma de suas variáveis ??livres para um valor. Em Java, expressões lambda será implementada por meio de encerramentos, de modo que os dois termos têm vindo a ser usados ??alternadamente na comunidade ".

Simplesmente falando, o fechamento é um truque sobre o escopo, lambda é uma função anônima. Podemos perceber fechamento com lambda mais elegante e lambda é frequentemente utilizado como um parâmetro passado para uma função mais elevada

expressão A Lambda é apenas uma função anônima. em java simples, por exemplo, você pode escrevê-lo como este:

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 função classe é apenas construído em código Java. Agora você pode chamar algum lugar mapPersonToJob.apply(person) para usá-lo. isso é apenas um exemplo. Isso é um lambda antes que houvesse sintaxe para isso. Lambdas um corte curto para isso.

Encerramento:

a Lambda torna-se um fecho de quando ele pode acessar as variáveis ??fora deste âmbito. eu acho que você pode dizer a sua magia, que magicamente pode envolver em torno do ambiente em que foi criado e usar as variáveis ??fora do seu âmbito (escopo externo. modo para ser claro, um fecho significa um lambda pode acessar seu escopo externo.

em Kotlin, um lambda pode sempre aceder ao seu fechamento (as variáveis ??que estão em seu escopo externo)

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

variáveis ??externas -. Variáveis ??definidas fora do âmbito de uma função

  • As expressões lambda são apátrida porque depende de parâmetros, variáveis ??internas ou constantes para executar operações.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • fechos estado de retenção porque utiliza variáveis ??externas (isto é variável definida fora do âmbito do corpo da função), juntamente com os parâmetros e constantes para executar operações.

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

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top