What is the exact definition of a closure?

后端 未结 8 1083
执念已碎
执念已碎 2020-11-28 09:43

I\'ve read through previous topics on closures on stackflow and other sources and one thing is still confusing me. From what I\'ve been able to piece together technically a

相关标签:
8条回答
  • 2020-11-28 09:53

    Great question! Given that one of the OOP principles of OOP is that objects has behavior as well as data, closures are a special type of object because their most important purpose is their behavior. That said, what do I mean when I talk about their "behavior?"

    (A lot of this is drawn from "Groovy in Action" by Dierk Konig, which is an awesome book)

    On the simplest level a close is really just some code that's wrapped up to become an androgynous object/method. It's a method because it can take params and return a value, but it's also an object in that you can pass around a reference to it.

    In the words of Dierk, imagine an envelope that has a piece of paper inside. A typical object would have variables and their values written on this paper, but a closure would have a list of instructions instead. Let's say the letter says to "Give this envelope and letter to your friends."

    In Groovy: Closure envelope = { person -> new Letter(person).send() }
    addressBookOfFriends.each (envelope)
    

    The closure object here is the value of the envelope variable and it's use is that it's a param to the each method.

    Some details: Scope: The scope of a closure is the data and members that can be accessed within it. Returning from a closure: Closures often use a callback mechanism to execute and return from itself. Arguments: If the closure needs to take only 1 param, Groovy and other langs provide a default name: "it", to make coding quicker. So for example in our previous example:

    addressBookOfFriends.each (envelope) 
    is the same as:
    addressBookOfFriends.each { new Letter(it).send() }
    

    Hope this is what you're looking for!

    0 讨论(0)
  • 2020-11-28 09:55

    From what I understand a closure also has to have access to the variables in the calling context. Closures are usually associated with functional programming. Languages can have elements from different types of programming perspectives, functional, procedural, imperative, declarative, etc. They get their name from being closed over a specified context. They may also have lexical binding in that they can reference the specified context with the same names that are used in that context. Your example has no reference to any other context but a global static one.

    From Wikipedia

    A closure closes over the free variables (variables which are not local variables)

    0 讨论(0)
  • 2020-11-28 10:04

    For the exact definition, I suggest looking at its Wikipedia entry. It's especially good. I just want to clarify it with an example.

    Assume this C# code snippet (that's supposed to perform an AND search in a list):

    List<string> list = new List<string> { "hello world", "goodbye world" };
    IEnumerable<string> filteredList = list;
    var keywords = new [] { "hello", "world" };
    foreach (var keyword in keywords)
        filteredList = filteredList.Where(item => item.Contains(keyword));
    
    foreach (var s in filteredList)  // closure is called here
        Console.WriteLine(s);
    

    It's a common pitfall in C# to do something like that. If you look at the lambda expression inside Where, you'll see that it defines a function that it's behavior depends on the value of a variable at its definition site. It's like passing a variable itself to the function, rather than the value of that variable. Effectively, when this closure is called, it retrieves the value of keyword variable at that time. The result of this sample is very interesting. It prints out both "hello world" and "goodbye world", which is not what we wanted. What happened? As I said above, the function we declared with the lambda expression is a closure over keyword variable so this is what happens:

    filteredList = filteredList.Where(item => item.Contains(keyword))
                               .Where(item => item.Contains(keyword)); 
    

    and at the time of closure execution, keyword has the value "world," so we're basically filtering the list a couple times with the same keyword. The solution is:

    foreach (var keyword in keywords) {
        var temporaryVariable = keyword;
        filteredList = filteredList.Where(item => item.Contains(temporaryVariable));
    }
    

    Since temporaryVariable is scoped to the body of the foreach loop, in every iteration, it is a different variable. In effect, each closure will bind to a distinct variable (those are different instances of temporaryVariable at each iteration). This time, it'll give the correct results ("hello world"):

    filteredList = filteredList.Where(item => item.Contains(temporaryVariable_1))
                               .Where(item => item.Contains(temporaryVariable_2));
    

    in which temporaryVariable_1 has the value of "hello" and temporaryVariable_2 has the value "world" at the time of closure execution.

    Note that the closures have caused an extension to the lifetime of variables (their life were supposed to end after each iteration of the loop). This is also an important side effect of closures.

    0 讨论(0)
  • 2020-11-28 10:07

    An object is state plus function. A closure, is function plus state.

    function f is a closure when it closes over (captured) x

    0 讨论(0)
  • 2020-11-28 10:12

    A closure is an implementation technique for representing procedures/functions with local state. One way to implement closures is described in SICP. I will present the gist of it, anyway.

    All expressions, including functions are evaluated in an environement, An environment is a sequence of frames. A frame maps variable names to values. Each frame also has a pointer to it's enclosing environment. A function is evaluated in a new environment with a frame containing bindings for it's arguments. Now let us look at the following interesting scenario. Imagine that we have a function called accumulator, which when evaluated, will return another function:

    // This is some C like language that has first class functions and closures.
    function accumulator(counter) {
        return (function() { return ++counter; });
    }
    

    What will happen when we evaluate the following line?

    accum1 = accumulator(0);
    

    First a new environment is created and an integer object (for counter) is bound to 0 in it's first frame. The returned value, which is a new function, is bound in the global environment. Usually the new environment will be garbage collected once the function evaluation is over. Here that will not happen. accum1 is holding a reference to it, as it needs access to the variable counter. When accum1 is called, it will increment the value of counter in the referenced environment. Now we can call accum1 a function with local state or a closure.

    I have described a few practical uses of closures at my blog http://vijaymathew.wordpress.com. (See the posts "Dangerous designs" and "On Message-Passing").

    0 讨论(0)
  • 2020-11-28 10:14

    No, that's not a closure. Your example is simply a function that returns the result of incrementing a static variable.

    Here's how a closure would work:

    function makeCounter( int x )
    {
      return int counter() {
        return x++;
      }
    }
    
    c = makeCounter( 3 );
    printf( "%d" c() ); => 4
    printf( "%d" c() ); => 5
    d = makeCounter( 0 );
    printf( "%d" d() ); => 1
    printf( "%d" c() ); => 6
    

    In other words, different invocations of makeCounter() produce different functions with their own binding of variables in their lexical environment that they have "closed over".

    Edit: I think examples like this make closures easier to understand than definitions, but if you want a definition I'd say, "A closure is a combination of a function and an environment. The environment contains the variables that are defined in the function as well as those that are visible to the function when it was created. These variables must remain available to the function as long as the function exists."

    0 讨论(0)
提交回复
热议问题