development

'폐쇄'와 '람다'의 차이점은 무엇입니까?

big-blog 2020. 9. 29. 08:03
반응형

'폐쇄'와 '람다'의 차이점은 무엇입니까?


누군가 설명 할 수 있습니까? 나는 그이면의 기본 개념을 이해하지만 종종 서로 바꿔서 사용되는 것을보고 혼란스러워합니다.

이제 우리가 여기 왔으니, 정규 함수와 어떻게 다릅니 까?


람다 단지 익명 함수입니다 - 아니 이름으로 정의 된 함수. Scheme과 같은 일부 언어에서는 명명 된 함수와 동일합니다. 실제로 함수 정의는 내부적으로 람다를 변수에 바인딩하는 것으로 다시 작성됩니다. 파이썬과 같은 다른 언어에서는 그들 사이에 약간의 (불필요한) 차이가 있지만 그렇지 않으면 같은 방식으로 작동합니다.

폐쇄 어떤 함수 를 통해 폐쇄 환경 이 정의시킨. 이는 매개 변수 목록에없는 변수에 액세스 할 수 있음을 의미합니다. 예 :

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

때문에, 오류가 발생합니다 func하지 않는 이상 주변 에서 환경 anotherfunc- h정의되지 않는다. func지구 환경에서만 닫힙니다. 이것은 작동합니다.

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

여기에서는 func에서 정의되고 anotherfunc파이썬 2.3 이상 (또는 이와 같은 숫자)에서 거의 정확한 클로저를 얻었을 (변이가 여전히 작동하지 않음), 이것은 의 환경을 닫고 anotherfunc 내부의 변수에 액세스 할 수 있음을 의미합니다. 그것. Python 3.1+에서 뮤 테이션 nonlocal키워드를 사용할 때도 작동 합니다 .

또 다른 중요한 점 은 .NET에서 더 이상 평가되지 않더라도의 환경을 func계속 닫을 것 anotherfunc입니다 anotherfunc. 이 코드도 작동합니다.

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

print anotherfunc(10)()

그러면 10이 인쇄됩니다.

아시다시피 이것은 람다 와는 아무런 관련이 없습니다. 두 개의 다른 개념입니다.


여기 StackOverflow 질문에 대한 답변에서도 람다와 클로저에 대해 많은 혼란이 있습니다. 특정 프로그래밍 언어 나 다른 단서없는 프로그래머로 실습을 통해 클로저에 대해 배운 임의의 프로그래머에게 묻는 대신 소스 (모든 것이 시작된 곳)로 이동하십시오. 그리고 람다와 클로저는 최초의 전자 컴퓨터가 존재하기 전인 30 년대에 알론조 교회가 발명 한 람다 미적분 에서 온 것이기 때문에 이것이 제가 말하는 소스 입니다.

Lambda Calculus는 세계에서 가장 간단한 프로그래밍 언어입니다. 당신이 할 수있는 유일한 것 : ►

  • APPLICATION : 표시되는 다른 표현식에 한 표현식을 적용 f x합니다.
    ( 함수 호출 로 생각 f하면 함수는 어디에 x있고 유일한 매개 변수입니다)
  • ABSTRACTION : 표현식에서 발생하는 기호를 바인딩하여이 기호가 "슬롯", 값으로 채워지기를 기다리는 빈 상자, "변수"그대로임을 표시합니다. 그리스 문자 λ(람다), 기호 이름 (예 x:) ., 표현식 앞에 을 추가하여 수행됩니다 . 그런 다음 표현식을 하나의 매개 변수가 필요한 함수 로 변환합니다 . 예 : 표현식 을 받아이 표현식 의 기호 바운드 변수 임을 알려줍니다 . 매개 변수로 제공 한 값으로 대체 할 수 있습니다. 이 방식으로 정의 된 함수는 익명입니다.
    λx.x+2x+2x
    – 이름이 없으므로 아직 참조 할 수 없지만 다음 과 같이 대기중인 매개 변수를 제공하여 즉시 호출 할 수 있습니다 (애플리케이션 기억?) (λx.x+2) 7. 그런 다음 식 (이 경우 리터럴 값) 7적용된 람다 x의 하위 식에서 와 같이 대체 x+2되므로를 얻습니다 7+2. 그러면 9일반 산술 규칙 의해 로 줄어 듭니다 .

