'클로저'와 '람다'의 차이점은 무엇입니까?


811

누군가 설명해 주시겠습니까? 나는 기본 개념을 이해하지만 종종 상호 교환 적으로 사용되는 것을 보았고 혼란스러워합니다.

그리고 우리가 여기 왔으니, 그것들은 일반 기능과 어떻게 다릅니 까?


85
람다는 언어 구성 (익명 함수)이고 클로저는 일급 함수 (익명 여부에 관계없이)를 구현 하는 구현 기술 입니다. 불행히도, 이것은 종종 많은 사람들에 의해 혼동됩니다.
안드레아스 Rossberg



PHP 클로저에 대해서는 php.net/manual/en/class.closure.php를 참조하십시오 . JavaScript 프로그래머가 기대하는 것은 아닙니다.
PaulH

SasQ의 답변은 훌륭합니다. 이 질문은 시청자가 해당 답변을 안내하면 SO 사용자에게 더 유용합니다.
AmigoNico

답변:


703

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

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

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 내부 변수에 액세스 할 수 있음을 의미합니다 그것. 사용할 때 파이썬에서 3.1, 돌연변이도 작동 키워드를 .nonlocal

또 다른 중요한 점 funcanotherfunc더 이상 평가되지 않더라도 환경을 계속 폐쇄하는 것 입니다 anotherfunc. 이 코드는 다음과 같이 작동합니다.

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

print anotherfunc(10)()

10이 인쇄됩니다.

아시다시피 람다 와는 아무런 관련이 없습니다. 두 가지 다른 개념이지만 서로 관련이 있습니다.


1
Claudiu, 내 불확실한 지식으로 파이썬은 클로저를 제대로 얻지 못했습니다. 내가 보이지 않는 동안 그들은 돌연변이 문제를 해결 했습니까? 상당히 가능합니다.
simon

사이먼-당신 말이 맞아요, 그들은 아직 완전히 정확하지 않습니다. 파이썬 3.0 이이 문제를 해결하기 위해 해킹을 추가 할 것이라고 생각합니다. ( '로컬이 아닌'식별자와 같은 것). 그것을 반영하기 위해 내 ansewr을 변경합니다.
Claudiu

2
@AlexanderOrlov : 그들은 람다와 클로저입니다. Java는 익명의 내부 클래스를 통해 폐쇄되었습니다. 이제이 기능은 람다 식을 통해 구문 적으로 더 쉬워졌습니다. 새 기능의 가장 관련성이 높은 부분은 이제 람다가 있다는 것입니다. 그것들을 람다라고 부르는 것은 잘못된 것이 아니며 실제로는 람다입니다. Java 8 작성자가 자신이 클로저라는 사실을 강조하지 않기로 선택한 이유는 내가 아는 것이 아닙니다.
Claudiu

2
@AlexanderOrlov Java 8 람다는 실제 클로저가 아니기 때문에 클로저 시뮬레이션입니다. 그것들은 파이썬 2.3 클로저와 유사하며 (변이 없음, 따라서 '효과적으로 최종'인 변수에 대한 요구 사항), 은폐 범위에서 참조 된 모든 변수를 숨겨진 파라미터로 취하는 클로저가 아닌 함수로 내부 컴파일됩니다.
Logan Pickup

7
@Claudiu 특정 언어 구현 (Python)에 대한 참조가 대답을 너무 복잡하게 할 수 있다고 생각합니다. 질문은 언어에 구애받지 않고 완전히 언어에 구애받지 않습니다 (언어 별 태그가 없음).
Matthew

