Ruby中的块和产量

Ruby中的块和产量,ruby,block,Ruby,Block,我试图理解块和产生以及它们在Ruby中的工作方式 如何使用yield?我看过的许多Rails应用程序都以一种奇怪的方式使用产生 有人能给我解释一下或者告诉我去哪里理解它们吗?在Ruby中,方法可以检查它们的调用方式是否在正常参数之外还提供了一个块。通常,这是使用block\u given?方法完成的,但您也可以通过在最终参数名称前加上一个符号(&)将该块作为显式过程引用 如果使用块调用一个方法,那么如果需要,该方法可以使用一些参数将控制权交给块(调用块)。考虑这个示例方法,演示: def foo

我试图理解块和
产生
以及它们在Ruby中的工作方式

如何使用
yield
?我看过的许多Rails应用程序都以一种奇怪的方式使用
产生


有人能给我解释一下或者告诉我去哪里理解它们吗?

在Ruby中,方法可以检查它们的调用方式是否在正常参数之外还提供了一个块。通常,这是使用
block\u given?
方法完成的,但您也可以通过在最终参数名称前加上一个符号(
&
)将该块作为显式过程引用

如果使用块调用一个方法,那么如果需要,该方法可以
使用一些参数将
控制权交给块(调用块)。考虑这个示例方法,演示:

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那里发现,这是对块、过程和lambdas之间微妙关系的一个很好的解释

我应该补充一点,我相信我链接到的帖子是ruby 1.8特有的。Ruby1.9中的一些东西已经改变了,比如块变量是块的局部变量。在1.8中,您将得到如下内容:

>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Goodbye"
respond_to do |format|
  format.html { render template: "my/view", layout: 'my_layout' }
end
enum_for(method = :each, *args) → enum
Creates a new Enumerator which will enumerate by calling method on obj, passing args if any.

str = "xyz"
enum = str.enum_for(:each_byte)
enum.each { |b| puts b }    
# => 120
# => 121
# => 122
鉴于1.9将为您提供:

>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Hello"

我在这台机器上没有1.9版本,所以上面可能有一个错误。

我想在已经很好的答案中添加为什么你会这样做

不知道您来自哪种语言,但假设它是一种静态语言,这类事情看起来会很熟悉。这就是用java读取文件的方式

