可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have seen samples of closure from - What is a 'Closure'?
Can anyone provide simple example of when to use closure?
Specifically, scenarios in which closure makes sense?
Lets assume that the language doesn't have closure support, how would one still achieve similar thing?
Not to offend anyone, please post code samples in a language like c#, python, javascript, ruby etc.
I am sorry, I do not understand functional languages yet.
回答1:
Closures are simply great tools. When to use them? Any time you like... As has already been said, the alternative is to write a class; for example, pre C# 2.0, creating a parameterised thread was a real struggle. With C# 2.0 you don't even need the `ParameterizedThreadStart' you just do:
string name = // blah
int value = // blah
new Thread((ThreadStart)delegate { DoWork(name, value);}); // or inline if short
Compare that to creating a class with a name and value
Or likewise with searching for a list (using a lambda this time):
Person person = list.Find(x=>x.Age > minAge && x.Region == region);
Again - the alternative would be to write a class with two properties and a method:
internal sealed class PersonFinder
{
public PersonFinder(int minAge, string region)
{
this.minAge = minAge;
this.region = region;
}
private readonly int minAge;
private readonly string region;
public bool IsMatch(Person person)
{
return person.Age > minAge && person.Region == region;
}
}
...
Person person = list.Find(new PersonFinder(minAge,region).IsMatch);
This is fairly comparable to how the compiler does it under the bonnet (actually, it uses public read/write fields, not private readonly).
The biggest caveat with C# captures is to watch the scope; for example:
for(int i = 0 ; i < 10 ; i++) {
ThreadPool.QueueUserWorkItem(delegate
{
Console.WriteLine(i);
});
}
This might not print what you expect, since the variable i is used for each. You could see any combination of repeats - even 10 10's. You need to carefully scope captured variables in C#:
for(int i = 0 ; i < 10 ; i++) {
int j = i;
ThreadPool.QueueUserWorkItem(delegate
{
Console.WriteLine(j);
});
}
Here each j gets captured separately (i.e. a different compiler-generated class instance).
Jon Skeet has a good blog entry covering C# and java closures here; or for more detail, see his book C# in Depth, which has an entire chapter on them.
回答2:
I agree with a previous answer of "all the time". When you program in a functional language or any language where lambdas and closures are common, you use them without even noticing. It's like asking "what is the scenario for a function?" or "what is the scenario for a loop?" This isn't to make the original question sound dumb, rather it's to point out that there are constructs in languages that you don't define in terms of specific scenarios. You just use them all the time, for everything, it's second nature.
This is somehow reminiscent of:
The venerable master Qc Na was walking
with his student, Anton. Hoping to
prompt the master into a discussion,
Anton said "Master, I have heard that
objects are a very good thing - is
this true?" Qc Na looked pityingly at
his student and replied, "Foolish
pupil - objects are merely a poor
man's closures."
Chastised, Anton took his leave from
his master and returned to his cell,
intent on studying closures. He
carefully read the entire "Lambda: The
Ultimate..." series of papers and its
cousins, and implemented a small
Scheme interpreter with a
closure-based object system. He
learned much, and looked forward to
informing his master of his progress.
On his next walk with Qc Na, Anton
attempted to impress his master by
saying "Master, I have diligently
studied the matter, and now understand
that objects are truly a poor man's
closures." Qc Na responded by hitting
Anton with his stick, saying "When
will you learn? Closures are a poor
man's object." At that moment, Anton
became enlightened.
(http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html)
回答3:
The most simple example of using closures is in something called currying. Basically, let's assume we have a function f()
which, when called with two arguments a
and b
, adds them together. So, in Python, we have:
def f(a, b):
return a + b
But let's say, for the sake of argument, that we only want to call f()
with one argument at a time. So, instead of f(2, 3)
, we want f(2)(3)
. This can be done like so:
def f(a):
def g(b): # Function-within-a-function
return a + b # The value of a is present in the scope of g()
return g # f() returns a one-argument function g()
Now, when we call f(2)
, we get a new function, g()
; this new function carries with it variables from the scope of f()
, and so it is said to close over those variables, hence the term closure. When we call g(3)
, the variable a
(which is bound by the definition of f
) is accessed by g()
, returning 2 + 3 => 5
This is useful in several scenarios. For example, if I had a function which accepted a large number of arguments, but only a few of them were useful to me, I could write a generic function like so:
def many_arguments(a, b, c, d, e, f, g, h, i):
return # SOMETHING
def curry(function, **curry_args):
# call is a closure which closes over the environment of curry.
def call(*call_args):
# Call the function with both the curry args and the call args, returning
# the result.
return function(*call_args, **curry_args)
# Return the closure.
return call
useful_function = curry(many_arguments, a=1, b=2, c=3, d=4, e=5, f=6)
useful_function
is now a function which only needs 3 arguments, instead of 9. I avoid having to repeat myself, and also have created a generic solution; if I write another many-argument function, I can use the curry
tool again.
回答4:
Typically, if one doesn't have closures, one must define a class to carry with it the equivalent of the closure's environment, and pass it around.
For example, in a language like Lisp, one can define a function that returns a function (with a closed-over environment) to add some predefined amount to its argument thusly:
(defun make-adder (how-much)
(lambda (x)
(+ x how-much)))
and use it like this:
cl-user(2): (make-adder 5)
#<Interpreted Closure (:internal make-adder) @ #x10009ef272>
cl-user(3): (funcall * 3) ; calls the function you just made with the argument '3'.
8
In a language without closures, you would do something like this:
public class Adder {
private int howMuch;
public Adder(int h) {
howMuch = h;
}
public int doAdd(int x) {
return x + howMuch;
}
}
and then use it like this:
Adder addFive = new Adder(5);
int addedFive = addFive.doAdd(3);
// addedFive is now 8.
The closure implicitly carries its environment with it; you seamlessly refer to that environment from inside the executing part (the lambda). Without closures you must make that environment explicit.
That should explain to you when you would use closures: all the time. Most instances where a class is instantiated to carry with it some state from another part of the computation and apply it elsewhere are elegantly replaced by closures in languages which support them.
One can implement an object system with closures.
回答5:
Here is an example from Python's standard library, inspect.py. It currently reads
def strseq(object, convert, join=joinseq):
"""Recursively walk a sequence, stringifying each element."""
if type(object) in (list, tuple):
return join(map(lambda o, c=convert, j=join: strseq(o, c, j), object))
else:
return convert(object)
This has, as parameters, a convert function and a join function, and recursively walks over lists and tuples. The recursion is implemented using map(), where the first parameter is a function. The code predates the support for closures in Python, so needs two additional default arguments, to pass convert and join into the recursive call. With closures, this reads
def strseq(object, convert, join=joinseq):
"""Recursively walk a sequence, stringifying each element."""
if type(object) in (list, tuple):
return join(map(lambda o: strseq(o, convert, join), object))
else:
return convert(object)
In OO languages, you typically don't use closures too often, as you can use objects to pass state - and bound methods, when your language has them. When Python didn't have closures, people said that Python emulates closures with objects, whereas Lisp emulates objects with closures. As an example from IDLE (ClassBrowser.py):
class ClassBrowser: # shortened
def close(self, event=None):
self.top.destroy()
self.node.destroy()
def init(self, flist):
top.bind("<Escape>", self.close)
Here, self.close is a parameter-less callback invoked when Escape is pressed. However, the close implementation does need parameters - namely self, and then self.top, self.node. If Python didn't have bound methods, you could write
class ClassBrowser:
def close(self, event=None):
self.top.destroy()
self.node.destroy()
def init(self, flist):
top.bind("<Escape>", lambda:self.close())
Here, the lambda would get "self" not from a parameter, but from the context.
回答6:
In Lua and Python it's a very natural thing to do when "just coding", because the moment you reference something that's not a parameter, you're making a closure. (so most of these will be quite dull as examples.)
As for a concrete case, imagine an undo/redo system, where the steps are pairs of (undo(), redo()) closures. The more cumbersome ways of doing that might be to either: (a) Make unredoable classes have a special method with universally dorky arguments, or (b) subclass UnReDoOperation umpteen times.
Another concrete example is infinite lists: Instead of working with genericized containers, you frob a function that retrieves the next element. (this is part of the power of iterators.) In this case you can either keep just little bit of state (the next integer, for the list-of-all-nonnegative-integers or similar) or a reference to a position in an actual container. Either way, it's a function that references something that is outside itself. (in the infinite-list case, the state variables must be closure variables, because otherwise they'd be clean for every call)
回答7:
I'm told there are more uses in haskell, but I've only had the pleasure of using closures in javascript, and in javascript I don't much see the point. My first instinct was to scream "oh no, not again" at what a mess the implementation must be to make closures work.
After I read about how closures were implemented (in javascript anyway), it doesn't seem quite so bad to me now and the implementation seems somewhat elegant, to me at least.
But from that I realized "closure" isn't really the best word to describe the concept. I think it should better be named "flying scope."
回答8:
As one of the previous answers notes, you often find yourself using them without hardly noticing that you are.
A case in point is that they are very commonly used in setting up UI event handling to gain code reuse while still allowing access to the UI context. Here's an example of how defining an anonymous handler function for a click event creates a closure that includes the button
and color
parameters of the setColor()
function:
function setColor(button, color) {
button.addEventListener("click", function()
{
button.style.backgroundColor = color;
}, false);
}
window.onload = function() {
setColor(document.getElementById("StartButton"), "green");
setColor(document.getElementById("StopButton"), "red");
}
Note: for accuracy it's worth noting that the closure is not actually created until the setColor()
function exits.
回答9:
This article includes two examples of where closures are actually useful:
Closure