318

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

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

  • 응용 프로그램 :로 표현 된 표현을 다른 표현에 적용 f x합니다.
    (a이라고 생각 함수 호출 , f함수이고, x그것의 유일한 파라미터이다)
  • AbstraCTION : 표현식에서 발생하는 심볼을 바인딩하여이 심볼이 "슬롯", 값으로 채워지기를 기다리는 빈 상자, "변수"를 표시합니다. 그리스어 문자 λ(람다), 기호 이름 (예 x:) ., 식 앞에 점을 붙여서 수행합니다. 그러면 표현식을 하나의 매개 변수를 기대 하는 함수 로 변환합니다 . 예를 들어 : 표현식 을 가져 와서이 표현식 의 기호 가 바운드 변수 임을 나타냅니다. 매개 변수로 제공 한 값으로 대체 할 수 있습니다. 이 방법으로 정의 된 함수는 익명입니다.
    λx.x+2x+2x
    – 이름이 없으므로 아직 참조 할 수 없지만 다음 과 같이 대기중인 매개 변수를 제공하여 즉시 호출 할 수 있습니다 (응용 프로그램 기억?) (λx.x+2) 7. 그런 다음 적용된 람다 의 하위 표현식에서와 7같이 표현식 (이 경우 리터럴 값) 이 대체 되므로 일반적인 산술 규칙에 따라 줄어 듭니다 .xx+27+29

그래서 우리는 수수께끼 중 하나를 해결했습니다.
lambda 는 위 예제 의 익명 함수 입니다 λx.x+2.


다른 프로그래밍 언어에서는 기능 추상화 (lambda) 구문이 다를 수 있습니다. 예를 들어, JavaScript에서는 다음과 같습니다.

function(x) { return x+2; }

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

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

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

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  )


OK, 지금은 다른 신비를 해결하기 위해 시간하십시오 무엇 폐쇄 . 그렇게하기 위해 람다 식의 기호 ( 변수 )에 대해 이야기합시다 .

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

사실, 같은 다른 두 개의 기호로 간다, 2+. 단지이 두 가지 기호에 대해 잘 알고 있기 때문에 일반적으로 컴퓨터가이를 알지 못한다는 사실을 잊어 버립니다. 예를 들어 라이브러리 나 언어 자체와 같이 어딘가에 정의하여 의미를 알려야합니다.

당신은 생각할 수 없는 그라고는 "주변 상황"에서, 표현 밖에, 다른 곳에서 정의 된 문자 환경을 . 환경은이 표현이 Qui-Gon Jinn이 말한 것처럼 : "항상 더 큰 물고기가 있습니다";) 또는 일부 라이브러리 또는 언어 자체 ( 원시적 ) 로 표현되는 더 큰 표현 일 수 있습니다 .

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

  • 닫힌 표현식 :이 표현식에서 발생하는 모든 심볼은 람다 추상화에 의해 구속 됩니다. 다시 말해서, 그들은 독립적입니다 ; 주변 상황을 평가할 필요가 없습니다. 그들은 또한 결합 자라고 불립니다 .
  • 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]  }

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

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


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

썬 / 오라클, 마이크로 소프트, 구글 등의 기업 시장은 이러한 구조를 그들의 언어 (자바, C #, Go 등)로 불렀기 때문에 책임을 져야한다. 그들은 종종 람다라고 여겨지는 것을 "폐쇄"라고 부릅니다. 또는 어휘 범위 지정을 구현하는 데 사용 된 특정 기술, 즉 함수가 정의시 외부 범위에 정의 된 변수에 액세스 할 수 있다는 사실을 "클로저"라고합니다. 그들은 종종 함수가 이러한 변수를 "닫는"즉, 외부 함수가 실행을 마친 후에 파괴되지 않도록 데이터 구조로 캡처한다고 말합니다. 그러나 이것은 사실 "민속 어원"과 마케팅으로 구성되어 있습니다.

그리고 그들이 말하는 내용에는 항상 약간의 진실이 있기 때문에 더 나쁩니다. 그러면 진실을 쉽게 거짓으로 무시할 수 없습니다.

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

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

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

이러한 데이터 구조는 다른 함수에 매개 변수로 전달되고 함수에서 리턴되며 변수에 저장되어 람다를 나타내며 해당 환경에서 실행할 기계 코드뿐만 아니라 주변 환경에 액세스 할 수 있습니다. 그러나 그것은 단지 할 수있는 방법 (중 하나)의 구현 폐쇄하지 폐쇄 자체를.

위에서 설명한 것처럼 람다 식의 닫힘은 해당 람다 식에 포함 된 자유 변수에 값을 제공하여 식을 효과적으로 닫는 ( 아직 평가할 수없는 열린 람다 식을 변환하는) 정의의 하위 집합입니다. 폐쇄 그 안에 포함 된 모든 심볼들은 현재 정의되어 있기 때문에 다음, 평가할 수 람다 식).