그래서 우리는 미스터리 중 하나를 해결했습니다.
lambda위의 예에서 가져온 익명 함수 입니다 λx.x+2.


프로그래밍 언어에 따라 함수 추상화 (람다) 구문이 다를 수 있습니다. 예를 들어 JavaScript에서는 다음과 같습니다.

function(x) { return x+2; }

다음과 같은 매개 변수에 즉시 적용 할 수 있습니다.

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

또는이 익명 함수 (람다)를 일부 변수에 저장할 수 있습니다.

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

효과적으로 이름을 부여하여 f나중에 참조하고 여러 번 호출 할 수 있습니다. 예 :

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

하지만 이름을 지을 필요는 없었습니다. 즉시 호출 할 수 있습니다.

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

LISP에서 람다는 다음과 같이 만들어집니다.

(lambda (x) (+ x 2))

매개 변수에 즉시 적용하여 이러한 람다를 호출 할 수 있습니다.

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


자, 이제 다른 미스터리를 풀 시간입니다 : 클로저 가 무엇입니까 ? 이를 위해 람다 식의 기호 ( 변수 )에 대해 이야기하겠습니다 .

앞서 말했듯이 람다 추상화가하는 일은 하위 표현식에 기호를 바인딩 하여 대체 가능한 매개 변수가 됩니다. 이러한 기호를 bound 라고 합니다. 그러나 표현에 다른 기호가 있으면 어떨까요? 예 : λx.x/y+2. 이 식에서 기호 xλx.앞에 있는 람다 추상화로 묶여 있습니다. 그러나 다른 기호 인 y은 구속되지 않습니다 . 자유 입니다. 우리는 그것이 무엇인지 모르는 그것이 어디에서 오는, 그래서 우리는 무엇을 모르는 의미 와 어떤 가치 가 나타내는, 우리는 무엇을 알아낼 때까지 그러므로 우리가 식을 계산할 수 없습니다 y의미.

실제로 다른 두 기호 2+. 단지 우리가이 두 기호에 너무 익숙해서 컴퓨터가 그것들을 알지 못한다는 사실을 잊고 라이브러리 나 언어 자체와 같은 어딘가에 정의함으로써 그것들이 의미하는 바를 말할 필요가 있습니다.

자유 기호는 환경 이라고하는 "주변 컨텍스트"에서 표현식 외부의 다른 곳에서 정의 된 것으로 생각할 수 있습니다 . 환경은이 표현이 포함하는 더 큰 표현 (퀴곤 진이 말했듯이 "항상 더 큰 물고기가 있습니다";)) 또는 일부 라이브러리 또는 언어 자체 ( 원시적 ) 일 수 있습니다.

이를 통해 람다 식을 두 가지 범주로 나눌 수 있습니다.

  • CLOSED 표현식 : 이러한 표현식에서 발생하는 모든 기호는 일부 람다 추상화에 의해 바인딩 됩니다. 즉, 자체 포함되어 있습니다 . 주변 컨텍스트를 평가할 필요가 없습니다. 그들은 또한라고 연결자를 .
  • OPEN 표현식 :이 표현식의 일부 기호는 바인딩 되지 않습니다. 즉, 기호에서 발생하는 일부 기호는 자유롭고 일부 외부 정보가 필요하므로 이러한 기호의 정의를 제공 할 때까지 평가할 수 없습니다.

