使用闭包解决的自然问题

发布于 2024-09-02 06:29:50 字数 440 浏览 1 评论 0原文

我读了很多关于闭包的文章,但令人尴尬的是,我仍然不理解这个概念!文章通过一些示例解释了如何创建闭包,但我认为没有必要过多关注它们,因为它们很大程度上看起来是人为的示例。我并不是说所有这些都是做作的,只是我发现的那些看起来很做作,而且我不知道即使在理解它们之后,我也能够使用它们。因此,为了理解闭包,我正在研究一些实际问题,这些问题可以使用闭包非常自然地解决。

例如,向人们解释递归的一种自然方式可能是解释 n! 的计算。理解像使用递归计算数字的阶乘这样的问题是很自然的。同样,通过读取每个元素并与相关数字进行比较来找到未排序数组中的元素几乎是理所当然的。此外,在不同的层面上,进行面向对象编程也是有意义的。

所以我试图找到一些可以用或不用闭包来解决的问题,但是使用闭包可以让思考和解决它们变得更容易。此外,闭包有两种类型,其中每次调用闭包都可以创建环境变量的副本,或引用相同的变量。那么什么样的问题可以在哪种闭包实现中更自然地解决呢?

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 each 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

无远思近则忧 2024-09-09 06:29:51

闭包是 JavaScript 的强大优势之一,因为 JavaScript 是一种 lambda 语言。有关此主题的更多信息,请访问此处:
尝试使用闭包简化一些 Javascript

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

千紇 2024-09-09 06:29:50

回调就是一个很好的例子。让我们以 JavaScript 为例。

想象一下,您有一个新闻网站,其中有标题和简短的简介,每个网站旁边都有“阅读更多...”按钮。当用户单击按钮时,您希望异步加载与单击的按钮对应的内容,并在请求的标题旁边向用户显示一个指示符,以便用户可以“看到正在处理的页面”。

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);
      }
  );
}

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);
      }
  );
}
猥︴琐丶欲为 2024-09-09 06:29:50

好吧,假设您正在用 javascript 生成一个菜单。

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

并且您在循环中创建它们,如下所示:

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);

}

现在,对于 onclick 侦听器,您可能会尝试如下操作:

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

但您会发现每个链接警报都会显示“3”。因为在单击时,您的 onclick 代码将被执行,并且 menuItem 的值将被评估为最后一项,因为这是在 for 的最后一次迭代时最后分配的值环形。

相反,您可以做的是为每个链接创建一个新的闭包,使用 menuItem 的值,在执行时

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

那么这里发生了什么?我们实际上正在创建一个接受 item 的函数,然后立即调用该函数,传递 menuItem。因此,当您单击链接时,不会执行“包装器”函数。我们立即执行它,并将返回的函数指定为点击处理程序。

这里发生的情况是,对于每次迭代,都会使用新值 menuItem 调用该函数,并返回一个可以访问该值的函数,这实际上创建了一个新的闭包。

希望事情能够解决=)

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 =)

还在原地等你 2024-09-09 06:29:50

就我个人而言,当我有一个对象列表时,我讨厌编写排序例程
通常您有排序函数和一个单独的函数来比较两个对象。
现在您可以用一条语句来完成

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

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));
━╋う一瞬間旳綻放 2024-09-09 06:29:50

好吧,经过一段时间的使用 Scala,我无法想象在某些列表上操作的代码没有关闭。示例:

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)

输出将是

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

我不确定,如果它是您正在寻找的示例,但我个人认为,闭包真正威力的最佳示例可以在列表示例中看到:各种排序,搜索,迭代等

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.

遇到 2024-09-09 06:29:50

我个人认为 Stuart Langridge 关于 JavaScript 中的闭包(pdf 中的幻灯片)。它充满了很好的例子,也充满了幽默感。

alt text

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.

alt text

最终幸福 2024-09-09 06:29:50

在 C# 中,函数可以实现通过接近点进行过滤的概念:

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

lambda(闭包)捕获提供的参数并将它们绑定到稍后执行的计算中。

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.

○闲身 2024-09-09 06:29:50

好吧,我每天都以函数组合运算符和柯里运算符的形式使用闭包,两者都是通过闭包在Scheme中实现的:

例如Scheme中的快速排序:

(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))))))

其中cmp是一个二元函数,通常应用为(cmp一二) ),在这种情况下,我将其柯里化,通过创建一个单一谓词(如果您愿意)将我的堆栈分成两部分,我的柯里化运算符:

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

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

分别柯里化右手和左手。

所以柯里化是闭包的一个很好的例子,另一个例子是函数组合。或者例如,一个函数接受列表并从中创建一个谓词来测试其参数是否是该列表的成员,这也是一个闭包。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文