【问题标题】:Explain Iterator Syntax on Ruby on Rails解释 Ruby on Rails 中的迭代器语法
【发布时间】:2010-07-20 18:30:12
【问题描述】:

我开始学习 Ruby on Rails 并发现自己对语法感到困惑,所以我不得不阅读一些 Ruby 语法。我从http://www.cs.auckland.ac.nz/references/ruby/doc_bundle/Manual/man-1.4/syntax.html学习了语法:

method_call do [`|' expr...`|'] expr...end

他们称之为迭代器。我了解迭代器通过循环运行,但我不明白我应该如何阅读此内容或此语法中发生了什么。我一直在 RoR 截屏视频中看到它,而且这些话是有道理的,但我实际上不知道发生了什么。谁能给我解释一下?

编辑:示例

respond_to do |format|
    format.json
    format.xml { render :xml => @posts }
end

【问题讨论】:

    标签: ruby-on-rails ruby syntax iterator


    【解决方案1】:

    方法可以采用称为“块”的构造。这些是传递给方法的匿名方法。

    另一种语法是:

    method_call { |var| do_something(var) }
    

    基本上,您是说对于迭代中的每个项目,将其命名为“var”并对该项目执行一些操作。该方法只是调用您传入的块,因为它向它“生成”项目。

    这有帮助吗?

    编辑:在您的示例中,您他们正在以一种有趣的方式使用迭代器模式...可能只将一个 format 对象传递到您的块中,因此您可以告诉它哪些格式处理,以及当你看到它时该怎么做。

    换句话说,他们正在使用该模式来创建某种类型的 DSL,让您可以配置响应的内容。

    【讨论】:

    • 谢谢,我从没想到这是一个匿名函数。我现在理解语法了,但是你能解释一下 DSL 吗?
    • DSL(域特定语言)可以让您以自己的语言声明有关您的域(在本例中为网站)的信息。在这种情况下,它可以让您以极其声明的方式声明您响应的格式。
    • Ruby DSL 经常使用这个:Rails 示例显然就是其中之一 - 也可以看看 Rake,Capistrano 得到了 Ruby DSL 的清晰示例。
    【解决方案2】:

    在迭代器的情况下,把它们想象成 Java 中的一个接口:你可以在 Ruby 中做一个 for 循环,但是你可能想要迭代的所有对象(应该)实现 'each' 方法一个块(即一个闭包,一个匿名函数)。

    在 Ruby 中到处都在使用块。想象一下你有这个数组:

    [1, 2, 3, 4, 5, 6].each do |i| puts i.to_s end
    

    在这里,您正在创建数组,然后在其上调用 'each' 方法。您将块传递给它。你可以把它分开,像这样:

    arr = [1, 2, 3, 4, 5, 6]
    string_printer = lambda do |i| puts i.to_s end
    arr.each(&string_printer)
    

    这种接口是在其他方面实现的:Hash 集合让您可以遍历键值对:

    {:name => "Tom", :gender => :male}.each do |key, value| puts key end
    

    do..end 可以用大括号代替,如下所示:

    [1, 2, 3, 4, 5, 6].each {|i| puts i.to_s }
    

    这种迭代之所以成为可能,是因为 Ruby 采用了函数式编程:如果您正在创建一个需要迭代某物的类,您也可以实现 each 方法。考虑:

    class AddressBook
      attr_accessor :addresses
      def each(&block)
        @addresses.each {|i| yield i }
      end
    end
    

    各种类都通过这种块模式实现有趣的功能:例如,看看 String 的 each_line 和 each_byte 方法。

    【讨论】:

      【解决方案3】:
      method_call do [`|' expr...`|'] expr...end
      

      不限于迭代函数。

      在 ruby​​ 中,任何方法都可以将块作为参数。然后可以通过该方法调用该块。在迭代器的情况下,该方法看起来像这样:

      def iter
        for i in [:x,:y,:z]
          yield i
        end
      end
      

      如果你用一个块调用iter,它将遍历[:x, :y, :z]并将它们中的每一个都交给块,然后它可以做任何你想做的事情。例如打印出来:

      iter { |z| puts z }
      

      您还可以使用它来隐藏初始化和清理步骤,例如打开和关闭文件。例如File.open。 File.open,如果它是用纯 ruby​​ 实现的(它是用 C 来提高性能)会做这样的事情。

      def File.open filename, opts
        f = File.new filename, opts
        yield f
        f.close
      end
      

      这就是为什么你可以使用

      File.open 'foobar', 'w' do |f|
        f.write 'awesome'
      end
      

      respond_to 类似。它的工作原理是这样的:(查看真正的实现here

         def respond_to
           responder = Responder.new(self)
           block.call(responder)
           responder.respond
         end
      

      它创建一个响应者对象,该对象具有html 之类的方法,这些方法接受一个块并将其传递给您。事实证明这非常方便,因为它可以让您执行以下操作:

      def action
        @foo = Foo.new params[:foo]
        respond_to do |format|
          if @foo.save
            format.html { redirect_to foo_path @foo }
            format.xml { render :xml => @foo.to_xml }
          else
            flash[:error] = "Foo could not be saved!"
            format.html { render :new }
            format.xml { render :xml => {:errors => @foo.errors }.to_xml}
          end
        end
      end
      

      看看我如何改变依赖于块内保存的行为?如果没有它,这样做会更烦人。

      【讨论】:

        【解决方案4】:
        <function> do |<temp variable>|
            <code to operate on temp variable>
        end
        

        这将创建一个临时匿名函数,该函数将项目接受到临时变量中,然后让事情对该项目进行操作。匿名函数被传入指定的原始&lt;function&gt; 以对该函数产生的项目进行操作。

        【讨论】:

          【解决方案5】:

          你看到的是一段代码,第一次看到语法有点别扭。

          所以,基本上,有了迭代器,你就有了一个可以重复的“东西”,它会收到一个块来知道该做什么。

          例如,Range 类有一个名为“each”的方法,它接收要在范围内的每个元素上执行的代码块。

          假设您要打印它:

          range = 1..10 #range literal 
          range.each {|i|
              puts i
          }
          

          代码:{|i| puts i} 是一个块,它说明当这个范围迭代其每个元素时要做什么。替代语法是您发布的语法:

           range.each do |i|
                puts i 
           end
          

          这些块与迭代器一起使用,但它们不限于“迭代”代码,您可以在其他场景中使用它们,例如:

          class Person 
            def initialize( with_name ) 
              @name = with_name
            end
            # executes a block 
            def greet 
                yield @name #passes private attribute name to the block 
            end 
          end 
          
          p = Person.new "Oscar" 
          p.greet { |n|
               puts "Name length = #{n.length}"
               puts "Hello, #{n}"
          }
          

          打印:

          Name length = 5
          Hello, Oscar
          

          因此,与其使用具有固定行为的greet 方法,不如使用块让开发人员指定要做什么,这对迭代器非常有用,但正如您所见证的那样,这不是唯一的地方。在您的情况下,该块允许您在 respond_to 方法中指定要执行的操作。

          【讨论】:

            【解决方案6】:

            您正在阅读的文档是古老的——实际上是史前的。如果网页有可能会聚集灰尘,那它就会有一层厚厚的灰尘。

            the ruby-lang website 试用参考资料。此外,Programming Ruby (pickaxe) 这本书也是必不可少的参考资料。

            【讨论】:

            • 我只是在寻找语法指南,而不是“从头开始学习编程”指南。
            • 我明白了。如果您还没有,请查看名为“The Ruby Language”的镐书章节,该章节对语法进行了非常易读和全面的描述。
            【解决方案7】:

            我认为您可以将其称为迭代器,因为通常会多次调用块函数。如:

            5.times do |i|
              puts "#{i} "
            end
            

            “幕后”,做了以下步骤:

            • 对象实例5的方法times被调用,在Proc对象实例中传递代码puts "#{i} "
            • times 方法中,此代码在循环中调用,将当前索引作为参数传递。这就是times 可能的样子 (it's in C, actually):

            class Fixnum
              def times_2(&block) # Specifying &block as a parameter is optional
                return self unless block_given?
                i = 0
                while(i < self) do
                  yield i # Here the proc instance "block" is called
                  i += 1
                end
                return self
              end
            end

            注意范围(即局部变量等)被复制到块函数中:

            x = ' '
            5.times do { |i| puts "#{i}" + x }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-05-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多