환경 을 제공하여 개방형 람다 식을 닫을 수 있습니다 . 환경 은 이러한 모든 자유 기호를 일부 값 (숫자, 문자열, 익명 함수 일명 람다 등)에 바인딩하여 정의합니다.

그리고 여기에 온다 폐쇄 부분 : 폐쇄 (A)의 람다 식을 받는 값을주는 외부 환경 (환경)에 정의 된 심볼이 특정 세트입니다 무료 문자를 더 이상 그들을 비 무료 제작,이 표현에. 여전히 일부 "정의되지 않은"자유 기호를 포함 하는 열린 람다 식을 더 이상 자유 기호가없는 닫힌 형식으로 바꿉니다 .

예를 들어, 다음 람다 식을 경우 : λx.x/y+2심볼은 x심볼이있는 동안, 바인딩 y무료이며, 따라서 표현은, open당신이 말을하지 않는 평가 될 수없는 것을 y의미 (과와 같은 +2무료있는). 그러나 다음 과 같은 환경 도 있다고 가정합니다 .

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

환경 우리의 람다 식의 모든 "정의되지 않은"(무료) 기호 공급 정의 ( y, +, 2), 그리고 몇 가지 추가 문자 ( q, w). 정의해야하는 기호는 다음 환경의 하위 집합입니다.

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

그리고 이것은 정확히 우리의 람다 표현식 종결 입니다.

즉, 열린 람다 식을 닫습니다 . 이것이 이름 폐쇄 가 처음 시작된 곳이며, 이것이이 스레드에서 많은 사람들의 답변이 정확하지 않은 이유입니다.


그렇다면 그들은 왜 착각합니까? 왜 그렇게 많은 사람들이 클로저가 메모리의 일부 데이터 구조이거나 사용하는 언어의 일부 기능이라고 말하거나 클로저와 람다를 혼동하는 이유는 무엇입니까? :피

글쎄요, Sun / Oracle, Microsoft, Google 등의 기업 마케 노이드가 책임을 져야합니다. 그들이 그들의 언어 (Java, C #, Go 등)에서 이러한 구조를 부르기 때문입니다. 그들은 종종 단지 람다라고하는 것을 "폐쇄"라고 부릅니다. 또는 어휘 범위 지정을 구현하는 데 사용한 특정 기술, 즉 함수가 정의 당시 외부 범위에 정의 된 변수에 액세스 할 수 있다는 사실을 "클로저"라고합니다. 그들은 종종 함수가 이러한 변수를 "포괄"한다고 말합니다. 즉, 외부 함수가 실행을 마친 후 파괴되는 것을 방지하기 위해 일부 데이터 구조로 캡처합니다. 하지만 이것은 사후에 만들어진 "민속 어원"과 마케팅에 불과합니다.

그리고 그것은 그들이 말하는 것에 항상 약간의 진실이 있다는 사실 때문에 더 나쁩니다. 이는 당신이 그것을 거짓으로 쉽게 무시할 수 없도록합니다 : P 설명하겠습니다 :

람다를 일류 시민으로 사용하는 언어를 구현하려면 주변 컨텍스트에서 정의 된 기호를 사용할 수 있도록 허용해야합니다 (즉, 람다에서 자유 변수를 사용). 그리고 이러한 기호는 주변 함수가 반환 될 때도 있어야합니다. 문제는 이러한 기호가 함수의 로컬 저장소 (보통 호출 스택에 있음)에 바인딩되어 함수가 반환 될 때 더 이상 존재하지 않는다는 것입니다. 따라서 람다가 예상 한대로 작동하려면 외부 컨텍스트에서 이러한 모든 자유 변수를 "캡처"하고 나중에 외부 컨텍스트가 사라질 때도 저장해야합니다. 즉, 클로저 를 찾아야합니다.람다 (그가 사용하는 모든 외부 변수)를 저장하고 다른 곳에 저장합니다 (복사본을 만들거나 스택이 아닌 다른 곳에 미리 공간을 준비하여). 이 목표를 달성하기 위해 사용하는 실제 방법은 언어의 "구현 세부 사항"입니다. 여기서 중요한 것은입니다 폐쇄 의 집합입니다 자유 변수 로부터 환경 필요가 어딘가에 저장하는 것을 당신의 람다의가.

사람들이 자신의 언어 구현에서 사용하는 실제 데이터 구조를 호출하여 클로저를 "클로저"자체로 구현하는 데 너무 오래 걸리지 않았습니다. 구조는 일반적으로 다음과 같습니다.

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

and these data structures are being passed around as parameters to other functions, returned from functions, and stored in variables, to represent lambdas, and allowing them to access their enclosing environment as well as the machine code to run in that context. But it's just a way (one of many) to implement closure, not the closure itself.

As I explained above, the closure of a lambda expression is the subset of definitions in its environment that give values to the free variables contained in that lambda expression, effectively closing the expression (turning an open lambda expression, which cannot be evaluated yet, into a closed lambda expression, which can then be evaluated, since all the symbols contained in it are now defined).

Anything else is just a "cargo cult" and "voo-doo magic" of programmers and language vendors unaware of the real roots of these notions.

I hope that answers your questions. But if you had any follow-up questions, feel free to ask them in the comments, and I'll try to explain it better.


When most people think of functions, they think of named functions:

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

These are called by name, of course:

foo(); //returns the string above

With lambda expressions, you can have anonymous functions:

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

With the above example, you can call the lambda through the variable it was assigned to:

foo();

More useful than assigning anonymous functions to variables, however, are passing them to or from higher-order functions, i.e., functions that accept/return other functions. In a lot of these cases, naming a function is unecessary:

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 closure may be a named or anonymous function, but is known as such when it "closes over" variables in the scope where the function is defined, i.e., the closure will still refer to the environment with any outer variables that are used in the closure itself. Here's a named closure:

@x = 0;

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

incrementX(); // x now equals 1

That doesn't seem like much but what if this was all in another function and you passed incrementX to an external function?

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)

