What are the correct semantics of a closure over a loop variable? [closed]

こ雲淡風輕ζ 提交于 2019-12-02 04:28:05

The Lua manual explains exactly why this works. It describes the index for-loop in terms of a while loop as this:

 for v = e1, e2, e3 do block end

--Is equivalent to:

 do
   local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
   if not (var and limit and step) then error() end
   while (step > 0 and var <= limit) or (step <= 0 and var >= limit) do
     local v = var
     block
     var = var + step
   end
 end

Notice how the loop variable v is declared inside the scope of the while loop. This is done specifically to allow exactly what you're doing.

There is no "correct" way. There are different ways. In C#, you would fix it by making a variable scoped to the loop:

for (int i = 0; i < 10; i++)
{
    int j = i;

    f[i] = (new Action(delegate()
    {
        Console.Write(j + " ");
    }));
}

In JavaScript, you might add a scope by making and calling an anonymous function:

for (var i = 0; i < 10; i++) {
    (function(i) {
        f[i] = function() {
            document.write(i + ' ');
        };
    })(i);
}

Iteration variables in C# don't have loop scope. JavaScript doesn't have block scope, just function scope. They're just different languages and they do things differently.

"what is the behavior of closing over a looped variable in lambda calculus?"

There are no loop variables in lambda calculus.

Mud

Closing over a loop variable is like closing over any other variable. The problem is with language-specific looping constructs and whether they translate into code that puts the loop variable inside or outside the loop.

For instance, if you use a while loop in C#, Lua or JavaScript, the result in all three languages is the same (10). Ditto for a for(;;) loop in JavaScript or C# (not available in Lua).

However, if you use a for (i in x) loop in JavaScript, you'll find that each closure gets a new copy of i (output: 0 1 2 3 ...). Ditto for for i=x,y in Lua and foreach in C#. Again, that has to do with how those languages construct those loops and how they expose the value of the loop variable to the body of the loop, not a difference in closure semantics.

In fact, in the case of C#'s foreach, this behavior changed from 4.5 to 5. This construct:

 foreach (var x in l) { <loop body> }

Used to translate into (pseudocode):

 E e = l.GetEnumerator()
 V v
 while (e.MoveNext()) {
      v = e.Current
      <loop body>
 }

In C# 5, this was changed to:

 E e = l.GetEnumerator()
 while (e.MoveNext()) {
      V v = e.Current
      <loop body>
 }

This was a breaking change, done to better meet programmer expectations when closing over the loop variable. The closure semantics didn't change; the position of the loop variable did.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!