我试图理解块以及yield
它们如何在Ruby中工作.
如何yield
使用?我看过的许多Rails应用程序都是yield
以一种奇怪的方式使用的.
有人可以向我解释或告诉我去哪里去理解它们吗?
Osc*_*Ryz 375
是的,一开始有点令人费解.
在Ruby中,方法可以接收代码块以执行任意代码段.
当一个方法需要一个块时,它会通过调用该yield
函数来调用它.
例如,这非常方便迭代列表或提供自定义算法.
请看以下示例:
我将定义一个Person
用名称初始化的类,并提供一个do_with_name
方法,在调用时,只将name
属性传递给接收到的块.
class Person
def initialize( name )
@name = name
end
def do_with_name
yield( @name )
end
end
Run Code Online (Sandbox Code Playgroud)
这将允许我们调用该方法并传递任意代码块.
例如,要打印我们要做的名称:
person = Person.new("Oscar")
#invoking the method passing a block
person.do_with_name do |name|
puts "Hey, his name is #{name}"
end
Run Code Online (Sandbox Code Playgroud)
会打印:
Hey, his name is Oscar
Run Code Online (Sandbox Code Playgroud)
注意,块接收一个名为变量的变量作为参数name
(NB你可以随意调用这个变量,但调用它是有意义的name
).当代码调用时,yield
它将使用值填充此参数@name
.
yield( @name )
Run Code Online (Sandbox Code Playgroud)
我们可以提供另一个块来执行不同的操作.例如,反转名称:
#variable to hold the name reversed
reversed_name = ""
#invoke the method passing a different block
person.do_with_name do |name|
reversed_name = name.reverse
end
puts reversed_name
=> "racsO"
Run Code Online (Sandbox Code Playgroud)
我们使用完全相同的方法(do_with_name
) - 它只是一个不同的块.
这个例子很简单.更有趣的用法是过滤数组中的所有元素:
days = ["monday", "tuesday", "wednesday", "thursday", "friday"]
# select those which start with 't'
days.select do | item |
item.match /^t/
end
=> ["tuesday", "thursday"]
Run Code Online (Sandbox Code Playgroud)
或者,我们也可以提供自定义排序算法,例如基于字符串大小:
days.sort do |x,y|
x.size <=> y.size
end
=> ["monday", "friday", "tuesday", "thursday", "wednesday"]
Run Code Online (Sandbox Code Playgroud)
我希望这能帮助你更好地理解它.
顺便说一句,如果该块是可选的,您应该将其称为:
yield(value) if block_given?
Run Code Online (Sandbox Code Playgroud)
如果不是可选的,只需调用它.
mae*_*ics 24
在Ruby中,方法可以检查它们是否以除了普通参数之外还提供块的方式被调用.通常,这是使用该block_given?
方法完成的,但您也可以通过在&
最终参数名称前添加一个&符号()来将该块称为显式Proc .
如果使用块调用方法,则该方法可以yield
根据需要使用一些参数控制块(调用块).请考虑以下示例方法:
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 =)
Run Code Online (Sandbox Code Playgroud)
或者,使用特殊块参数语法:
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 =)
Run Code Online (Sandbox Code Playgroud)
the*_*eIV 22
很可能有人会在这里提供一个真正详细的答案,但我总是发现Robert Sosinski的这篇文章是对块,过程和lambdas之间微妙之处的一个很好的解释.
我应该补充一点,我相信我链接的帖子特定于ruby 1.8.ruby 1.9中的一些内容已经发生了变化,例如块变量是块的本地变量.在1.8中,你会得到如下内容:
>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Goodbye"
Run Code Online (Sandbox Code Playgroud)
而1.9会给你:
>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Hello"
Run Code Online (Sandbox Code Playgroud)
我在这台机器上没有1.9,所以上面可能有错误.
Mat*_*ggs 13
我想补充一下为什么你会按照已经很好的答案做事情.
不知道你来自哪种语言,但假设它是一种静态语言,这种事情看起来很熟悉.这是您在java中读取文件的方式
public class FileInput {
public static void main(String[] args) {
File file = new File("C:\\MyFile.txt");
FileInputStream fis = null;
BufferedInputStream bis = null;
DataInputStream dis = null;
try {
fis = new FileInputStream(file);
// Here BufferedInputStream is added for fast reading.
bis = new BufferedInputStream(fis);
dis = new DataInputStream(bis);
// dis.available() returns 0 if the file does not have more lines.
while (dis.available() != 0) {
// this statement reads the line from the file and print it to
// the console.
System.out.println(dis.readLine());
}
// dispose all the resources after using them.
fis.close();
bis.close();
dis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Run Code Online (Sandbox Code Playgroud)
忽略整个流链接的东西,这个想法就是这个
这就是你在ruby中的表现
File.open("readfile.rb", "r") do |infile|
while (line = infile.gets)
puts "#{counter}: #{line}"
counter = counter + 1
end
end
Run Code Online (Sandbox Code Playgroud)
完全不同.打破这一个
在这里,您基本上将其委托给另一个类,而不是处理第一步和第二步.正如您所看到的,这会大大降低您必须编写的代码量,从而使事情更容易阅读,并减少内存泄漏或文件锁定无法清除的可能性.
现在,它不像你不能在java中做类似的东西,事实上,人们已经做了几十年了.它被称为战略模式.区别在于没有块,对于像文件示例这样简单的东西,由于需要编写的类和方法的数量,策略变得过度.使用块,它是这样一种简单而优雅的方式,没有任何意义,不以这种方式构建代码.
这不是使用块的唯一方法,但是其他的(比如Builder模式,你可以在rails_for api中看到它们在rails中)足够相似,一旦你绕开它,它应该是显而易见的.当你看到块时,通常可以安全地假设方法调用是你想要做的,而块正在描述你想要怎么做.
zel*_*nix 12
我发现这篇文章非常有用.特别是,以下示例:
#!/usr/bin/ruby
def test
yield 5
puts "You are in the method test"
yield 100
end
test {|i| puts "You are in the block #{i}"}
test do |i|
puts "You are in the block #{i}"
end
Run Code Online (Sandbox Code Playgroud)
哪个应该给出以下输出:
You are in the block 5
You are in the method test
You are in the block 100
You are in the block 5
You are in the method test
You are in the block 100
Run Code Online (Sandbox Code Playgroud)
所以基本上每次调用yield
ruby都会在do
块或内部运行代码{}
.如果yield
提供了参数,则将其作为参数提供给do
块.
对我来说,这是我第一次真正了解这些do
街区正在做什么.它基本上是一种函数访问内部数据结构的方法,用于迭代或函数配置.
因此,在rails中,您可以编写以下内容:
respond_to do |format|
format.html { render template: "my/view", layout: 'my_layout' }
end
Run Code Online (Sandbox Code Playgroud)
这将运行respond_to
产生do
具有(内部)format
参数的块的函数.然后,您.html
可以在此内部变量上调用该函数,然后生成代码块以运行该render
命令.请注意,.html
只有在请求的文件格式时才会产生.(技术性:这些函数实际上并block.call
没有yield
从源头看到,但功能基本相同,请参阅此问题进行讨论.)这为函数提供了一种方法,可以执行一些初始化,然后从调用代码中获取输入,然后根据需要进行处理.
或者换句话说,它类似于将匿名函数作为参数然后在javascript中调用它的函数.
小智 7
在Ruby中,块基本上是一块代码,可以传递给任何方法并由其执行.块总是与方法一起使用,这些方法通常将数据提供给它们(作为参数).
块广泛用于Ruby gem(包括Rails)和编写良好的Ruby代码中.它们不是对象,因此不能分配给变量.
块是由{}或do..end包围的一段代码.按照惯例,大括号语法应该用于单行块,而do..end语法应该用于多行块.
{ # This is a single line block }
do
# This is a multi-line block
end
Run Code Online (Sandbox Code Playgroud)
任何方法都可以接收块作为隐式参数.块由一个方法中的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.
Run Code Online (Sandbox Code Playgroud)
当达到yield语句时,meditate方法产生对块的控制,执行块内的代码并将控制返回给方法,该方法在yield语句之后立即恢复执行.
当方法包含yield语句时,它期望在调用时接收块.如果未提供块,则在达到yield语句后将抛出异常.我们可以使块可选,并避免引发异常:
def meditate
puts "Today we will practice zazen."
yield if block_given?
end meditate
Output:
Today we will practice zazen.
Run Code Online (Sandbox Code Playgroud)
无法将多个块传递给方法.每种方法只能接收一个块.
有关详细信息,请访问:http://www.zenruby.info/2016/04/introduction-to-blocks-in-ruby.html
小智 5
我有时会像这样使用"yield":
def add_to_http
"http://#{yield}"
end
puts add_to_http { "www.example.com" }
puts add_to_http { "www.victim.com"}
Run Code Online (Sandbox Code Playgroud)