This is how you get stateful objects in functional programming. Since naming "incrementX" isn't needed, you can use a lambda in this case:

function foo()
 { @x = 0;

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

Not all closures are lambdas and not all lambdas are closures. Both are functions, but not necessarily in the manner we're used to knowing.

A lambda is essentially a function that is defined inline rather than the standard method of declaring functions. Lambdas can frequently be passed around as objects.

A closure is a function that encloses its surrounding state by referencing fields external to its body. The enclosed state remains across invocations of the closure.

In an object-oriented language, closures are normally provided through objects. However, some OO languages (e.g. C#) implement special functionality that is closer to the definition of closures provided by purely functional languages (such as lisp) that do not have objects to enclose state.

What's interesting is that the introduction of Lambdas and Closures in C# brings functional programming closer to mainstream usage.


It's as simple as this: lambda is a language construct, i.e. simply syntax for anonymous functions; a closure is a technique to implement it -- or any first-class functions, for that matter, named or anonymous.

More precisely, a closure is how a first-class function is represented at runtime, as a pair of its "code" and an environment "closing" over all the non-local variables used in that code. This way, those variables are still accessible even when the outer scopes where they originate have already been exited.

Unfortunately, there are many languages out there that do not support functions as first-class values, or only support them in crippled form. So people often use the term "closure" to distinguish "the real thing".


From the view of programming languages, they are completely two different things.

Basically for a Turing complete language we only needs very limited elements, e.g. abstraction, application and reduction. Abstraction and application provides the way you can build up lamdba expression, and reduction dertermines the meaning of the lambda expression.

Lambda provides a way you can abstract the computation process out. for example, to compute the sum of two numbers, a process which takes two parameters x, y and returns x+y can be abstracted out. In scheme, you can write it as

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

You can rename the parameters, but the task that it completes doesn't change. In almost all of programming languages, you can give the lambda expression a name, which are named functions. But there is no much difference, they can be conceptually considered as just syntax sugar.

OK, now imagine how this can be implemented. Whenever we apply the lambda expression to some expressions, e.g.

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

We can simply substitute the parameters with the expression to be evaluated. This model is already very powerful. But this model doesn't enable us to change the values of symbols, e.g. We can't mimic the change of status. Thus we need a more complex model. To make it short, whenever we want to calculate the meaning of the lambda expression, we put the pair of symbol and the corresponding value into an environment(or table). Then the rest (+ x y) is evaluated by looking up the corresponding symbols in the table. Now if we provide some primitives to operate on the environment directly, we can model the changes of status!

With this background, check this function:

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

We know that when we evaluate the lambda expression, x y will be bound in a new table. But how and where can we look z up? Actually z is called a free variable. There must be an outer an environment which contains z. Otherwise the meaning of the expression can't be determined by only binding x and y. To make this clear, you can write something as follows in scheme:

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

So z would be bound to 1 in an outer table. We still get a function which accepts two parameters but the real meaning of it also depends on the outer environment. In other words the outer environment closes on the free variables. With the help of set!, we can make the function stateful, i.e, it's not a function in the sense of maths. What it returns not only depends on the input, but z as well.

This is something you already know very well, a method of objects almost always relies on the state of objects. That's why some people say "closures are poor man's objects. " But we could also consider objects as poor man's closures since we really like first class functions.

I use scheme to illustrate the ideas due to that scheme is one of the earliest language which has real closures. All of the materials here are much better presented in SICP chapter 3.

To sum up, lambda and closure are really different concepts. A lambda is a function. A closure is a pair of lambda and the corresponding environment which closes the lambda.


Concept is same as described above, but if you are from PHP background, this further explain using PHP code.

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

function ($v) { return $v > 2; } is the lambda function definition. We can even store it in a variable, so it can be reusable:

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

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

Now, what if you want to change the maximum number allowed in the filtered array? You would have to write another lambda function or create a closure (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));

A closure is a function that is evaluated in its own environment, which has one or more bound variables that can be accessed when the function is called. They come from the functional programming world, where there are a number of concepts in play. Closures are like lambda functions, but smarter in the sense that they have the ability to interact with variables from the outside environment of where the closure is defined.

Here is a simpler example of PHP closure:

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

$closure();

Nicely explained in this article.


This question is old and got many answers. Now with Java 8 and Official Lambda that are unofficial closure projects it revives the question.

The answer in Java context (via Lambdas and closures — what’s the difference?):

"A closure is a lambda expression paired with an environment that binds each of its free variables to a value. In Java, lambda expressions will be implemented by means of closures, so the two terms have come to be used interchangeably in the community."


Simply speaking, closure is a trick about scope, lambda is an anonymous function. We can realize closure with lambda more elegantly and lambda is often used as a parameter passed to a higher function


A Lambda expression is just an anonymous function. in plain java, for example, you can write it like this:

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

where the class Function is just built in java code. Now you can call mapPersonToJob.apply(person) somewhere to use it. thats just one example. Thats a lambda before there was syntax for it. Lambdas a short cut for this.

Closure:

a Lambda becomes a closure when it can access the variables outside of this scope. i guess you can say its magic, it magically can wrap around the environment it was created in and use the variables outside of its scope(outer scope. so to be clear, a closure means a lambda can access its OUTER SCOPE.

in Kotlin, a lambda can always access its closure (the variables that are in its outer scope)


It depends on whether a function uses external variable or not to perform operation.

External variables - variables defined outside the scope of a function.

  • Lambda expressions are stateless because It depends on parameters, internal variables or constants to perform operations.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • Closures hold state because it uses external variables (i.e. variable defined outside the scope of the function body) along with parameters and constants to perform operations.

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

When Java creates closure, it keeps the variable n with the function so it can be referenced when passed to other functions or used anywhere.

참고URL : https://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda

반응형