我正在尝试理解块和yield以及它们在Ruby中的工作方式。
如何使用产量?我研究过的许多Rails应用程序都以一种奇怪的方式使用yield。
谁能给我解释一下或者告诉我怎么理解他们?
我正在尝试理解块和yield以及它们在Ruby中的工作方式。
如何使用产量?我研究过的许多Rails应用程序都以一种奇怪的方式使用yield。
谁能给我解释一下或者告诉我怎么理解他们?
当前回答
简单地说,yield允许您创建的方法获取和调用块。yield关键字是块中'stuff'将被执行的位置。
其他回答
我想补充一下为什么你会这样做在已经很好的答案上。
不知道你来自什么语言,但假设它是静态语言,这类事情看起来会很熟悉。这是在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();
}
}
}
忽略整个流链接的事情,想法是这样的
初始化需要清理的资源 使用资源 一定要清理干净
这是在ruby中如何做的
File.open("readfile.rb", "r") do |infile|
while (line = infile.gets)
puts "#{counter}: #{line}"
counter = counter + 1
end
end
完全不同。把这个分解一下
告诉File类如何初始化资源 告诉文件类如何处理它 嘲笑那些还在打字的Java家伙;-)
这里,不是处理第一步和第二步,而是将其委托给另一个类。正如您所看到的,这极大地减少了必须编写的代码量,从而使内容更易于阅读,并降低了内存泄漏或文件锁未清除等问题的可能性。
现在,并不是说你不能在java中做类似的事情,事实上,人们已经做了几十年了。这被称为策略模式。不同之处在于,如果没有块,对于像文件这样简单的例子,策略就会因为需要编写的类和方法的数量而变得多余。使用块,这是一种简单而优雅的方式,不以这种方式构建代码是没有任何意义的。
这并不是使用块的唯一方式,但是其他的(比如Builder模式,你可以在rails中的form_for api中看到)都非常相似,一旦你仔细思考,应该就会很清楚发生了什么。当您看到块时,通常可以安全地假设方法调用是您想要做的,并且块描述了您想要如何做。
很可能有人会在这里提供一个真正详细的答案,但我一直认为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中,方法可以检查调用方法时是否在正常参数之外提供了块。通常这是使用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 =)
我有时会这样使用yield:
def add_to_http
"http://#{yield}"
end
puts add_to_http { "www.example.com" }
puts add_to_http { "www.victim.com"}
Yield可以用作在方法中返回值的无名块。考虑下面的代码:
Def Up(anarg)
yield(anarg)
end
您可以创建一个方法“Up”,它被分配了一个参数。你现在可以将这个参数赋值给yield,它将调用并执行一个相关的块。您可以在参数列表之后分配块。
Up("Here is a string"){|x| x.reverse!; puts(x)}
当Up方法调用带有参数的yield时,它被传递给块变量以处理请求。