在Ruby on Rails上解释Iterator语法
我开始学习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的截屏video中看到它并且这些词语有意义,但我实际上不知道发生了什么。 谁有人向我解释这个?
编辑:示例
respond_to do |format| format.json format.xml { render :xml => @posts } end
方法可以采用称为“块”的构造。 这些是传递给方法的匿名方法。
另一种语法是:
method_call { |var| do_something(var) }
基本上,您是说对于迭代中的每个项目,将其命名为“var”并对该项执行某些操作。 该方法只是调用您传入的块,因为它会“生成”项目。
这有帮助吗?
编辑:在你的例子中,他们以一种有趣的方式使用迭代器模式…可能只将一个format
对象传递到你的块中,这样你就可以告诉它要处理哪些格式,以及当你看到它时要做什么。
换句话说,他们正在使用该模式创建各种DSL,以便您配置响应的内容。
在迭代器的情况下,将它们想象成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使用的函数编程,这种迭代成为可能:如果您正在创建需要迭代某些东西的类,您还可以实现每个方法。 考虑:
class AddressBook attr_accessor :addresses def each(&block) @addresses.each {|i| yield i } end end
各种类通过这种块模式实现了有趣的function:例如,查看String的each_line和each_byte方法。
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
是类似的。 它的工作原理如下:(查看这里的实际实现)
def respond_to responder = Responder.new(self) block.call(responder) responder.respond end
它创建一个响应器对象,其中包含html
方法,它们会将块传递给您。 事实certificate这非常方便,因为它可以让你做以下事情:
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
看看我如何改变依赖于块内保存的行为? 没有它,这样做会更烦人。
do || end
这将创建一个临时匿名函数,该函数将项接受到临时变量中,然后让事物对该项进行操作。 匿名函数被传递给指定的原始
,以对该函数产生的项进行操作。
你看到的是一段代码,当你第一次看到它时,语法有点尴尬。
所以,基本上,对于迭代器,你有一个可以重复的“东西”,它会收到一个块来知道该怎么做。
例如, Range类有一个名为“each”的方法,它接收要在该范围内的每个元素上执行的代码块。
假设您要打印它:
range = 1..10 #range literal range.each {|i| puts i }
代码: {|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
方法中执行的操作。
你正在阅读的文件很古老 – 实际上是史前的。 如果网页可能会聚集灰尘,那么会有一层厚厚的网页。
在ruby-lang网站上试用参考资料。 此外, Programming Ruby (pickaxe)书籍是必不可少的参考书。
我认为你可以称之为迭代器,因为通常会多次调用块函数。 如:
5.times do |i| puts "#{i} " end
“在幕后”,执行以下步骤:
- 调用对象实例
5
的方法times
,传递代码puts "#{i} "
放入Proc
对象实例中。 - 在
times
方法中,此代码在循环内部调用,将当前索引作为参数传递。 这就是times
样子( 实际上是在C中 ):
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 }