【问题标题】:What's this &block in Ruby? And how does it get passed in a method here?Ruby 中的 &block 是什么?它是如何在这里通过方法传递的?
【发布时间】:2010-10-23 07:39:37
【问题描述】:

在一本 Ruby on Rails 书中看到了这段代码。第一个来自视图,第二个是辅助模块。我不明白&blockattributes={} 是如何工作的。谁能指导我看一个解释这个的教程?

<% hidden_div_if(@cart.items.empty?, :id => "cart") do %>
 <%= render(:partial => "cart", :object => @cart) %>
<% end %>

module StoreHelper
 def hidden_div_if(condition, attributes = {}, &block)
  if condition
   attributes["style"] = "display: none"
  end
   content_tag("div", attributes, &block)
  end
end

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    &amp;block 是一种将一段 Ruby 代码发送到方法中,然后在该方法范围内评估该代码的方法。在上面的示例代码中,这意味着将在 div 中呈现部分命名的购物车。我认为closure 一词在计算机科学中用于此目的。

    所以在您的示例中,&amp;block 是:

    <%= render(:partial => "cart", :object => @cart) %>
    

    可以在Robert Sosinski's blog 找到一些很好的阅读和对块、proc 和 lamdas 的解释。

    【讨论】:

    【解决方案2】:

    Ruby 实现了 Block、Procs 和 lambda,它们在计算机科学界被称为闭包。 如果你开始学习 Ruby,你很快就会遇到这样的代码。

    a = ["dog", "cat", "bird"]
    a.alter_each! do |n, i|
      "#{i}_#{n}"
    end
    

    那么这里发生了什么?

    我们从一组动物名称开始,然后调用 alter_each!方法传递一个块。在这段代码中,我们可以指定我们希望如何更改每个项目。我们的示例将在每个动物名称前面加上它在数组中的位置。作为alter_each!方法遍历每个项目,它将执行我们的块并传递值和索引。我们的块捕获这些参数,将索引作为名称的前缀并返回结果。 现在让我们看看alter_each!方法。

    注意该方法没有指定任何参数,这是因为一个块被自动分配给 yield 关键字。调用 yield 就像一个函数,传入数组中每个项目的值和索引并覆盖原始值。

    class Array
      def alter_each!
        self.each_with_index do |n, i|
          self[i] = yield(n,i)
        end
      end
    end
    

    如果您需要将参数传递给此方法怎么办?

    您可以修改方法签名以接受参数,最后捕获带有以 & 符号开头的参数的块。在下面的示例中,我们的块将使用 &block 参数捕获,我们将调用 call 方法。这是代替使用yield

    class Array
      def modify_each!(add_one = true, &block)
        self.each_with_index do |n, i|
          j = (add_one) ? (i + 1) : i
          self[i] = block.call(n,j)
        end
      end
    end
    

    Full article on ruby blocks

    【讨论】:

      【解决方案3】:

      块是 ruby​​ 中相当基本的部分。它们由do |arg0,arg1| ... end{ |arg0,arg1,arg2| ... } 分隔。

      它们允许您指定要传递给方法的回调。 可以通过两种方式调用此回调 - 通过捕获 通过指定以&amp; 为前缀的最终参数,或者通过 使用yield 关键字:

      irb> def meth_captures(arg, &block)
             block.call( arg, 0 ) + block.call( arg.reverse , 1 )
           end
      #=> nil
      irb> meth_captures('pony') do |word, num|
             puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
             word + num.to_s
           end
      in callback! word = "pony" num = 0
      in callback! word = "ynop" num = 1
      #=> "pony0ynop1" 
      irb> def meth_yields(arg)
             yield(arg, 0) + yield(arg.upcase, 1)
           end
      #=> nil
      irb> meth_yields('frog') do |word, num|
             puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
             word + num.to_s
           end
      in callback! word = "frog", num = 0
      in callback! word = "FROG", num = 1
      #=> "frog0FROG1"
      

      请注意,我们的回调在每种情况下都是相同的 - 我们可以删除 通过将我们的回调保存在一个对象中,然后将其传递给每个对象来重复 方法。这可以使用lambda 来捕获对象中的回调来完成, 然后通过前缀&amp;传递给一个方法。

      irb> callback = lambda do |word, num|
             puts "in callback! word = #{word.inspect}, num = #{num.inspect}"
             word + num.to_s
           end
      #=> #<Proc:0x0052e3d8@(irb):22>
      irb> meth_captures('unicorn', &callback)
      in callback! word = "unicorn", num = 0
      in callback! word = "nrocinu", num = 1
      #=> "unicorn0nrocinu1"
      irb> meth_yields('plate', &callback)
      in callback! word = "plate", num = 0
      in callback! word = "PLATE", num = 1
      #=> "plate0PLATE1"
      

      在这里了解&amp; 作为函数最后一个参数的前缀的不同用途很重要

      • 在函数定义中,它将任何传递的块捕获到该对象中
      • 在函数调用中,它将给定的回调对象扩展为一个块

      如果你环顾四周,到处都在使用块,尤其是在迭代器中,比如Array#each

      【讨论】:

      • 谢谢你,对我真的很有帮助!
      • 谢谢,很好的例子,但是在 Fixnum 上调用检查有什么意义呢?
      • 我很后悔读了这篇文章,我的心已经碎了。我应该吃蓝色药丸……这是一个很好的例子,我理解到足以害怕它。
      【解决方案4】:

      它是这样工作的:

      @cart.items.empty? 是密码

      :id =&gt; "cart" 按照约定成为属性,如果参数哈希是最后一个,您可以删除参数哈希上的 {}。

      块是

      render(:partial =&gt; "cart", :object =&gt; @cart)

      所以如果购物车是空的,它会在函数中添加属性 值为“display: none”的样式

      然后它将创建一个 div 标签,其中填充了执行块的结果的内容,这将是使用 @cart 的内容渲染部分视图购物车的结果。

      【讨论】:

        【解决方案5】:

        Re attributes = {},这只是一个具有默认值的方法参数。因此,如果您调用hidden_div_if(whatever),即只传递第一个参数,attributes 将默认为空哈希。

        这很有用,因为它简化了稍后设置attributes["style"],因为不必先将attributes 初始化为哈希值。 (不过也可以简单地使用(attributes ||= {})["style"] = …。)


        &amp;block 只是稍微复杂一点。

        Ruby 方法可以使用块的最后一个参数,使用特殊语法method(args) { |block_args| block_code }&amp;block 基本上将该块作为Proc 对象捕获到block 变量中。所以block在这里只是一个指向匿名过程的变量。

        当稍后调用content_tag,并将&amp;block作为其最后一个参数传递时,它会扩展为一个块,就像调用确实是content_tag(…) { block originally passed to hidden_if_div }一样


        所以也许我在这里真的很困惑。您应该搜索的是“ruby 默认参数”和“ruby 块”。

        【讨论】:

          猜你喜欢
          • 2015-10-08
          • 2015-04-17
          • 2012-06-27
          • 1970-01-01
          • 1970-01-01
          • 2012-07-09
          • 1970-01-01
          • 2014-03-13
          • 1970-01-01
          相关资源
          最近更新 更多