Closures/Value capture

From Rosetta Code
Revision as of 11:07, 20 July 2011 by rosettacode>Spoon! (new task that interests me)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Closures/Value capture is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Task: Create a list of 10 functions, in the simplest manner possible (anonymous functions are encouraged), such that the function at index i (i from 0 to 9), when run, should return the number i. Display the result of running any but the last function, to demonstrate that the function indeed remembers its value.

Goal: To demonstrate how to create a series of independent closures based on the same template but maintain separate copies of the variable closed over. In imperative languages, one would generally use a loop with a mutable counter variable. For each function to maintain the correct number, it has to capture the value of the variable at the time it was created, rather than just a reference to the variable, which would have a different value by the time the function was run.

Python

The naive way does not work: <lang python>funcs = [] for i in range(10):

   funcs.append(lambda: i)

print funcs[3]() # prints 9</lang>

The simplest solution is to add optional parameters with default arguments at the end of the parameter list, to create a local copy of the variable, and evaluate the variable at the time the function is created. (The optional parameter is not expected to ever be passed.) Often, the optional parameter will be named the same as the variable to be closed over (leading to odd-looking code of the form foo=foo in the arguments), so that the code inside the function need not be changed, but this might lead to confusion. This technique does not work for functions with a variable number of arguments. <lang python>funcs = [] for i in range(10):

   funcs.append(lambda i=i: i)

print funcs[3]() # prints 3</lang> or equivalently the list comprehension: <lang python>funcs = [lambda i=i: i for i in range(10)] print funcs[3]() # prints 3</lang>

Another solution is to wrap an immediately-executed function around our function. The wrapping function creates a new scope, and its execution forces the evaluation of the variable to be closed over. <lang python>funcs = [] for i in range(10):

   funcs.append((lambda i: lambda: i)(i))

print funcs[3]() # prints 3</lang> or equivalently the list comprehension: <lang python>funcs = [(lambda i: lambda: i)(i) for i in range(10)] print funcs[3]() # prints 3</lang>