IT_Programming/JavaScript

[펌] 클로져(Closure) is what?

JJun ™ 2010. 3. 25. 00:36

==================================================================================================

출처: http://rhio.tistory.com/208

==================================================================================================

 

Scope에 제약을 받지 않는 변수들을 포함하고 있는 코드블록이다.

이러한 변수들은 코드블럭이나 글로벌 컨텍스트에서 정의되지 않고 코드 블록이 정의된 환경에서 정의된다.

"클로져"라는 명칭은 실행할 코드블록(자유 변수의 관점에서, 변수 레퍼런스와 관련하여 폐쇄적이지 않는) 과 자유변수들에 대한 바인딩을 제공하는 평가 환경(범위)의 결합에서 탄생한 것이다.


사실 저런 이야기 매우 어렵습니다. 그래서 좀더 어렵고 혼돈되게 책에 있는 내용을 인용합니다.

From javascript definitive guide 5th


This combination of code and scope is known as a closure (…).

All Javascript functions are closures.

When a nested function is exported outside the scope in which it is defined

When a nested function is used in this way, it is often explicitly called a closure

 


이 코드와
스코프의 조합은 클로져 알려져 있습니다.
자바스크립트 모든 함수들은
클로져입니다.

중첩합수가 함수 정의하는 것을 스코프 밖에서 이루어질 때 입니다.

중첩함수를 이 방식으로 사용할 때 그것은 자주 명백하게 클로져 불러지고

있습니다.

 


 

2008.10.14 추가내용

When a nested function is exported outside the scope in which it is defined

"중첩된 함수가 그 함수가 정의된 유효 범위의 바깥으로 익스포트(export)될 때다."

 (한글 번역서p.191)<낭망백수님 의견>


 

매우 혼동스러워 집니다.  그래서 클로져라는게 도대체 무엇인지 개념적으로 A = B이다. 라고

답을 낸다는 것은 너무 단편적인 결론을 요구하는 것이기 때문에 클로져의 개념을 이해하는 것을

목적으로 둡니다..

좀더 빠른 이해를 돕기 위해서 그림으로 표현해봅니다.

  


outerFn()은 우리가 알고 있는 function입니다.   function을 수행하게 되면 var을 통해서 function 내에서만

사용되거나 설정되는 variable을 가집니다. 이는 달리 말해 function scope에서만 참조되는 변수이지만

경우에 따라 클로져(closure)에 의해서 참조가 가능해진다는 것입니다.

outerFn 내의 var closure='is Closure.'; 는 메모리에 적재를 하게 되고 이는 outerFn이 수행될 때

해당 메모리를 참조하게 되고 수행이 종료되고 참조가 없어지면 자동으로 GC(가비지 컬렉션)이 일어나

메모리에서 해제되게 됩니다.

하지만 var closure = 'is Closure.'; 를 어디선가 참조를 하고 있다면 아니 그전에 다른곳에서

참조가 발생할 때 바로 클로져가 생성되면서 클로저를 통해서 해당 variable를 참조하게 됩니다.

위에서 볼때 func에는 outerFn에 의해서 반환된 function을 가지고 있습니다. 이 function은 outerFn이

가지고 있는 내부 variable를 참조하고 있습니다. 이 때 outerFn이 갖은 variable을 외부에서 참조하려고

하니 쉽게 private를 public으로 참조하려고 하니 과연 되지 않아야 하지만 우리의 javascript의 유연함은

이뤄 말할 수 없을 만큼의 기능을 가지고 있습니다.

아무튼 그렇게 클로져는 이런 상태에서 private를 참조하려고 시도할 때 발상하여 해당 참조를 해야하는 function의 즉 outerFn의 코드블럭을 클로져를 통해서 참조할 수 있게 됩니다. 

클로져 자체가 코드블럭이라 해도 과언이 아니겠습니다.

위의 그림에서는 클로져가 발생했고 이는 GC에 의해서 메모리 해제가 되지 않습니다. 

즉 func 이 계속적으로 클로져를 통하여 var closure = 'is Closure.'; 를 계속 참조하고 있기 때문입니다. 

그래서 우리는 강제적으로 클로저를 해제할 수 있는 상태를 만들어 주어야 합니다.

var func = outerFn(); 을 하고. func('function'); 을 통해서 원하는 결과 값을 얻었다면 var func = null; 을

통해서 클로져를 통한 참조 point를 null 시켜주게 되면 주기적으로 GC에 의해서 메모리에서 반환되게

됩니다.


결론

  Scope에 제약을 받지 않는 변수들을 포함하고 있는 코드블록이다.
그리고 이는 Javascript에서 메모리 누수를 발생하는 요인중에 하나이다.



참조 :
 1,
http://www.ibm.com/developerworks/kr/library/j-jtp04247.html

 2. http://en.wikipedia.org/wiki/Closure_%28computer_science%29


 

 


 

 

