Ruby 求 JS 代码对应的 Ruby 代码...

blacktulip · 2013年06月23日 · 最后由 nevill 回复于 2013年06月23日 · 3195 次阅读

正在看 JS 的书,于是...

function createFunctions() {
    var result = new Array();

    for (var i = 0; i < 10; i++) {
        result[i] = function() {
            return i;
        };
    }

    return result;

}

alert(createFunctions()[0]());

大家不妨先目测一下结果,然后运行试试猜对没有...

然后我想看看 Ruby 对应应该怎么写,但是我只能写出这样的来...

def create_functions
  result = []
  (0..9).each do |i|
    result[i] = lambda { return i}.call
  end
  result
end

puts create_functions[0]

结果不一样... 如果想要得出跟上面 JS 代码一样的结果来, Ruby 代码应该是怎么样的呢?还是根本没有对应的写法?

Thanks

def create_functions
  result = []
  for i in 0..9
    result[i] = lambda { i }
  end
  result
end

puts create_functions[0].call

EDIT: 忽略了 i++ 的问题. 见 #2 楼

1 是 lambda call 早了 2 是 js 里的 i 是多个 functions 之间共享的变量, 而你的代码里 i 是 block local 的 3 是 for 循环最后 i 会变成 10, 而用 each i 最多变成 9

def create_functions
  result = []
  i = 0
  while i < 10
    result[i] = lambda { i }
    i += 1
  end
  result
end

create_functions[0].call

#1 楼 @5long 这个结果还是 0 .. thx thx

#2 楼 @luikore thx thx 哎呀 call 早了这么明显都没发现 -_-||

话说回来, 现在写 JS 的也未必要用 for 循环. 有 ES5 环境的直接用 Array#each, 被迫兼容旧 IE 的话也可以用 jQuery.each, underscore 的 _.each 等. 就不用担心这个问题了.

#5 楼 @5long 嗯... 还没学到那里 -_-|| 完全是小白从头开始

写 js 总要想 each 的第一个参数究竟是 index 还是元素... 直接 coffee

#7 楼 @luikore 不懂 js 不能直接学 coffee 吧

#8 楼 @blacktulip 好像是不能直接学, 但可以直接用... 还是先看完 js 书吧

这段应该是书里用来证明 for 循环里起闭包很容易出错的: 我们写代码时经常会在循环里建造一系列的闭包 function, 以为每个都对应了不同的 i, 结果全部都是同一个 i 还是超出范围的那个...

所以 js 里其实推荐用 array.forEach 的, 或者再包个 function 让 result 的每个元素都有自己的一个 i (文绉绉一点说, 就是 "不用共享状态来传递消息, 而用传递消息来共享状态") :

for (var i = 0; i < 10; i++) {
    result[i] = (function(i) {
        return (function() {
            return i;
        })
    })(i);
} // 爆炸了没? 还是乖乖用 forEach 吧

btw.

js 的 array.forEach 不能 breakcontinue, 大大限制了其用途, 所以依然是个蛋疼的语言...

其实为了 forEach 支持 break 和 continue, 很多语言都做了超多的工作.

例如 C++ 和 java 教程里都会唱 iterator 很好使: 用 iterator, 就能把抽象的元素 traverse 放到普通的 for 里面去, break 和 continue 都能自由使用了还容易做优化 -- 唯一问题是一碰到并发/并行访问一个容器, iterator 就从救世主变成了万恶之源...

而在纯函数式编程语言里, 一般是只有 map/fold/filter 而没有 for 的. 一般需要 break 的地方, 程序员可以自行脑内转换成 find 调用, 而本来要 continue 的地方, 程序员都自行包个条件判断或者模式匹配... ES5 的话就是 array.every() 了 (这个函数相当于 ruby 里的 Array#all?, 却是 js 里唯一具备按返回值中断循环之神奇功效的 functional primitive ...), 但我基本没见过有人用它来替代 break ...

#10 楼 @luikore

书里不敢再用 i 了,换了变量名

function createFunctions(){ var result = new Array();
    for (var i=0; i < 10; i++){
        result[i] = function(num){
            return function(){
                return num;
            };
        }(i);
    }
    return result; 
}

JS 确实看得我很头大,刚看了继承有 Prototype Chaining, Constructor Stealing, Prototypal Inheritance, Parasitic Inheritance, Parasitic Combination Inheritance 已经差不多要自杀了... 还看到用多一层 function 来模拟其他语言中的 block scope ...

@luikore 我最近的实际项目中确实看到同事使用 underscore.js 的_.every(),理解起来非常费劲。

#12 楼 @xds2000 如果没有利用返回值, every / some 就是纯当作条件中断循环用... 如果利用了返回值, 就相当于 ruby 里的 all?any?, 就是 js 函数名不能带问号没 ruby 看起来好理解...

#11 楼 @blacktulip 额有些写法是为了解决 IE5 不能释放循环引用而弄的吧, 大部分都是没用的花拳绣腿...

我觉得理解 coffeescript 的 class / 继承 做法 和 jquery 的 extend 就够了...

目测这段 js 就是出了名的循环变量闭包大坑。

#8 楼 @blacktulip 看文档用 JS 语法, 写代码用 coffee, 不影响. 表示我是了解 JS 语法后先学成了 coffee 的, 路走得通

create_functions = -> [0...10].map (x) -> -> x
console.log create_functions()[0]()
console.log create_functions()[1]()

create_functions = ->
  result = []
  (do (i) -> result.push -> i) for i in [0...10]
  result
console.log create_functions()[0]()
console.log create_functions()[1]()

我的学习路线是大学时 js 语法明白 工作以后用的 jquery js 一直是个半吊子水平 后来直接用 coffee 慢慢就力不从心了 然后重新看的 javascript 搞基程序设计 目前为止基本够用...

#17 楼 @zj0713001 当然, 现有的教程都是用 JS 写的, 写的是 coffee, 学的还是 JS

#11 楼 @blacktulip 这个对比的例子很好,还可以用来对比说明一下两种语言的作用域问题。 JS 会在 function 定义中打开一个新的作用域范围(即 变量的可见范围),这点跟 Ruby 的 def 一样。 不同的地方在于 Ruby 并不能在 def 定义的作用域里面看到外围作用域中的变量, 而 JS 则在定义新 function 的时候,外部变量仍能从里面看到。 下面两段代码展示了这个不同。

function a() {
  var v = 10;
  function b() {
    return v;
  }
  return b();
}
console.log(a()); // 10
# 上面 JS 代码的一对一翻译
def a
  v = 10
  def b
    v
  end
  b
end
puts a # NameError

因此,楼主的 JS 代码并不能一一对照翻译成 Ruby,而需要使用 lambda。 由此,我们也可以看到,Ruby 里面的 lambda 跟 def 不同,它没有打开一个新的作用域,而是继续使用当前作用域。

另,看到了这么多英文术语,长姿势了。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号