다른 개념은 프로그래머와 언어 공급 업체의 "카고 컬트"와 "부두 매직"으로, 이러한 개념의 실제 뿌리를 알지 못합니다.

귀하의 질문에 답변이 되었기를 바랍니다. 그러나 후속 질문이 있으시면 언제든지 의견을 물어보십시오. 더 잘 설명하려고 노력할 것입니다.


50
언어 별이 아닌 일반적인 사항을 설명하는 가장 좋은 답변
Shishir Arora

39
나는 물건을 설명 할 때 이런 종류의 접근 방식을 좋아합니다. 처음부터 시작하여 일이 작동하는 방식과 현재 오해가 어떻게 발생했는지 설명합니다. 이 답변은 맨 위로 이동해야합니다.
Sharky

1
@SasQ> 람다 식의 닫힘은 해당 람다 식에 포함 된 자유 변수에 값을 제공하는 환경 정의의 하위 집합입니다. <데이터 구조의 예에서 " 람다 함수의 환경 "이 폐쇄인가?
Jeff M

3
Lambda 미적분학이 나에게 기계어처럼 느껴지지만 나는 그것이 "만들어진"언어와 대조적으로 "발견 된"언어라는 것에 동의해야한다. 따라서 임의의 규칙을 적용하는 것이 훨씬 적고 기본 현실 구조를 캡처하는 데 훨씬 적합합니다. Linq, JavaScript, F #에서 접근하기 쉽고 접근하기 쉬운 세부 사항을 찾을 수 있지만 Lambda 미적분학은 방해받지 않고 문제를 해결합니다.
StevePoling

1
매번 약간 다른 표현으로 포인트를 여러 번 반복 해 주셔서 감사합니다. 개념 강화에 도움이됩니다. 더 많은 사람들이 이것을하기를 바랍니다.
johnklawlor

175

대부분의 사람들은 함수 를 생각할 때 명명 된 함수 를 생각 합니다 .

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

이들은 물론 이름으로 불립니다.

foo(); //returns the string above

람다 식을 사용하면 익명 함수를 가질 수 있습니다 .

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

위의 예제에서 할당 된 변수를 통해 람다를 호출 할 수 있습니다.

foo();

그러나 익명 함수를 변수에 할당하는 것보다 더 유용한 것은 변수를 상위 함수, 즉 다른 함수를 수락 / 반환하는 함수에 전달하거나 전달하는 것입니다. 이러한 많은 경우에 함수 이름 지정은 불필요합니다.

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)}); 

폐쇄 명명 또는 익명 기능하지만, 그 자체가 공지되어 될 수있다 때 기능 즉, 폐쇄가 여전히에서 사용되는 모든 외부 변수 환경 참조되며, 정의 된 범위의 변수 그것을 "위에 폐쇄" 폐쇄 자체. 명명 된 폐쇄는 다음과 같습니다.

@x = 0;

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

incrementX(); // x now equals 1

그다지 좋아 보이지는 않지만 이것이 모두 다른 함수에 있고 incrementX외부 함수에 전달 되면 어떻게 될까요?

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)

이것이 함수형 프로그래밍에서 상태 저장 객체를 얻는 방법입니다. "incrementX"라는 이름을 지정할 필요가 없으므로이 경우 람다를 사용할 수 있습니다.