[클로저의 부작용]

 

 

 window.onload = function() {
     for(var i=1; i < 5; i++ ) {
         document.getElementById("btn" + i).addEventListener("click", function() { alert(i); }, false);
     }
 }

 

 

모두는 아니겠지만 보통 위와같은 코드를 시도하리라고 생각하고 정상적으로 동작할 것을 기대하지만

위 코드는 제대로 동작하지 않습니다.


위의 예제는 클로저의 생성으로 인한 부작용을 보여줍니다.
원래 의도는 각 버튼마다 alert!시에 1,2,3,4를 결과로 보여주려는 의도이나 이벤트 핸들러 함수의 i값이

바깥쪽 변수인 i값에 대한 참조를 유지하고 있어, 즉 클로저의 생성으로 인해 최종값인 5를 모두 가리키게 되는 예제입니다.


이것의 부작용을 해결한 방법은 아래와 같습니다.

 

 

 

 window.onload = function() {
     for(var i=1; i < 5; i++ ) {
         (function(m)
          {
             document.getElementById("btn" + m).addEventListener("click", function() { alert(m); },

                                                                                            false);
          }
        )(i);
     }
 }

 

 

위의 예제는 클로저의 부작용을 막기위한 처리로 제시한 예제인데,

이 예제도 클로저가 생성되긴 합니다만 익명함수의 인자로 값을 넘겨버림으로써 바깥쪽 변수인 i에 대한

변수 스코프를 끊어버리고, 이벤트 핸들러에서는 익명함수의 인자값에 접근함으로써 의도한 대로 처리가

되게 됩니다. 괄호로 둘러싼 함수 표현식 안에서는 바깥쪽 변수에 접근하지 못한다는 것을 여기서 알 수

있습니다.

 

이렇게 하면 익명 함수안의 내부변수인 m에 i의 값이 할당되어 버리고 구조상은 실행 뒤에 소멸되어야

하지만 클로저로 인하여 각 값으로 할당된 멤버변수를 각 이벤트 리스너에서 그대로 사용할 수 있게 됩니다.

 

 


 

 

 

클로저 (Closure) - 마틴 파울러

원문 - http://www.martinfowler.com/bliki/Closure.html

클로저 (Closure) 마틴 파울러 (Martin Fowler)

동적 언어에 대한 관심이 커짐에 따라 많은 사람들이 클로저 (Closure) 혹은 블록 (Block) 이라는 프로그래밍 개념을 접하게 되었다. 그런데, 클로저를 지원하지 않는 C/C++/Java/C# 같은 언어들에 익숙한 사람들은 그게 무엇인지 잘 알지 못한다. Here's a brief explanation, those who done a decent amount of programming in languages that have them won't find this interesting.

클 로저란 개념은 오래전부터 여러 프로그래밍 언어들 속에 있었다. 스몰토크(Smalltalk) 에서는 블록이라는 이름으로 불리는데, 필자는 이 스몰토크를 통해 처음으로 클로저를 사용했다. Lisp 은 클로저가 비중있게 사용되는 언어이다. 또한 스크립트 언어인 루비 (Ruby) 에서도 클로저가 사용이 되는데, 이는 프로그래머들이 루비를 좋아하는 가장 큰 이유이기도 하다.

기본적 으로 클로저란 함수 호출시 인자로 전달 될 수 있는 코드 블록을 말한다. 간단한 예를 들어 설명하겠다. 직원 (employee) 객체들의 리스트가 있을때, 이 리스트에서 관리자 (manager) 객체들의 리스트만 얻고자 한다. 직원 객체가 관리자 객체인지 판별하는 메소드는 IsManager 라고 하자. C# 으로는 다음과 같이 쓸수 있다.

public static IList Managers(IList emps) {
      IList result = new ArrayList();
      foreach(Employee e in emps)
            if (e.IsManager) result.Add(e);

       

      return result;

}

그런데, 클로저를 지원하는 언어(여기서는 루비를 사용한다) 사용하면, 다음과 같이 쓸수 있다.

def managers(emps)
      return emps.select {|e| e.isManager}
end

select 는 루비의 컬렉션 클래스에서 정의되는 메소드이다. select 메소드는 코드 블록, 즉 클로저를 인자로 받는다. 루비에서는 이 코드 블록을 괄호사이에 작성한다. (다른 방법도 있긴 하다.) 이 코드 블록이 인자를 필요로 한다면, 인자를 수직 바 '|' 사이에 선언해주면 된다. select 가 하는 일은 입력 배열의 각 원소들에 대해 이 코드 블록을 실행하고, 실행 결과가 참인 원소들의 리스트를 반환하는 것이다.

