• 《Rubu基础教程第五版》第十一章笔记 块


    块(block)就是在调用方法时能与参数一期传递的多个处理的集合。

    对象.方法名(参数列表) do |块变量|

      希望循环的处理

    end

    或者

    对象.方法名(参数列表) do { |块变量| 希望循环的处理}

    块的开头是块变量,块变量就是在执行块时,从方法传进来的参数

    >> ary = ("a".."c").to_a
    => ["a", "b", "c"]
    >> ary.each{|obj| p obj}
    "a"
    "b"
    "c"
    => ["a", "b", "c"]
    >> ary.each_with_index{|obj, index| p [obj, index]}
    ["a", 0]
    ["b", 1]
    ["c", 2]
    => ["a", "b", "c"]
    >> 
    

    块的使用方法

    在ruby中,我们常常使用块来实现循环。在接收块的方法中,实现了循环处理的方法称为迭代器(iterator)。each方法就是一个典型的迭代器,相当于Python中的iter

    >> hash.each {|name| p name}
    [:a, "b"]
    => {:a=>"b"}
    >> 
    

     下面演示了each对hash的操作

    sum = 0
    outcome = {"参加费"=>1000, "挂号费"=>1000, "联欢费"=>4000}
    outcome.each do |pair|
      sum += pair[1]
    end
    
    puts "合计: #{sum}"
    
    sum = 0
    outcome = {"参加费"=>1000, "挂号费"=>1000, "联欢费"=>4000}
    outcome.each do |item, price|
      sum += price
    end
    
    puts "合计: #{sum}"
    

    逐行读取文件

    file = File.open("file_each.rb")
    
    file.each_line do |line|
      print line
    end
    file.close
    

    隐藏常规处理

    这个有点像Python中的 with open as

    File.open("file_open.rb") do |file|   # 类型Python中的with open
      file.each_line do |line|
        print line
      end
    end
    

     这个代码普通写法如下

    file = File.open("file_open_no_block.rb")
    begin
      file.each_line do |line|
        print line
      end
    ensure
      file.close
    end
    

    替换部分算法

    通过块的设置,指定sort规则

    Array#sort方法没有指定块时,会使用<=>运算符对各个元素进行比较,并根据比较后的结果进行排序。<=>运算符的返回值为-1,0,1中的一个

    a <=> b  当a < b -1; a == b 0; a > b 1,排序的时候,a < b 排前面

    这个跟Python中的sorted(key差不多)

    ary = %w(
     Ruby is a open source programming language with a focus 
     on simplicity and productivity.It has an elegant syntax
     that is natural to read and easy to write
    )
    
    sorted = ary.sort_by{|item| item.length }
    p sorted
    
    call_num = 0 # 块的调用次数 sorted = ary.sort do |a, b| call_num += 1 a.length <=> b.length # 长度进行排序 end puts "排序结果 #{sorted}" puts "数组的元素数量 #{ary.length}" puts "调用块的次数 #{call_num}"

    从执行可以看出调用长度的方法调用了很多次,可以改成sort_by进行排序,在上面

    定义块的方法

    块的方法跟Python中的生成器比较像,生成器也使迭代器

    def myloop
      while true
        yield
      end
    end
    
    num = 1
    myloop do 
       puts "num is #{num}"
       break if num > 100
       num *= 2
    end
    

    传递块参数,获取块的值

    有意思,有点生产者与消费者的关系

    def total(from, to)
      result = 0
      from .upto(to) do |num|
        if block_given?     # 如果有块的话
          result += yield(num)   # 累加经过块处理的值
        else
          result += num    #没有块处理直接累加
        end
      end
      result
      end
    
    p total(1, 10)
    p total(1, 10){|num| num **2 }   # 这个后面的块处理就像前面的num是yield传过来的参数,后面的num ** 2将传递给result
    

    测试yield返回多个值的情况

    通过|a|接收块变量
    [nil]
    [1]
    [1]
    
    通过|a, b, c|接收块变量
    [nil, nil, nil]
    [1, nil, nil]
    [1, 2, 3]
    
    通过|*a|接收块变量
    [[]]
    [[1]]
    [[1, 2, 3]]
    
    shijianzhongdeMacBook-Pro:chapter_11 shijianzhong$ 
    

    下面使代码,分别用多值或者单值或者不定长参数接收参数

    shijianzhongdeMacBook-Pro:chapter_11 shijianzhong$ cat block_args_test.rb 
    def block_args_test
      yield()    # 0个块变量
      yield(1)   # 1个块变量
      yield(1, 2, 3)  # 3个块变量
    end
    
    puts "通过|a|接收块变量"
    block_args_test do |a|
      p [a]
    end
    
    puts
    
    puts "通过|a, b, c|接收块变量"
    block_args_test do |a, b, c|
      p [a, b, c]
    end
    
    puts
    
    puts "通过|*a|接收块变量"
    block_args_test do |*a|
      p [a]
    end
    
    puts
    

    控制块的执行

    def total(from, to)
      result = 0
      from .upto(to) do |num|
        if block_given?     # 如果有块的话
          result += yield(num)   # 累加经过块处理的值
        else
          result += num    #没有块处理直接累加
        end
      end
      result
      end
    
    p total(1, 10)
    p total(1, 10){|num| num **2 }   # 这个后面的块处理就像前面的num是yield传过来的参数,后面的num ** 2将传递给result
    
    n = total(1, 10) do |num|
      if num == 5
        break    # 默认这里就停了,返回nil  
      end
      num
    end
    p n
    
    m = total(1, 10) do |num|
      if num % 2 != 0
        next 0             # 当不等于1,3等的时候返回返回0
      end
      num
    end
    
    p m
    

    将块封装为对象

    把块当做对象操作时,我们需要用到Proc对象。定义Proc对象的典型的方法是,调用Proc.new方法这个带块的方法。在调用Proc对象的call方法之前,块中定义的程序不会执行

    这个有点像Python中的匿名函数 lambda

    hello = Proc.new  {
     |name| puts "hello #{name}"
    }
    
    hello.call("sidian")
    hello.call("laji huawei")
    

    把块的一个方法传给另一个方法时,首先会通过变量将块作为Proc对象接收,然后再传给另一个方法。在方法定义时,如果末尾的参数使用"&参数名"的形式,Ruby就会自动把调用方法时传进来的块封装成Proc对象

    shijianzhongdeMacBook-Pro:chapter_11 shijianzhong$ cat total2.rb 
    def total(from, to, &block)
      result = 0
      from .upto(to) do |num|
        if block     # 如果有块的话
          result += block.call(num)   # 累加经过块处理的值
        else
          result += num    #没有块处理直接累加
        end
      end
      result
      end
    
    p total(1, 10)
    p total(1, 10){|num| num **2 }   # 这个后面的块处理就像前面的num是yield传过来的参数,后面的num ** 2将传递给result
    

     在自定义方法的时候,定义了&block参数,像这样在变量名前添加&的参数称为Proc参数。如果没有传参就是nil,如果传参了可以通过block.call调用

    Proc参数一定要在最后一个位置

    proc.call的调用像执行一个匿名函数,下面这种是直接通过方法后面执行的逻辑,参数&block,到函数里面不用变

    将块封装为Proc对象后,我们就可以根据需要随时调用块,甚至还可以将其赋值给实例变量,让别的实例方法取任意调用。

    def call_each(ary, &block)
      ary.each(&block)
    end
    
    call_each [1, 2, 3] do |item|  # 调用这个方法,传入两个参数
      p item
    end
    

    局部变量与块变量

    块外部定义的局部变量,在块中可以继续使用。而被作为块变量使用的变量,即使与块外部的变量同名,Ruby也会认为它们是两个不同的变量

    x = 1
    y = 1
    ary = [1, 2, 3]
    
    ary.each do |x|   # 块变量与外部的局部变量重名,但不会影响局部变量
      y = x       # 将x 赋值给y,符修改局部变量y的值
    end
    
    p [x, y]
    

    局部变量中没有的变量名,在块中进行赋值,后续在块的外面无法读取到该变量

    x = 1
    # y = 1  # 屏蔽外部变量局部变量,在块内部进行变量赋值,外部无法读取
    ary = [1, 2, 3]
    
    ary.each do |x|   # 块变量与外部的局部变量重名,但不会影响局部变量
      y = x       # 将x 赋值给y,符修改局部变量y的值
    end
    
    p [x, y]
    

    当不想修改局部变量的时候,可以定义块局部变量

    x = y = z = 0
    ary = [1, 2, 3]
    ary.each do |a; y|   # 这个分号与逗号都可以用,y就变成了块局部变量,这样y的值修改,不会影响块外面y的值
      x = a
      y = a
      z = a
      p [x, y, z]
    end
    puts
    p [x, y, z]
    
  • 相关阅读:
    图片api
    基于NoneBot的天气查询插件
    在Linux云服务上运行酷Q机器人并DIY自己的功能
    破解zip密码的几种方法
    攻防世界wp--crypto 幂数加密
    攻防世界wp--crypto Caesar
    攻防世界wp--web command_execution
    攻防世界wp--web weak_auth
    python中yield的用法以及和yield from的区别
    Linux进阶之正则,shell三剑客(grep,awk,sed),cut,sort,uniq
  • 原文地址:https://www.cnblogs.com/sidianok/p/13032223.html
Copyright © 2020-2023  润新知