`
RednaxelaFX
  • 浏览: 3052967 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

C#与Ruby 1.8在作用域与闭包上的比较

阅读更多
在读Programming Ruby,突然想起以前在别人的blog上回过的一帖,顺带再回忆一下在自己blog上也记下来。

先看两个例子:
testClosure.cs:
using System;
using System.Collections.Generic;

sealed class TestClosure {    
    static void Main(string[] args) {
        var actions = new List<Action>();
        for (int i = 0; i < 3; ++i) {
            int j = i;
            actions.Add(() => Console.WriteLine(j));
        }
        foreach (var a in actions) {
            a();
        }
        
        // Output:
        // 0
        // 1
        // 2
        
        ////////////////////////////////////////////////////
        
        actions = new List<Action>();
        for (int i = 0; i < 3; ++i) {
            actions.Add(() => Console.WriteLine(i));
        }
        foreach (var a in actions) {
            a();
        }
        
        // Output:
        // 3
        // 3
        // 3
    }
}


testClosure.rb:
procs = []
(1..3).each do |i|
  procs << lambda { puts i }
end
procs.each { |p| p.call }

# Output:
# 1
# 2
# 3

###############################################

procs = []
for j in (1..3) do
  procs << lambda { puts j }
end
procs.each { |p| p.call }

# Output:
# 3
# 3
# 3


在上面的两段代码里,可以看到C#与Ruby有很相似的行为——这是因为C#中的delegate和Ruby中的block都是闭包(closure),所以它们可以“记忆”不在当前作用域内的变量(或者说非局部变量)。但闭包所记忆的并不一定是,而可能是引用;在C#与Ruby中的闭包都是记住引用的,而像纯函数式语言则是记住值(其实没区别,纯函数式语言里的“变量”的值改变不了)。
既然C#和Ruby的闭包都是记引用,那为什么上面每组测试的前一个与后一个看起来行为不一样?

其实每组测试的前后两部分正好说明它们的本质是一样的。闭包的概念与静态作用域是紧密相关的。在C#的测试中,循环的控制变量i作用域覆盖整个循环过程;或者说整个循环过程使用到的i都是同一个i;前后两部分测试的结果不一样,是因为前一个测试里闭包所捕获的是只在一次循环里有效的j,每一轮循环中的j都不是“同一个”j;后一个测试的闭包捕获的是整个循环过程都有效的i,每一轮循环中的i都是同一个i,所以后续循环轮次中对i的改变都会影响到闭包所捕获的值。

Ruby方面同理。在Ruby 1.8.x中,与each迭代器关联的block里的变量是局部变量,每次each中使用yield来调用block时,block都会创建新的局部变量。而for与each的区别就是在循环变量的作用域上:for被展开后,先定义了一个局部变量,然后再调用each迭代器。
印象中上个月还是什么时候在经过别人的blog时见到过对Ruby的for的讨论,正好今天读到Programming Ruby时也看到了这介绍,记下。

不过在Ruby 1.9里,block的作用域有所改变。像这样:
x = 15
(1..5).each do |x|
  puts x
end
puts x # -> 15

(这消息来自Matz on Ruby 1.9
分享到:
评论
5 楼 lwwin 2008-03-31  
米事的嗯,偶总有时间来玩的-v-+

考试加油>o<!!!
4 楼 RednaxelaFX 2008-03-31  
下次……下次画几幅图来解释应该会比较清楚。这两天考试忙……
3 楼 lwwin 2008-03-31  
不要回避啊,

偶当然知道没这东西了,但是还是要问啊,反正不知道就是不知道XD

于是FX大又要麻烦你了=v=|
2 楼 RednaxelaFX 2008-03-30  
lwwin 写道
每次你说closure的时候总是用到函数里面定义函数这种非常的定义

我对“非常”这点无法认同……函数里无法定义函数是C/C++/Java的痛,或者说是出于设计考虑而不允许这点。
怎么说呢,如果只是用C/C++的话,不知道闭包是啥也没关系,完全不影响生活,反正想用也用不了。
1 楼 lwwin 2008-03-29  
closure这个概念真是不够形象……
偶总是不能够理解其中的概念

看了一些说明,跟没看一样,每次你说closure的时候总是用到函数里面定义函数这种非常的定义,弄了半天不知道怎么回事……

相关推荐

    ruby基础教程(第四版)第21章 Proc类1

    `lambda`创建的Proc对象在行为上与普通Proc对象有所不同,主要体现在两个方面: 1. 参数检查:`lambda`对参数的检查更为严格,如果传入的参数数量与定义不符,它会抛出错误。相比之下,普通的Proc对象会尝试适应...

    JavaScript语言精粹

    然而,【部分内容】实际上是包含了一系列与多个IT主题相关的资源和学习材料的链接,包括Java、.Net技术、C#、C++、Python、Ruby、数据库管理系统、平面设计、Linux、PHP、UML、Linux系统管理、Linux shell编程、UNIX...

    提升JavaScript生产力的“异样”编程手段

    2. **作用域与闭包**: - JavaScript拥有独特的作用域机制——基于函数的作用域。闭包是函数和与其相关的引用环境的组合,允许函数访问并操作其定义时所在环境中的变量。这种能力在编写异步代码或处理事件驱动逻辑...

    The boo programming language

    9. **闭包与匿名函数**:Boo支持闭包,即能够访问其自身作用域的函数,这对于实现回调和高阶函数非常有用。匿名函数(lambda表达式)则允许快速定义一次性使用的函数。 10. **单元测试**:Boo内建了对单元测试的...

    SD大会精品讲座:JavaScript引擎技术

    例如,闭包和作用域的概念就是通过特定的数据结构和算法来实现的。 ##### 5.2 引擎如何处理原型继承 JavaScript引擎通过内部数据结构来维护对象之间的继承关系。当一个对象试图访问一个不存在的属性或方法时,引擎...

    CodeWars

    在CodeWars中,你可以通过解决Kata来学习JavaScript的核心特性,如变量、数据类型(包括基本类型和引用类型)、作用域、闭包、原型链、异步编程(回调函数、Promise、async/await)等。 此外,CodeWars还强调代码的...

Global site tag (gtag.js) - Google Analytics