이쯤해서, 만약 당신이 C 프로그래머라면 아마도 "함수 포인터를 이용해서 똑같이 할 수 있어" 라고 생각할지도 모른다. Java 프로그래머라면 "익명 내부 클래스를 이용해서 똑같이 할 수 있어" 라고 생각할지 모른다. C# 프로그래머라면 위임 (Delegate) 를 고려해 볼 것이다. 이런 방식들은 클로저와 유사하긴 하지만 2가지 점에서 클로저와 다르다.

첫번째는 형식적인 차이다. 클로저는 그것이 처음 정의되는 시점에서 볼수 있는 변수들을 참조할 수 있다. 다음 메소드를 보자.

def highPaid(emps)
      threshold = 150
      return emps.select {|e| e.salary > threshold}
end

select 내의 코드 블록이 메소드의 지역 변수를 참조하고 있음을 눈여겨 보길 바란다. 진정한 클로저를 지원하지않는 언어들에서 사용되는 클로저와 비슷한 기능 들 (이를테면 앞서 얘기한 함수 포인터나 익명 내부 클래스, 위임 등, 역자 주) 은 불가능한 일이다. 클로저는 이보다 더 흥미로운 일을 할 수 있도록 해준다. 다음 함수를 보자.

def paidMore(amount)
      return Proc.new {|e| e.salary > amount}
end

이 함수는 클로저를 리턴한다. 그렇다. 내부로 전달된 인자에 의해 동작이 결정되는 클로저를 리턴한다. 이런 함수를 만들고 이를 변수에 할당하는게 가능하다.

highPaid = paidMore(150)

highPaid 변수는 150이상의 급여를 갖는 객체를 테스트하는 코드 블록 (루비의 Proc 에 의해 만들어진) 을 갖게 된다. 이를 가지고 다음과 같이 사용할 수 있을 것이다.

john = Employee.new
john.salary = 200
print highPaid.call(john)


highPaid.call (john) 이라는 문장은 앞서 정의한 대로 e.salary > amount 라는 코드를 호출한다. 여기서 amount 변수는 proc 객체를 생성할 때 인자로 전달한 150 이라는 값을 갖고 있다. print 호출이 일어나는 지점은 150 이라는 값의 범위 (Scope) 에서 벗어나지만, 여전히 바인딩은 지속되고 있다.

이처럼, 클로저의 첫번째 중요한 점은 클로저는 처음 만들어진 환경에서의 바인딩이 더해진 코드 블록이라는 것이다. 이는 클로저가 함수 포인터나 다른 유사한 기술들 (Java 의 익명 클래스도 지역 변수에 접근할 수 있지만 오직 final 일 때만 가능하다.) 과 구별되도록 해주는 형식적인 부분이다.

두번째 차이점은 형식적인 차이점보다는 덜 실용적이다는 점에서 작지만 그래도 여전히 중요하다. 클로저를 지원하는 언어들은 매우 적은 문법으로 그것들을 정의할 수 있도록 해준다. 그다지 중요하지 않게 보일지 모르지만 나는 중요하다고 믿는다. 이와 같은 단순함이 클로저를 자주 사용할 수 있도록 해주는 것이다. Lisp 과 스몰토크, 루비를 보자. 다른 언어에서 그와 유사한 구조들 보다 더욱 자주 클로저가 사용되는 것을 볼 수 있다. 지역 변수바인딩 기능은 클로저가 자주 사용되는 이유 중 하나이지만 그보다 더 큰 이유는 바로 단순하고 명료한 문법때문이다.

이전의 스몰토크 프로그래머들이 자바를 사용하기 시작했을때 일어난 일들은 이에 대한 좋은 예다. 처음엔 필자를 포함해서 많은 사람들이 스몰토크에서 블록으로 해왔던 많은 일들을 자바의 익명 내부 클래스를 사용하여 작업을 했었다. 그러나 그 결과 코드는 매우 어지럽고 보기 흉해서 결국 우리는 그것을 포기했다.

필자는 루비를 가지고 클로저를 자주 사용한다. 그러나, Proc 를 생성하고 그것을 전달하는 방식은 잘 쓰지 않는다. 주로 클로저를 사용하는 용도는 앞서 보여줬던 select 메소드와 유사한 CollectionClosureMethod 를 사용할 때이다. 다른 주요 용도로는 파일을 처리할 때와 같은 '메소드와 함께 실행하기 (excure around method)' 이다.

File.open(filename) {|f| doSomethingWithFile(f)}

여 기서 open 메소드는 파일을 열고, 제공된 블록을 실행한 다음, 다시 파일을 닫는다. 이것은 트랜잭션 (커밋이나 롤백을 빼먹지 않도록) 혹은 끝에 무언가를 해야한다는 것을 반드시 기억해야하는 어느 경우에라도 사용될 수 있는 편리한 기법이다. 필자는 XML 변환 루틴을 작성하면서 이 기법을 광범위하게 사용하고 있다.