function foo()
 { @x = 0;

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

14
여기에 어떤 언어가 있습니까?
Claudiu

7
기본적으로 의사 코드입니다. 여기에는 변수 선언 연산자의 이름을 딴 "@"( "at")이라는 언어와 함께 lisp와 JavaScript가 있습니다.
Mark Cidade

3
@MarkCidade,이 언어는 어디에 있습니까? 문서와 다운로드가 있습니까?
Pacerier

5
Javascript를 사용하고 앞에 @ 기호로 변수를 선언하는 제약 조건을 추가하지 않는 이유는 무엇입니까? 시간을 조금 절약 할 수 있습니다 :)
Nemoden

5
@Pacerier : 언어를 구현하기 시작했습니다 : github.com/marxidad/At2015
Mark Cidade

54

모든 클로저가 람다 인 것은 아니며 모든 람다가 클로저 인 것은 아닙니다. 둘 다 기능이지만 반드시 익숙한 방식은 아닙니다.

람다는 본질적으로 함수를 선언하는 표준 방법이 아니라 인라인으로 정의 된 함수입니다. 람다는 종종 객체로 전달 될 수 있습니다.

클로저는 본문 외부의 필드를 참조하여 주변 상태를 둘러싸는 함수입니다. 닫힌 상태는 닫힌 호출을 통해 계속 유지됩니다.

