When to use closure? [closed]

2019-01-12 21:08发布

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.

9条回答
2楼-- · 2019-01-12 21:43

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.

查看更多
\"骚年 ilove
3楼-- · 2019-01-12 21:48

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.

查看更多
老娘就宠你
4楼-- · 2019-01-12 21:52

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.

查看更多
疯言疯语
5楼-- · 2019-01-12 21:54

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.

查看更多
爱情/是我丢掉的垃圾
6楼-- · 2019-01-12 21:54

This article includes two examples of where closures are actually useful: Closure

查看更多
我欲成王,谁敢阻挡
7楼-- · 2019-01-12 21:55

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)

查看更多
登录 后发表回答