이와 같은 클로저의 사용은 사실 Lisp 과 기능적 프로그래밍 세계에서 사람들이 사용하는 것에 비해 그 용도가 매우 적은 편이다. 그러나 필자는 이 용도가 제한적임에도 클로저를 지원하지 않는 다른 언어를 가지고 프로그래밍을 할때면 클로저가 매우 아쉽다. 처음 클로저를 접하는 사람은 이게 별것 아닌 것처럼 보여지지만 오래지 않아 클로저를 좋아하게 된다.

 


다른 언어에서의 예제들

Joe Walnes 는 C# 다음 버젼의 클로저에 대한 블로그를 남겼다. 정적 타입 언어에서 요구되는 문법을 고려해볼때 C# 의 클로저 지원은 매우 합리적이다. 이는 위임에 기반해 있으며 delegate 키워드를 이용하여 클로저를 구현할 수 있다. (http://joewalnes.com/2004/09/16/the-power-of-closures-in-c-20/)

 

더보기

 Martin Fowler (obligitary Fowlbot namedrop) recently blogged about the power of closures in 

 languages that support them. It’s worth remembering that C# 2.0 has true closure support in the form

 of anonymous delegates. This includes reading and modifying variables outside the context of the

 closure – unlike Java’s anonymous inner classes.

 

 Just for kicks, I’ve rewritten all of the examples Martin’s Ruby examples in C# 2.0. This makes use

 of the improved APIs in .NET 2.0 pointed out by Zohar.

 

