我正在尝试理解块和yield以及它们在Ruby中的工作方式。
如何使用产量?我研究过的许多Rails应用程序都以一种奇怪的方式使用yield。
谁能给我解释一下或者告诉我怎么理解他们?
我正在尝试理解块和yield以及它们在Ruby中的工作方式。
如何使用产量?我研究过的许多Rails应用程序都以一种奇怪的方式使用yield。
谁能给我解释一下或者告诉我怎么理解他们?
当前回答
在Ruby中,方法可以检查调用方法时是否在正常参数之外提供了块。通常这是使用block_given?方法,但你也可以通过在最终参数名前加上&来将该块引用为显式Proc。
如果一个方法是用块调用的,那么如果需要的话,该方法可以通过一些参数将控制权交给块(调用块)。考虑这个示例方法,它演示了:
def foo(x)
puts "OK: called as foo(#{x.inspect})"
yield("A gift from foo!") if block_given?
end
foo(10)
# OK: called as foo(10)
foo(123) {|y| puts "BLOCK: #{y} How nice =)"}
# OK: called as foo(123)
# BLOCK: A gift from foo! How nice =)
或者,使用特殊的块参数语法:
def bar(x, &block)
puts "OK: called as bar(#{x.inspect})"
block.call("A gift from bar!") if block
end
bar(10)
# OK: called as bar(10)
bar(123) {|y| puts "BLOCK: #{y} How nice =)"}
# OK: called as bar(123)
# BLOCK: A gift from bar! How nice =)
其他回答
在Ruby中,方法可以检查调用方法时是否在正常参数之外提供了块。通常这是使用block_given?方法,但你也可以通过在最终参数名前加上&来将该块引用为显式Proc。
如果一个方法是用块调用的,那么如果需要的话,该方法可以通过一些参数将控制权交给块(调用块)。考虑这个示例方法,它演示了:
def foo(x)
puts "OK: called as foo(#{x.inspect})"
yield("A gift from foo!") if block_given?
end
foo(10)
# OK: called as foo(10)
foo(123) {|y| puts "BLOCK: #{y} How nice =)"}
# OK: called as foo(123)
# BLOCK: A gift from foo! How nice =)
或者,使用特殊的块参数语法:
def bar(x, &block)
puts "OK: called as bar(#{x.inspect})"
block.call("A gift from bar!") if block
end
bar(10)
# OK: called as bar(10)
bar(123) {|y| puts "BLOCK: #{y} How nice =)"}
# OK: called as bar(123)
# BLOCK: A gift from bar! How nice =)
很可能有人会在这里提供一个真正详细的答案,但我一直认为Robert Sosinski的这篇文章对block, procs和lambdas之间的微妙之处进行了很好的解释。
我应该补充一点,我所链接的这篇文章是专门针对ruby 1.8的。ruby 1.9中有一些变化,比如块变量是块的局部变量。在1.8中,你会得到如下内容:
>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Goodbye"
而1.9会给你:
>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Hello"
我在这台机器上没有1.9,所以上面可能有一个错误。
是的,一开始有点令人费解。
在Ruby中,方法可以接收一个代码块,以便执行任意的代码段。
当一个方法需要一个块时,可以通过调用yield函数来调用它。
例子:
以Person为例,它是一个具有name属性和do_with_name方法的类。当调用该方法时,它将把name属性传递给块。
class Person
def initialize( name )
@name = name
end
def do_with_name # expects a block
yield( @name ) # invoke the block and pass the `@name` attribute
end
end
现在可以调用此方法并传递任意代码块。
person = Person.new("Oscar")
# Invoking the method passing a block to print the value
person.do_with_name do |value|
puts "Got: #{value}"
end
将打印:
Got: Oscar
注意,块接收一个名为value的变量作为参数。当代码调用yield时,它将@name的值作为参数传递。
yield( @name )
相同的方法可以用不同的块调用。
例如反转名称:
reversed_name = ""
# Invoke the method passing a different block
person.do_with_name do |value|
reversed_name = value.reverse
end
puts reversed_name
=> "racsO"
其他更有趣的现实生活例子:
数组中的筛选元素:
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
# Select those which start with 'T'
days.select do | item |
item.match /^T/
end
=> ["Tuesday", "Thursday"]
或按名称长度排序:
days.sort do |x,y|
x.size <=> y.size
end
=> ["Monday", "Friday", "Tuesday", "Thursday", "Wednesday"]
如果块是可选的,你可以使用:
yield(value) if block_given?
如果不是可选的,只需调用它。
您可以在您的计算机上使用irb(交互式Ruby Shell)尝试这些示例。
以下是复制/粘贴表单中的所有示例:
class Person
def initialize( name )
@name = name
end
def do_with_name # expects a block
yield( @name ) # invoke the block and pass the `@name` attribute
end
end
person = Person.new("Oscar")
# Invoking the method passing a block to print the value
person.do_with_name do |value|
puts "Got: #{value}"
end
reversed_name = ""
# Invoke the method passing a different block
person.do_with_name do |value|
reversed_name = value.reverse
end
puts reversed_name
# Filter elements in an array:
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
# Select those which start with 'T'
days.select do | item |
item.match /^T/
end
# Sort by name length:
days.sort do |x,y|
x.size <=> y.size
end
Yield可以用作在方法中返回值的无名块。考虑下面的代码:
Def Up(anarg)
yield(anarg)
end
您可以创建一个方法“Up”,它被分配了一个参数。你现在可以将这个参数赋值给yield,它将调用并执行一个相关的块。您可以在参数列表之后分配块。
Up("Here is a string"){|x| x.reverse!; puts(x)}
当Up方法调用带有参数的yield时,它被传递给块变量以处理请求。
在Ruby中,块基本上是可以传递给任何方法并由任何方法执行的代码块。块总是与方法一起使用,方法通常向它们提供数据(作为参数)。
块在Ruby宝石(包括Rails)和编写良好的Ruby代码中被广泛使用。它们不是对象,因此不能赋值给变量。
基本语法
block是由{}或do. end括起来的一段代码。按照惯例,花括号语法应该用于单行块,do. end语法应该用于多行块。
{ # This is a single line block }
do
# This is a multi-line block
end
任何方法都可以接收块作为隐式参数。块由方法中的yield语句执行。基本语法是:
def meditate
print "Today we will practice zazen"
yield # This indicates the method is expecting a block
end
# We are passing a block as an argument to the meditate method
meditate { print " for 40 minutes." }
Output:
Today we will practice zazen for 40 minutes.
当到达yield语句时,冥想方法将控制权交给块,执行块中的代码并将控制权返回给方法,该方法在yield语句之后立即恢复执行。
当一个方法包含yield语句时,它期望在调用时接收一个块。如果没有提供块,一旦到达yield语句,就会抛出异常。我们可以将block设置为可选的,并避免引发异常:
def meditate
puts "Today we will practice zazen."
yield if block_given?
end meditate
Output:
Today we will practice zazen.
不可能将多个块传递给一个方法。每个方法只能接收一个块。
详见:http://www.zenruby.info/2016/04/introduction-to-blocks-in-ruby.html