公共类文件输入{
公共静态void main(字符串[]args){
File File=新文件(“C:\\MyFile.txt”);
FileInputStream fis=null;
BufferedInputStream bis=null;
DataInputStream dis=null;
试一试{
fis=新文件输入流(文件);
//此处添加了BufferedInputStream以实现快速读取。
bis=新的缓冲数据流(fis);
dis=新数据输入流(bis);
//如果文件没有更多行,则dis.available()返回0。
while(dis.available()!=0){
//此语句从文件中读取该行并将其打印到
//控制台。
System.out.println(dis.readLine());
}
//使用完所有资源后,请进行处置。
fis.close();
二、关闭();
dis.close();
}catch(filenotfounde异常){
e、 printStackTrace();
}捕获(IOE异常){
e、 printStackTrace();
}
}
}
忽略整个流链接的事情,想法是这样的

  • 初始化需要清理的资源
  • 使用资源
  • 一定要把它清理干净
  • 在ruby中就是这样做的

    File.open("readfile.rb", "r") do |infile|
        while (line = infile.gets)
            puts "#{counter}: #{line}"
            counter = counter + 1
        end
    end
    
    完全不同。把这个拆了

  • 告诉File类如何初始化资源
  • 告诉file类如何处理它
  • 嘲笑那些还在打字的java家伙;-) 在这里,您不需要处理第一步和第二步,而是将其委托给另一个类。正如您所看到的,这大大减少了您必须编写的代码量,从而使内容更易于阅读,并减少了内存泄漏或文件锁未被清除的可能性

    现在,并不是说你不能在java中做类似的事情,事实上,人们已经做了几十年了。这就是所谓的模式。不同之处在于,如果没有块,对于像文件示例这样简单的东西,由于需要编写的类和方法的数量太多,策略变得过于复杂。对于块,这是一种非常简单和优雅的方式,因此不以这种方式构造代码是没有任何意义的


    这并不是使用块的唯一方式,但其他方式(如构建器模式,您可以在rails中api的形式_中看到)非常相似,一旦您了解了这一点,就会清楚地知道发生了什么。当您看到块时,通常可以安全地假设方法调用是您想要做的,并且块描述了您想要如何做。

    是的,一开始有点令人费解

    在Ruby中,方法可以接收代码块以执行任意代码段

    当一个方法需要一个块时,它通过调用
    yield
    函数来调用它

    例如,这非常方便,可以迭代列表或提供自定义算法

    以以下为例:

    #!/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
    
    我将定义一个用名称初始化的
    Person
    类,并提供一个
    do_with_name
    方法,调用该方法时,只需将
    name
    属性传递给接收到的块

    class Person 
        def initialize( name ) 
             @name = name
        end
    
        def do_with_name 
            yield( @name ) 
        end
    end
    
    这将允许我们调用该方法并传递任意代码块

    例如,要打印名称,我们将执行以下操作:

    person = Person.new("Oscar")
    
    #invoking the method passing a block
    person.do_with_name do |name|
        puts "Hey, his name is #{name}"
    end
    
    将打印:

    Hey, his name is Oscar
    
    请注意,该块作为参数接收一个名为
    name
    (注意,您可以随意调用该变量,但调用
    name
    )的变量。当代码调用
    yield
    时,它会用
    @name
    的值填充此参数

    yield( @name )
    
    我们可以提供另一个块来执行不同的操作。例如,反转名称:

    #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"
    
    我们使用了完全相同的方法(
    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"]
    
    或者,我们也可以提供自定义排序算法,例如基于字符串大小:

     days.sort do |x,y|
        x.size <=> y.size
     end
    
    => ["monday", "friday", "tuesday", "thursday", "wednesday"]
    
    如果不是可选的,就调用它

    编辑


    @hmak为这些示例创建了一个repl.it:

    简单地说,就是允许您创建的方法获取和调用块。yield关键字是执行块中“stuff”的位置。

    我发现它非常有用。特别是以下示例:

    #!/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
    
    哪个应该
    def add_to_http
       "http://#{yield}"
    end
    
    puts add_to_http { "www.example.com" }
    puts add_to_http { "www.victim.com"}
    
    { # This is a single line block }
    
    do
      # This is a multi-line block
    end 
    
    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.
    
    def meditate
      puts "Today we will practice zazen."
      yield if block_given? 
    end meditate
    
    Output:
    Today we will practice zazen. 
    
    Def Up(anarg)
      yield(anarg)
    end
    
    Up("Here is a string"){|x| x.reverse!; puts(x)}
    
    class Fruit
      attr_accessor :kinds
    
      def initialize 
        @kinds = %w(orange apple pear banana)
      end
    
      def each 
        puts 'inside each'
        3.times { yield (@kinds.tap {|kinds| puts "selecting from #{kinds}"} ).sample }
      end  
    end
    
    f = Fruit.new
    f.each do |kind|
      puts 'inside block'
    end    
    
    => inside each
    => selecting from ["orange", "apple", "pear", "banana"]
    => inside block
    => selecting from ["orange", "apple", "pear", "banana"]
    => inside block
    => selecting from ["orange", "apple", "pear", "banana"]
    => inside block
    
    class Fruit
      def initialize
        @kinds = %w(orange apple)
      end
    
      def kinds
        yield @kinds.shift
        yield @kinds.shift
      end
    end
    
    f = Fruit.new
    enum = f.to_enum(:kinds)
    enum.next
     => "orange" 
    enum.next
     => "apple" 
    
    enum_for(method = :each, *args) → enum
    Creates a new Enumerator which will enumerate by calling method on obj, passing args if any.
    
    str = "xyz"
    enum = str.enum_for(:each_byte)
    enum.each { |b| puts b }    
    # => 120
    # => 121
    # => 122
    
    str = "I like fruit"
    enum = str.to_enum
    enum.next
    => NoMethodError: undefined method `each' for "I like fruit":String