객체 지향 언어에서 클로저는 일반적으로 객체를 통해 제공됩니다. 그러나 일부 OO 언어 (예 : C #)는 상태를 둘러싸는 객체가없는 순수 기능 언어 (예 : lisp)가 제공하는 클로저 정의에 더 가까운 특수 기능을 구현 합니다.

흥미로운 점은 C #에 Lambdas and Closures가 도입되면서 기능 프로그래밍이 주류 사용에 더 가까워진다는 것입니다.


그렇다면 클로저는 람다의 부분 집합이고 람다는 함수의 부분 집합이라고 말할 수 있습니까?
sker

클로저는 람다의 부분 집합이지만 람다는 일반 함수보다 더 특별합니다. 내가 말했듯이 람다는 인라인으로 정의됩니다. 본질적으로 다른 함수에 전달되거나 반환 값으로 반환되지 않는 한 참조하는 방법은 없습니다.
Michael Brown

19
람다와 클로저는 각각 모든 함수의 부분 집합이지만 람다와 클로저 사이에는 교차점이 있습니다. 클로저의 교차하지 않는 부분은 클로저 인 함수로 명명되고 교차하지 않는 람다는 완전히 포함 된 자체 포함 함수입니다. 바운드 변수.
Mark Cidade

제 생각에는 람다는 함수보다 더 근본적인 개념입니다. 그것은 실제로 프로그래밍 언어에 달려 있습니다.
Wei Qiu

15

다음과 같이 간단합니다. lambda는 언어 구성, 즉 단순히 익명 함수에 대한 구문입니다. 클로저는이를 구현하는 기술 또는 그 문제에 대해 이름이 있거나 익명 인 모든 일류 함수입니다.

보다 정확하게는 클로저는 런타임에 퍼스트 클래스 함수 가 "코드"와 해당 코드에 사용 된 모든 비 로컬 변수에 대해 "닫는"쌍으로 표현되는 방식입니다. 이런 식으로 변수가 시작된 외부 범위가 이미 종료 된 경우에도 해당 변수에 여전히 액세스 할 수 있습니다.

불행히도, 함수에는 일급 값으로 기능을 지원하지 않거나 주름진 형태로만 지원하는 많은 언어가 있습니다. 따라서 사람들은 종종 "실제"라는 용어를 "실제"를 구별하기 위해 사용합니다.


12

프로그래밍 언어의 관점에서 보면 완전히 다른 두 가지입니다.

기본적으로 Turing 완전한 언어의 경우 추상화, 적용 및 축소와 같이 매우 제한된 요소 만 필요합니다. 추상화 및 응용 프로그램은 lamdba 식을 구축 할 수있는 방법을 제공하며 축소는 람다 식의 의미를 결정합니다.

Lambda는 계산 프로세스를 추상화 할 수있는 방법을 제공합니다. 예를 들어, 두 숫자의 합을 계산하기 위해 두 개의 매개 변수 x, y를 가져 와서 x + y를 반환하는 프로세스를 추상화 할 수 있습니다. 계획에서는 다음과 같이 쓸 수 있습니다.

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

매개 변수의 이름을 바꿀 수는 있지만 완료 한 작업은 변경되지 않습니다. 거의 모든 프로그래밍 언어에서 람다 식에 함수라는 이름을 지정할 수 있습니다. 그러나 큰 차이는 없지만 개념적으로 구문 설탕으로 간주 될 수 있습니다.

이제 이것이 어떻게 구현 될 수 있는지 상상해보십시오. 람다 식을 일부 식에 적용 할 때마다

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

매개 변수를 평가할 표현식으로 간단히 대체 할 수 있습니다. 이 모델은 이미 매우 강력합니다. 그러나이 모델을 사용하면 기호 값을 변경할 수 없습니다. 예를 들어 상태 변경을 모방 할 수 없습니다. 따라서 더 복잡한 모델이 필요합니다. 간단히 말해서 람다 식의 의미를 계산할 때마다 기호 쌍과 해당 값을 환경 (또는 테이블)에 넣습니다. 그런 다음 표에서 해당 기호를 찾아 나머지 (+ xy)를 평가합니다. 이제 환경에서 직접 작동 할 기본 요소를 제공하면 상태 변경을 모델링 할 수 있습니다!

이 배경으로 다음 기능을 확인하십시오.

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

우리는 람다 식을 평가할 때 xy가 새로운 테이블에 묶일 것이라는 것을 알고 있습니다. 그러나 어떻게 그리고 어디에서 z를 찾을 수 있습니까? 실제로 z는 자유 변수라고합니다. z가 포함 된 외부 환경이 있어야합니다. 그렇지 않으면 표현식의 의미는 x와 y를 바인딩하여 결정할 수 없습니다. 이를 분명히하기 위해 구성표에 다음과 같이 무언가를 쓸 수 있습니다.

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

따라서 z는 외부 테이블에서 1에 바인딩됩니다. 우리는 여전히 두 개의 매개 변수를 허용하는 함수를 얻지 만 그 의미는 외부 환경에 달려 있습니다. 다시 말해, 외부 환경은 자유 변수로 닫힙니다. set!의 도움으로 함수 상태를 상태화할 수 있습니다. 즉 수학적인 의미의 함수가 아닙니다. 그것이 반환하는 것은 입력뿐만 아니라 z에도 달려 있습니다.

이것은 당신이 이미 잘 알고있는 것입니다. 객체의 방법은 거의 항상 객체의 상태에 의존합니다. 그렇기 때문에 어떤 사람들은 "폐쇄는 가난한 사람의 물건"이라고 말합니다. 그러나 우리는 일류 함수를 정말로 좋아하기 때문에 물건을 가난한 사람의 닫힌 것으로 간주 할 수도 있습니다.

나는 그 체계로 인해 아이디어를 설명하기 위해 체계를 사용합니다.이 체계는 실제로 가장 가까운 언어 중 하나입니다. 여기에있는 모든 자료는 SICP 3 장에 훨씬 더 잘 소개되어 있습니다.

요약하면 람다와 클로저는 실제로 다른 개념입니다. 람다는 함수입니다. 클로저는 람다와 람다를 닫는 해당 환경의 쌍입니다.


자유 변수가 더 이상 없을 때까지 중첩 된 람다로 모든 클로저를 대체 할 수 있습니까? 이 경우 클로저는 특별한 종류의 람다로 볼 수 있다고 말하고 싶습니다.
Trilarion

8

개념은 위에서 설명한 것과 동일하지만 PHP 배경 출신이라면 PHP 코드를 사용하여 자세히 설명합니다.

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

함수 ($ v) {return $ v> 2; }은 람다 함수 정의입니다. 변수에 저장할 수도 있으므로 재사용 할 수 있습니다.

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

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

필터링 된 배열에서 허용되는 최대 수를 변경하려면 어떻게해야합니까? 다른 람다 함수를 작성하거나 클로저를 작성해야합니다 (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));