Ruby C# 2.0
def managers(emps)
return emps.select {|e| e.isManager}
end
public List<Employee> Managers(List<Employee> emps) {
return emps.FindAll(delegate(Employee e) {
return e.IsManager;
}
}
def highPaid(emps)
threshold = 150
return emps.select {|e| e.salary > threshold}
end
public List<Employee> HighPaid(List<Employee> emps) {
int threshold = 150;
return emps.FindAll(delegate(Employee e) {
return e.Salary > threshold;
});
}
def paidMore(amount)
return Proc.new {|e| e.salary > amount}
end
public Predicate<Employee> PaidMore(int amount) {
return delegate(Employee e) {
return e.Salary > amount;
}
}
highPaid = paidMore(150)
john = Employee.new
john.salary = 200
print highPaid.call(john)
Predicate<Employee> highPaid = PaidMore(150);
Employee john = new Employee();
john.Salary = 200;
Console.WriteLine(highPaid(john));

The code difference between the languages isn’t that difference. The C# 2.0 code is obviously longer (though not a lot) because:

  • C# 2.0 is staticly typed (let’s not get started on the static vs dynamic debate).
  • C# 2.0 requires the ‘delegate’ keyword.
  • Ruby allows you to ignore the ‘return’ keyword.

You can try this stuff out yourself by playing with Visual C# Express.

 


Ivan Moore 는 Python 에서 유사한 예제들을 제공한다. (http://ivan.truemesh.com/)
더보기

Closures in Python (part 1)

 Martin Fowler (obligitary Fowlbot namedrop) recently blogged about the power of closures   in  languages that support them. Here's a translation of Martin's Ruby code into Python. [Initial sentence and table structure copied from Joe Walnes] (not knowing how to get this to format correctly is my fault. I want to write a part 2, but that'll have to wait as I have other commitments (who are currently calling for me!) (now done)
Ruby Python (Direct translation, using "lambda")
def managers(emps)
  return emps.select {|e| e.isManager}
end
def managers(emps): 
        return filter(lambda e: e.isManager, emps) 
def highPaid(emps)
  threshold = 150
  return emps.select {|e| e.salary > threshold}
end
def highPaid(emps): 
        threshold = 150 
        return filter(lambda e: e.salary > threshold, emps) 
def paidMore(amount)
  return Proc.new {|e| e.salary > amount}
end
def paidMore(amount): 
        return lambda e: e.salary > amount
highPaid = paidMore(150)
john = Employee.new
john.salary = 200
print highPaid.call(john)
highPaid = paidMore(150)
john = Employee()
john.salary = 200
print highPaid(john)
Ruby Python (Idiomatic translation, using "list comprehensions")
def managers(emps)
  return emps.select {|e| e.isManager}
end
def managers(emps): 
        return [e for e in emps if e.isManager]
def highPaid(emps)
  threshold = 150
  return emps.select {|e| e.salary > threshold}
end
def highPaid(emps): 
        threshold = 150 
        return [e for e in emps if e.salary > threshold]
Posted by ivan at September 19, 2004 9:22 AM
Copyright (c) 2004-2008 Ivan Moore
Comments

I'm new too python, so used lambda in this simple wc.py app..

I was wondering if there would be a better way to do this??? Thanks..


import sys
files = map(lambda f: open(f), sys.argv[1:])
Twords = Tlines = Tchars = 0
for file in files:
   words = lines = chars = 0
   for line in file.xreadlines():
      lines += 1
      words += len(line.split())
      chars += len(line)
   print lines, words, chars, file.name

   Twords += words
   Tlines += lines
   Tchars += chars
if len(sys.argv) > 2:
   print " total" % (Tlines, Twords, Tchars)

Posted by: Rick Beacham at January 26, 2005 8:28 AM

You could write:

  files = [open(f) for f in sys.argv[1:]]

  instead of:

    files = map(lambda f: open(f), sys.argv[1:])

    (ie. use a "list comprehension" - I think it reads a bit easier than using the map/lambda).

 

 Ivan

 

 


 

 

Closures in Python (part 2)

Closures in Python (part 2)

This assumes that you've read Martin Fowler's article on closures. Part 1 shows a translation of Martin Fowler's Ruby code into Python, both a direct translation and a more idomatic translation using Python's "list comprehensions" (which are arguably neater for doing lots of the sorts of things that you use closures for in Smalltalk or Ruby). From this, you might think that Python can handle closures like Ruby or Smalltalk can, but this isn't quite the case.

Limitation of lambda

In the non-list comprehension examples, the "lambda" keyword for creating a closure in Python can only be used with an expression, not any arbitrary code. This happens to be OK for the examples in Martin's article, but consider something just a tiny bit more complicated:

Let's say you wanted to do:


map(lambda each: if each.isManager: each.salary = 2000, employees)

You can't. "if each.isManager: each.salary = 2000" isn't an expression.
Instead, you'd have to define a function (which doesn't take much syntax):


def anonymousFunction(employee):
	if employee.isManager: employee.salary = 2000

then you can do:

	
map(anonymousFunction, employees)

(As an aside, "map" returns the collection of the result of executing the function on every element of a collection.

  We just want to execute the function and don't care about the return result; there's no equivalent to

  that in Python other than just ignoring the return result which is what we'll do. It's not a problem.)

Assignment considered awkward

There are other problems too. Consider the following code:


def totalCostOfManagers(emps):
	total = 0
	def anonymousFunction(employee):
		if employee.isManager: total = total + employee.salary
	map(anonymousFunction, emps)
	return total

This looks like it should give you the total of the manager's salaries. (Ignore the fact that there are other ways to do this,

it's just an example). Try to execute this and you get:

	
UnboundLocalError: local variable 'total' referenced before assignment

This is because the "total" inside "anonymousFunction" is different to the "total" inside "totalCostOfManagers".

When you do an assignment to a variable, it is created if it didn't already exist (in that scope).

(If I find a suitable reference I'll edit this and put it here).

Work-around for assignment

One way around this would be not to try to assign to "total" itself, but rather have "total" refer to a list and assign to an element in that:


def totalCostOfManagers(emps):
	total = [0]
	def anonymousFunction(employee):
		if employee.isManager: total[0] = total[0] + employee.salary
	map(anonymousFunction, emps)
	return total[0]

This is the sort of thing you might also do with an anonymous inner class in Java,

where you also can't do assignments to variables in an outer scope.

Making functions look like objects

A slightly subtle thing that I haven't mentioned at all so far is the difference between the "closures" you've see in Python and those in Smalltalk or Ruby. In Smalltalk a closure is an object defining a "value" method. That is, to execute the code of a Smalltalk closure, you'd send it the message "value", with parameters as appropriate. The equivalent in Python would be something like: (given that "emps" has a method "do" that accepts an object with a "value" method)


def totalCostOfManagers(emps):
	total = [0]
	class AnonymousClass:
		def value(self, employee):
			if employee.isManager: total[0] = total[0] + employee.salary
	emps.do(AnonymousClass())
	return total[0]

You can do the equivalent in Java using an anonymous inner class. (Note in Python that "self" (or "this") is explicit,

and also has to be included as the first parameter in method definitions.)

(If you want to try this out, you could use the following:


class Employee:
	pass
ivan = Employee()
ivan.name="ivan"
ivan.isManager = False
ivan.salary = 2
tim = Employee()
tim.name="tim"
tim.isManager = True
tim.salary = 5
class Employees:
    def __init__(self):
        self.emps = [tim, ivan]
    def do(self, block):
        for e in self.emps:
            block.value(e)

and execute:


print totalCostOfManagers(Employees())

to see it work.)

You might also consider:


def totalCostOfManagers(emps):
	class AnonymousClass:
		def __init__(self):
			self.total = 0
		def value(self, employee):
			if employee.isManager: self.total = self.total + employee.salary
	block = AnonymousClass()
	emps.do(block)
	return block.total

Note that "__init__" defines the constructor for AnonymousClass, which is called by doing "AnonymousClass()"

(there's no "new" keyword needed).

Making objects look like functions

In Smalltalk, closures look like objects with a "value" method. In Python it is more idomatic to use a function instead, as you've seen earlier. To invoke a Python function, you put "()" after it. So, back to basics; if you have a function "foo":


def foo():
	return "hi mum"

then "foo" is a reference to the function, and "foo()" executes the function, i.e. evaluating: "print foo"

results in something like "<function foo at 0x008F7970>" and evaluating "print foo()" results in "hi mum".

So, rather than defining "do" to accept an object with a "value" method, more idomatic would be to use the built in function "map" and pass it a function (as shown earlier). In Python, you can make any object look like a function by defining a "__call__" method. So back to the example, another way to implement it would be:


def totalCostOfManagers(emps):
	class AnonymousClass:
		def __init__(self):
			self.total = 0
		def __call__(self, employee):
			if employee.isManager: self.total = self.total + employee.salary
	block = AnonymousClass()
	map(block, emps)
	return block.total

(execute "print totalCostOfManagers([tim, ivan])", with "tim" and "ivan" defined as before, to see it work).

Note the regularity in Python of having functions/methods callable (e.g. "foo()"), classes callable (e.g. the constructor "AnonymousClass()" and instances callable (e.g. the instance of "AnonymousClass").

To clarify, try:


class Foo:
	def __call__(self):
		return "hi mum"
	def bar(self):
		return "yo dude"
someFoo = Foo()
print someFoo.bar()
print someFoo()

Admitting defeat for now, and moving onto something more difficult

OK - let's admit the truth - it's looking like a closure style isn't working so great here. Let's just revert to a simple loop:


def totalCostOfManagers(emps):
	total = 0
	for employee in emps:
		if employee.isManager: total = total + employee.salary
	return total

So - what was all that effort for? Why are closures such a Good Thing? Where a closure becomes really neat is

if you have to do something more complicated. For example, consider if you have to do something like this:


def totalCostOfManagers(emps):
	total = 0
	try:
		emps.startSomething()
		employee = emps.next()
		while(employee != None):
			if employee.isManager: total = total + employee.salary
			employee = emps.next()
	finally:
		emps.endSomething()
	return total

where "emps" is some object defining "startSomething", "endSomething" and "next" methods that have to be called like

in this method, and the "endSomething" has to be called whether you finish looping through all the employees or not.

(You can use:


class Employees:
	def __init__(self):
		self.emps = [tim, ivan]
	def startSomething(self):
		pass
	def endSomething(self):
		pass
	def next(self):
		if len(self.emps) == 0:
			return None
		return self.emps.pop()
print totalCostOfManagers(Employees())

to try this out. Of course, this is not how a real implementation would look, it's just to illustrate the calling method

"totalCostOfManagers")

Now for some duplication, and how to remove it.

This pattern is typical of some types of code, e.g. database related code. If you had to do lots of things similar to this, but slightly different, you can easily end up with lots of duplicated code.

For example:


def totalCostOfNonManagers(emps):
	total = 0
	try:
		emps.startSomething()
		employee = emps.next()
		while(employee != None):
			if not employee.isManager: total = total + employee.salary
			employee = emps.next()
	finally:
		emps.endSomething()
	return total

In Java, I've seen lots of code like this that is mostly duplicated with just a bit different. At first sight,

to many Java developers it might look like too much effort to remove the duplication, but actually it's not too hard. This is a case where closures really help (or anonymous inner classes in Java). You can put the bulk of this code where it belongs, in the Employees class:


class Employees:
	#whatever ...
	def do(self, fun):
            try:
                self.startSomething()
                employee = self.next()
                while(employee != None):
                        fun(employee)
                        employee = self.next()
            finally:
                self.endSomething()       

Then the definition of "totalCostOfManagers" doesn't need to worry about most of that stuff and looks just like

the code from earlier (but "emps" is an instance of "Employees" rather than a simple list):


def totalCostOfManagers(emps):
	total = [0]
	def anonymous(employee):
		if employee.isManager: total[0] = total[0] + employee.salary
	emps.do(anonymous)
	return total[0]

However, as we've seen earlier, this would be neater if we could write it as just a simple loop.

Generators

Python has a trick up it's sleeve, called a "generator". If we define "do" as:


class Employees:
	#whatever ...
	def do(self):
            self.startSomething()
            employee = self.next()
            while(employee != None):
                    yield employee
                    employee = self.next()
            self.endSomething() 

The "yield" keyword creates a "generator", which, as far as a "for" loop is concerned, is a method that looks like

it returns a list, and as far as the "generator" method is concerned, looks like a method that "returns" one element at a time. Unfortunately, "'yield' not allowed in a 'try' block with a 'finally' clause", so I've deleted that until someone can tell me a way around that limitation (arghhh!). Anyway, back to the "generator" version of "do"; the calling code now becomes:


def totalCostOfManagers(emps):
	total = 0
	for employee in emps.do():
		if employee.isManager: total = total + employee.salary
	return total

which is nice and simple again.

Generators as Iterators

Another tweak is also available. If we change the name of "do" to "__iter__":


class Employees:
	#whatever ...
	def __iter__(self):
            self.startSomething()
            employee = self.next()
            while(employee != None):
                    yield employee
                    employee = self.next()
            self.endSomething()   

Then, our calling code becomes:


def totalCostOfManagers(emps):
	total = 0
	for employee in emps:
		if employee.isManager: total = total + employee.salary
	return total

and we've gone back to something that looks really simple!

Our other method now becomes:


def totalCostOfNonManagers(emps):
	total = 0
	for employee in emps:
		if not employee.isManager: total = total + employee.salary
	return total

which removes much of the duplication. Removing the last of the duplicaion is left as an exercise for the reader.

Conclusion

I miss the really neat syntax of closures in Smalltalk, and the fact that everything just works like it should in Smalltalk with syntax that is truth and beauty. However, despite thinking that Smalltalk is a better language, I use Python in my work for various reasons.

I have never been able to choose the main implementation language for a commercial project. (Yes, now I think of it, really, never.) It's always been dictated either by what's already been done, or for projects that I've joined from the start, it's been chosen by someone else (someone else less technical than me, that is!). These days, that means Java or C#. However, even on these projects, there's always the need for little scripts for automating parts of the development process or doing other useful things. Python is better than Smalltalk for this sort of thing, as it's easy to share Python programs with other people, and to move them from machine to machine. You just copy the relevant text file, edit it in your favourite text editor if required, and "Python whatever.py" and it works. Smalltalk doesn't have that convenience.

Some people use shell scripts for that sort of thing, but Python is a very much better language, and also works cross platform. Compared to Perl or Ruby, Python has very little syntax, which makes it easy to read even if you haven't been doing much Python recently. I'm a "bear of very little brain" (or something like that), so I want to be able to read code without having to remember what the syntax means. With Python, that's very easy. I can also read other people's Python code, which helps!

Python now has a very large following, and it's easy to create Python wrappers for "C" programs, so the quality and quantity of libraries is fantastic. The main Python implementation itself is also stable and reasonably performant.

From a language point of view, Python is less scary to the majority of Java developers than Smalltalk (although that's a deficiency on their behalf rather than Smalltalk's, sometimes you have to make compromises to get something accepted). Also, people have heard of Python and aren't frightened by it. The common reaction is "oh, that's like Perl but more readable, isn't it?", whereas the reaction to Smalltalk tends to be fear. Python also has some nice language features, such as list comprehensions and generators, which make it easy to write code that's easy to read.

If you want to learn a language with good support for closures, and one of the best languages ever invented, try Smalltalk. If you want to use a language that's easy to learn and useful in your day-to-day work, try Python. If you want to improve as a developer, learn both.

 

 


 

 

Closures in Python (part 3)

Just a short addition to part 2 - I was writing some code the other day and came across another closure related problem with Python. I wanted to create some buttons, with event callbacks, dynamically; something like this:


class Foo:
   def __init__(self):
      self.callbacks = [lambda event: self.callback(name) for name in ['bob','bill','jim']]
   def callback(self, name):
      print name
   def run(self):
      for callback in self.callbacks:
         callback("some event")

Now, what do you suppose happens when you run "Foo().run()"?
You get:


jim
jim
jim

This is because there is only one "name" variable (so Nat Pryce tells me). A simple solution is to do:


class Foo:
   def __init__(self):
      self.callbacks = [self.makeCallback(name) for name in ['bob','bill','jim']]
   def makeCallback(self, name):
      return lambda event: self.callback(name)
   def callback(self, name):
      print name
   def run(self):
      for callback in self.callbacks:
         callback("some event")

I'm sure there are many other solutions too. For the curious, in Ruby the equivalent code would be:


class Foo
   def initialize()
      @callbacks = ['bob','bill','jim'].map {|name| Proc.new {|event| callback(name)}}
   end
   def callback(name)
      print name
   end   
   def run()
      @callbacks.each { |callback| callback.call("some event") }
   end
end
Foo.new.run()

which works just fine. However, I still chose Python over Ruby for pragmatic reasons (a bit ironic I know) - the libraries, tools and support are all superior.

 


Boo 언어에 대한 예제도 있다. (http://boo.codehaus.org/Closures)
더보기
Introduction

 

Martin Fowler has a good introduction to closures on his bliki.

Syntax

There are two syntaxes for closures: a block based syntax with syntactically significant  whitespace and a braces based that ignores whitespace.

 

Block based syntax

import System.Windows.Forms
button = Button(Text: "Click me")
button.Click += def ():
    print("${button} was clicked!")
    print("and yes, this is just like any other block...")

 

Braces based

button1 = Button(Text: "Click me", Click: { print("clicked!") })
button2 = Button(Text:
"Me too!")
button2.Click = { print(
"${button2} was clicked!");
    print(
"whitespace is ignored inside {}...");
        print(
"that's why you need to use semicolons to include multiple statements...");
    print(
"but please, dont write code like this just becase you can :)")
}

 

Semantics

Boo closures have have full access (to read and write) to their enclosing lexical environment. For Instance:
a = 0 # declare a new variable
getter = { return a }
setter = { value | a = value }
assert 0 == getter()
setter(42)
assert 42 == getter()
assert 42 == a

The best source of information right now are the test cases for closures in the tests/testcases/integration directory.

Closures vs. Functions

See Functions As Objects.

Some things you can do with named functions that you cannot with closures include recursion and overloading:

This will not work because "c" is unknown from inside the closure:

c = do(x as int):
	print x
	--x
	c(x) if x > 0
c(5)

so you can use a regular named function or else create a 2nd callable to hold the name:

def c(x as int):
	print x
	--x
	c(x) if x > 0
c(5)
//or:

d as callable
c = do(x as int):
	print x
	--x
	d(x) if x > 0
d = c
c(5)

And you can use regular named functions to overload a method:

def doit(x as int):
    print x
def doit(x as string):
    print x
doit(3)


 

 

 

Martin Fowler's closure examples in boo

Below are boo versions of the examples in Martin Fowler's blog on closures:

class emp:
	public isManager = false
	public name = ""
	public salary = 100
	def ToString():
		return name
def managers(emps as List):
	return emps.Collect({ e as emp | return e.isManager})
def highPaid(emps as List):
	threshold = 150
	return emps.Collect({ e as emp | return e.salary > threshold})
def paidMore(amount as int):
	return { e as emp | return e.salary > amount}
emps = [emp(name:"Bill", isManager: true), emp(name: "Sally"), emp(name: "Molly", isManager: true)]
print managers(emps)
highPaid2 = paidMore(150)
john = emp(name: "John")
john.salary = 200
print highPaid2(john)

 

 

 

Lisp 에 대한 예제도 있다. (http://stefanroock.blogspot.com/2006/08/closures-in-common-lisp.html)

더보기

Closures in Common Lisp

Martin Fowler published an online article about closures. He used the Ruby programming language for the examples. There are translations of the examples to Python and a language called Boo.

As a programming task for myself I did the examples in Common Lisp, which supported Closured already decades ago. A real lisper might be able to write the code in a more elegant way but I think the principle should become clear.

I didn't use the Common Lisp Object System (CLOS) since for this simple example an ad-hoc modeling with hashes is simpler.


;; Ruby
; def managers(emps)
; return emps.select {|e| e.isManager}
; end

;; Common Lisp
(defun is-manager (emp) (getf emp :is-manager))

(defun managers (emps)
(remove-if-not (lambda (emp)
(when (is-manager emp) emp))
emps))


;; Ruby
; def highPaid(emps)
; threshold = 150
; return emps.select {|e| e.salary > threshold}
; end

;; Common Lisp
(defun high-paid (emps)
(let ((threshold 150))
(remove-if-not (lambda (emp)
(when (> (getf emp :salary) threshold) emp))
emps)))


;; Ruby
; def paidMore(amount)
; return Proc.new {|e| e.salary > amount}
; end

;; Common Lisp
(defun paid-more (amount)
(lambda (emp) (when (> (getf emp :salary) amount) emp)))

;; Ruby
; highPaid = paidMore(150)
; john = Employee.new
; john.salary = 200
; print highPaid.call(john)

;; Common Lisp
(let ((high-paid (paid-more 150))
(john '(:name John :is-manager nil :salary 200)))
(princ (funcall high-paid john)))


;; Tests
(defparameter manager-list
'((:name Stefan :is-manager nil :salary 150)
(:name Henning :is-manager t :salary 151)
(:name Martin :is-manager nil :salary 120)
(:name Christian :is-manager t :salary 200)))

(princ #\newline)
(princ "Test function managers")
(princ #\newline)
(princ (managers manager-list))
(princ #\newline)

(princ #\newline)
(princ "Test function high-paid")
(princ #\newline)
(princ (high-paid manager-list))
(princ #\newline)

Post bewerten

 

아래는 JavaScript에 대한 예제이다. (http://nonn-et-twk.net/twk/closure/)

더보기
Martin Fowler's closure examples in JavaScript

 

Below are JavaScript versions of the examples in Martin Fowler's blog on closures:

 

Try managers example now.

 

 

 

Try highpaid example now.

 


 

[ Closure 관련 Link ]

 

 

   C# -  http://joe.truemesh.com/blog//000390.html

   Pythonhttp://ivan.truemesh.com/archives/000392.html

   Boohttp://boo.codehaus.org/Martin+Fowler%27s+closure+examples+in+boo

   Lisphttp://stefanroock.blogspot.com/2006/08/closures-in-common-lisp.html

   JavaScripthttp://nonn-et-twk.net/twk/closure/