开发者

Natural problems to solve using closures

开发者 https://www.devze.com 2022-12-30 16:45 出处:网络
I have read quite a few articles on closures, and, embarassingly enough, I still don\'t understand this concept! Articles explain how to create a closure with a few examples, but I don\'t see any poin

I have read quite a few articles on closures, and, embarassingly enough, I still don't understand this concept! Articles explain how to create a closure with a few examples, but I don't see any point in paying much attention to them, as they largely look contrived examples. I am not saying all of them are contrived, just that the ones I found looked contrived, and I dint see how even after understanding them, I will be able to use them. So in order to understand closures, I am looking at a few real problems, that can be solved very naturally using closures.

For instance, a natural way to explain recursion to a person could be to explain the computation of n!. It is very natural to understand a problem like computing the factorial of a number using recursion. Similarly, it is almost a no-brainer to find an element in an unsorted array by reading e开发者_JAVA技巧ach element, and comparing with the number in question. Also, at a different level, doing Object-oriented programming also makes sense.

So I am trying to find a number of problems that could be solved with or without closures, but using closures makes thinking about them and solving them easier. Also, there are two types to closures, where each call to a closure can create a copy of the environment variables, or reference the same variables. So what sort of problems can be solved more naturally in which of the closure implementations?


Callbacks are a great example. Let's take JavaScript.

Imagine you have a news site, with headlines and short blurbs and "Read More..." buttons next to each one. When the user clicks the button, you want to asynchronously load the content corresponding to the clicked button, and present the user with an indicator next to the requested headline so that the user can "see the page working at it".

function handleClickOnReadMore(element) {
  // the user clicked on one of our 17 "request more info" buttons.
  // we'll put a whirly gif next to the clicked one so the user knows
  // what he's waiting for...
  spinner = makeSpinnerNextTo(element);

  // now get the data from the server
  performAJAXRequest('http://example.com/',
      function(response) {
        handleResponse(response);

        // this is where the closure comes in
        // the identity of the spinner did not
        // go through the response, but the handler
        // still knows it, even seconds later,
        // because the closure remembers it.
        stopSpinner(spinner);
      }
  );
}


Alright, say you're generating a menu in, say, javascript.

var menuItems = [
   { id: 1, text: 'Home' },
   { id: 2, text: 'About' },
   { id: 3, text: 'Contact' }
];

And your creating them in a loop, like this:

for(var i = 0; i < menuItems.length; i++) {

    var menuItem = menuItems[i];

    var a = document.createElement('a');
    a.href = '#';
    a.innerHTML = menuItem.text;

    // assign onclick listener

    myMenu.appendChild(a);

}

Now, for the onclick listener, you might attempt something like this:

a.addEventListener('click', function() {
    alert( menuItem.id );
}, false);

But you'll find that this will have every link alert '3'. Because at the time of the click, your onclick code is executed, and the value of menuItem is evaluated to the last item, since that was the value it was last assigned, upon the last iteration of the for loop.

Instead, what you can do is to create a new closure for each link, using the value of menuItem as it is at that point in execution

a.addEventListener('click', (function(item) {
    return function() {
        alert( item.id );
    }
})( menuItem ), false);

So what's going on here? We're actually creating a function that accepts item and then immediately call that function, passing menuItem. So that 'wrapper' function is not what will be executed when you click the link. We're executing it immediately, and assigning the returned function as the click handler.

What happens here is that, for each iteration, the function is called with a new value of menuItem, and a function is returned which has access to that value, which is effectively creating a new closure.

Hope that cleared things up =)


Personally I hated to write sort routines when I had a list of objects.
Usually you had the sort function and a separate function to compare the two objects.
Now you can do it in one statement

List<Person> fList = new List<Person>();
fList.Sort((a, b) => a.Age.CompareTo(b.Age));


Well, after some time, spent with Scala, I can not imagine a code, operating on some list without closures. Example:

val multiplier = (i:Int) => i * 2
val list1 = List(1, 2, 3, 4, 5) map multiplier

val divider = (i:Int) => i / 2
val list2 = List(1, 2, 3, 4, 5) map divider

val map = list1 zip list2

println(map)

The output would be

List((2,0), (4,1), (6,1), (8,2), (10,2))

I'm not sure, if it is the example, you are looking for, but I personally think, that best examples of closures's real power can be seen on lists-examples: all kinds of sortings, searchings, iterating, etc.


I personally find massively helpful presentation by Stuart Langridge on Closures in JavaScript (slides in pdf). It's full of good examples and a bit of sense of humour as well.

Natural problems to solve using closures


In C#, a function can implement the concept of filtering by proximity to a point:

IEnumerable<Point> WithinRadius(this IEnumerable<Point> points, Point c, double r)
{
    return points.Where(p => (p - c).Length <= r);
}

The lambda (closure) captures the supplied parameters and binds them up into a computation that will performed at some later time.


Well, I use Closures on a daily basis in the form of a function composition operator and a curry operator, both are implemented in Scheme by closures:

Quicksort in Scheme for instance:

(define (qsort lst cmp)
  (if (null? lst) lst
      (let* ((pivot (car lst))
             (stacks (split-by (cdr lst) (curry cmp pivot))))
        (append (qsort (cadr stacks) cmp)
                (cons pivot
                      (qsort (car stacks) cmp))))))

Where I cmp is a binary function usually applied as (cmp one two), I curried it in this case to split my stack in two by creating a unitary predicate if you like, my curry operators:

(define (curry f . args)
  (lambda lst (apply f (append args lst))))

(define (curryl f . args)
  (lambda lst (apply f (append lst args))))

Which respectively curry righthanded and lefthanded.

So currying is a good example of closures, another example is functional composition. Or for instance a function which takes list and makes from that a predicate that tests if its argument is a member of that list or not, that's a closure too.


Closure is one of the power strengths of JavaScript, because JavaScript is a lambda language. For more on this subject go here:
Trying to simplify some Javascript with closures

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号