클로저는 자체 환경에서 평가되는 함수로, 함수가 호출 될 때 액세스 할 수있는 하나 이상의 바운드 변수가 있습니다. 그것들은 많은 개념이 작용하는 기능적 프로그래밍 세계에서 왔습니다. 클로저는 람다 함수와 비슷하지만 클로저가 정의 된 외부 환경의 변수와 상호 작용할 수 있다는 점에서 더 똑똑합니다.

다음은 PHP 클로저의 간단한 예입니다.

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

$closure();

이 기사에서 잘 설명했습니다.


7

이 질문은 오래되었고 많은 답변을 얻었습니다.
이제는 비공식 폐쇄 프로젝트 인 Java 8 및 Official Lambda를 사용하여 질문을 되살 렸습니다.

Java 컨텍스트에서의 답변 ( Lambdas 및 클로저 를 통해 -차이점은 무엇입니까? ) :

"클로저는 각 자유 변수를 값에 바인딩하는 환경과 쌍을 이루는 람다 식입니다. Java에서는 람다식이 클로저를 통해 구현되므로 두 용어가 커뮤니티에서 상호 교환 가능하게 사용되었습니다."


Lamdas는 Java에서 클로저로 어떻게 구현됩니까? Lamdas 표현식이 이전 스타일의 익명 클래스로 변환되었음을 의미합니까?
hackjutsu

5

간단히 말해서, 클로저는 범위에 대한 트릭이며 람다는 익명의 함수입니다. 우리는 람다로 더 우아하게 닫을 수 있고 람다는 종종 더 높은 함수에 전달되는 매개 변수로 사용됩니다


4

Lambda 표현식은 익명 함수일뿐입니다. 예를 들어 일반 자바에서는 다음과 같이 작성할 수 있습니다.

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

여기서 Function 클래스는 Java 코드로 작성되었습니다. 이제 당신은 mapPersonToJob.apply(person)그것을 사용하기 위해 어딘가에 전화 할 수 있습니다 . 그게 단지 하나의 예입니다. 그것은 구문이 있기 전에 람다입니다. 람다는 바로 가기입니다.

폐쇄:

Lambda는이 범위 밖의 변수에 액세스 할 수있을 때 클로저가됩니다. 나는 당신이 그 마법을 말할 수 있다고 생각합니다. 마 법적으로 그것이 만들어진 환경을 감싸고 범위 밖에서 변수를 사용할 수 있습니다 (외부 범위입니다. 그래서 클로저는 람다가 외부 범위에 액세스 할 수 있음을 의미합니다.

Kotlin에서 람다는 항상 해당 폐쇄 (외부 범위에있는 변수)에 액세스 할 수 있습니다.


0

함수가 외부 변수를 사용하여 작업을 수행하는지 여부에 따라 다릅니다.

외부 변수 -함수 범위 밖에서 정의 된 변수

  • Lambda 식은 작업을 수행하기위한 매개 변수, 내부 변수 또는 상수에 의존하므로 상태 비 저장 입니다.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • 클로저 는 외부 변수 (예 : 함수 본문의 범위 밖에서 정의 된 변수)와 매개 변수 및 상수를 사용하여 작업을 수행하기 때문에 상태를 유지 합니다.

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

Java가 클로저를 작성할 때 변수 n을 함수와 함께 유지하므로 다른 함수에 전달되거나 어디서나 사용될 때 참조 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.