Ruby Ruby 中 block 的优先级

hiveer · 2015年04月08日 · 最后由 iBachue 回复于 2015年04月09日 · 2707 次阅读

Ruby 中的代码块有两种形式

{}

or

do
  ...
end

我相信你也相信他们可以随意使用,对么? 我一直都是这么认为的,直到有一天我写了如下的代码:

clear_cache users.each_with_object([]) do |user, array|
  disassociate_user user.id
  array << user.cache_key
end

def clear_cache
  Array(cache_keys).each { |key| Rails.cache.delete key }
end

这段代码始终和我的预期有很大的差距,我期望的是

users.each_with_object([]) do |user, array|
  disassociate_user user.id
  array << user.cache_key
end

这段代码能返回一个数组作为clear_cache的参数,但结果并不是这样。为什么呢? 这就涉及到了 Ruby 中代码块的优先级了,如果上面的代码我换个写法

clear_cache users.each_with_object([]) { |user, array|
  disassociate_user user.id
  array << user.cache_key }

你猜如何,这就是我要的结果。这说明了{}这个形式的代码块有更高的优先级,它和调用的方法会被优先执行。

下面再给出一些借来的例子:

1.upto 3 do |x|
  puts x
end

1.upto 3 { |x| puts x }
# SyntaxError: compile error

对于上例的第二种情况,因为 block 会被优先处理,那么这个例子就类似于method x y这样的方法调用,这必然是没有被 Ruby 允许的。

method1 method2 do
  puts "hi"
end

method1 method2 {
  puts "hi"
}

对于这个例子,第一种情况的执行逻辑是method1接受一个叫做method2的参数,且调用一个 block; 第二种情况就是method1 接受method2调用 block 返回的结果作为参数。

clear_cache(users.each_with_object([]) do |user, array|
  disassociate_user user.id
  array << user.cache_key
end)

@chaucerling 你不觉得这样很丑?

ruby 程序员都被惯坏了,不爱用括号...不知道 lisp 程序员怎么看。

#2 楼 @hiveer 如果users.each_with_object([])返回的是一个 enumerator 的对象,那可以这样

def clear_cache(enum, &block)
  cache_keys = enum.each &block
  Array(cache_keys).each { |key| Rails.cache.delete key }
end

#4 楼 @chaucerling 你的这个做法已经不再是我想要的逻辑了。 纠正下, each_with_object 返回的是一开始传给他的那个 object。

第一感觉是没有用括号的缘故,搜索了一下 Programming Ruby 果然如此:

http://phrogz.net/ProgrammingRuby/language.html#blocksclosuresandprocobjects

Braces have a high precedence; do has a low precedence. If the method invocation has parameters that are not enclosed in parentheses, the brace form of a block will bind to the last parameter, not to the overall invocation. The do form will bind to the invocation.

另外,这里用 map 应该就可以达成需求,不需要 each_with_object

clear_cache users.map{ |user| disassociate_user user.id; user.cache_key}

这只是优先级差距而已嘛 他们确实是可以任意互换的 只要写对语法

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