屈服于匿名块两个函数

时间:2016-07-07 22:29:39

标签: ruby-on-rails ruby yield

可能有一种简单的方法可以做到这一点。

我正在尝试重构以下内容

def foo(baz)
    baz.update_first
    if baz.has_condition?
       yield baz.val if block_given?
       baz.a
    else
       baz.b
    end
end

称为

foo(baz) {|b| b.modify}

类似

 def foo(baz)
    baz.update_first
    bar(baz)  {|i| yield i if block_given? }        
 end

 def bar(baz)
     if baz.has_condition?
       yield baz.val if block_given?
       baz.a
    else
       baz.b
    end
 end

那会有用吗?怎么样?

我认为它会,但我很欣赏一个清楚解释如何让一个块内的让步工作...通过proc.c和vm.c以及relevant git commit in the ruby source code阅读,我认为当调用bar时foo它执行直到它产生,然后你走向帧堆栈到foo中定义的块的本地环境指针,这被调用,其中yield产生走向块foo被调用,执行它,然后你是回到酒吧。那是对的吗?有更好的方法吗?

这对我来说感觉有点怪异,比如反转控制,并且需要foo更多地了解baz,但我很遗憾不能简单地在此代码中传递proc或lambda。

1 个答案:

答案 0 :(得分:3)

我认为,如果你看一个替代语法,将bloc转换为proc参数,那么yield的概念可能会更清楚。

例如,以下示例是相同的

def my_each(arr)
  arr.each { |x| yield x }
end

def my_each(arr, &blk)
  arr.each { |x| blk.call(x) }
end

# Both are called the same way
my_each([1,2,3]) { |x| print x }
# => 123

使用yield时,该变量在方法中可用,而不在参数列表中声明它。在参数前加&符号会将其转换为proc,因此在方法中可以使用.call运行。

这是一个向一个方法提供块然后在两个范围内执行它的示例:

def method_a(number, &blk)
  method_b do
    method_c do
      blk.call(number)
    end
  end
end

def method_b(&blk)
  blk.call
end

def method_c(&blk)
  blk.call
end

method_a(1) { |num| puts num + 1 }
# => 2

请注意,blk不是一个神奇的词 - 您可以根据需要为变量命名。

这与收益率相同:

def method_a(number)
  method_b do
    method_c do
      yield number
    end
  end
end

def method_b
  yield
end

def method_c
  yield
end

method_a(1) { |num| puts num + 1 }
# => 2

我认为使用&blk语法更清楚,因为它为proc分配了一个变量。仅仅因为在方法中使用了proc并不意味着你必须运行Proc.new。该块自动转换为proc。