[读书笔记] Ruby 中的 Block 和 Iterator

冰天雪地
• 阅读 3273

Block

定义

rubysome_array.each { |value| puts value + 3 }

sum = 0
other_array.each do |value|
  sum += value
  puts value / sum
end
  • A block is somewhat like the body of an anonymous method
  • Block can take parameters
  • Block 只有被 method 调用时才会起作用,如果 method 中有参数,block 出现在最后面

Block 中的变量

如果 block 的本地变量的名字和 block 之外但是在同样 scope 里面的 变量名字一样,那他们两个是一样的。block 内变量的值会改变 block 外变量的值。

rubysum = 0
[1,2,3,4].each do |value|
  sum += value
  puts value / sum
end
puts sum  # => 30

如果 block 中的变量只出现在 block 中,那么它只是 block 中本地变量,无法在 block 之外被引用。

rubysum = 0
[1,2,3,4].each do |value|
  square = value * value
  sum += square
end
puts sum  # => 30
puts square # undefined local variable or method 'square' for main:Object <NameError>

Parameters to a block are always local to a block, even if they have the same name as locals in the surrounding scope.

rubyvalue =  "some shape"
[1,2].each { |value| puts value }
puts value

# 1
# 2
# some shape

You can define a block-local variables by putting them after s semicolon in the block's parameter list

rubysquare = "some shape"
sum = 0
[1,2,3,4].each do |value; square|
    square = value * value
    sum += square
end
puts sum # 30
puts square # some shape

By making square block-local, values assigned inside the block will not affect the value of the variable with the same name in the outer scope.

Blocks for Transactions

You can use blocks to define a chunk of code that must be run under some kind of transnational control

rubyclass File
  def self.open_and_process(*args)
    f = File.open(*args)
    yield f
    f.close
  end
end

File.open_and_process("testfile","r") do |file|
  while line = file.gets 
    puts line
  end
end

Blocks Can Be Objects

You can convert a block into an object, store it in variables, pass it around, and then invoke its code later.

如果 method 的最后一个参数前面有 & 符号 (&action), 那么当此 method 被调用时,Ruby 会找一个 code block, 这个 code block 被转换成 class Proc 的一个对象。

rubyclass ProcExample
  def pass_in_block(&action)
    @stored_proc = action
  end

  def use_proc(parameter)
    @store_proc.call(parameter)
  end
end

eg = ProcExample.new
eg.pass_in_block { |param| puts "The parameter is #{param}"  }
eg.use_proc(99)
# => The parameter is 99
rubydef create_block_object(&block)
  block
end

bo = create_block_object { |param| puts "You called me with #{param}" }
bo.call 99  # => You called me with 99
bo.call "cat" # => You called me with cat

Ruby have two built-in methods that convert a block to an object: lambda and Proc.new

rubybo = lambda { |param| puts "You called me with #{param}" }
bo.call 99 # => You called me with 99

Blocks Can Be Closures

Closure: Variables in the surrounding scope that are referenced in a block remain accessible accessible for the life of that block and the life on any Proc object created from that block.

rubydef n_times(thing)
  lambda {|n| thing * n}
end

p1 = n_times(23)
p1.call(3) #=> 69
p2.call(4) #=> 92

def power_proc_generator
  value = 1
  lambda { value += value }
end

power_proc = power_proc_generator
puts power_proc.call # 2
puts power_proc.call # 4

lambda 表达式的另一种简写方式

rubylambda { |params| ... }
# 与下面的写法等价
-> params { ... }
# parmas 是可选的
rubyproc1 = -> arg1, arg2 {puts "#{arg1} #{arg2}"}

proc1.call "hello", "world"
# => hello world

proc2 = -> { "Hello World" }
proc2.call # => Hello World

Block Parameter List

Blocks can take default values, splat args, keyword args and a block parameter

rubyproc = -> a, *b, &block do 
  puts "a = #{a.inspect}"
  puts "b = #{b.inspect}"
  block.call
end

proc.call(1,2,3,4) {puts "in block"}
# a = 1
# b = [2,3,4]
# in block

Iterator

定义

A Ruby iterator is simple a method that can invoke a block of code.

  1. Block 一般是跟着 method 出现的, 并且 block 中的代码不一定会执行
  2. 如果 method 中有 yield, 那么它的block 中的代码会被执行
  3. Block 可以接收参数,和返回 value
rubydef two_times
    yield
    yield
end
two_times { puts "Hello" }
# Hello
# Hello
rubydef fib_up_to(max)
  i1, i2 = 1. 1
  while i1 <= max
      yield i1
      i1, i2 = i2, i1 + i2
  end
end

fib_up_to(1000) { |f| print f, " " }

# 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
  • 上面代码中的 yield 之后的 i1 会作为 parameter 传入到 block 中, 赋值给 block 的 argument f
  • Block 中可以有多个 arguments.

常见的 iterator

each

each is probable the simplest iterator - all it does is yield successive elements of its collection.

ruby[1, 3, 5, 7, 9].each { |i| puts i }

# 1 
# 3
# 5
# 7
# 9

find

A blocl may also return a value to the method. The value of the last expression evaluated in the block is passed back to the method as the value of the yield.

rubyclass Array
  def find
    each do |value|
        return value if yield(value)
    end
  end
end

[1,3,4,7,9].find { |v| V*V > 30 } # => 7

collect (also known as map)

Which takes each element from the collection and passes it to the block. The results returned by the block are used to construct a new array

ruby["H", "A", "L"].collect { |x| x.succ } # => ["I", "B", "M"]

inject

The inject method lets you accumulate a value across the members of a collection.

ruby[1,3,5,7].inject { |sum, element| sum + element } # => 16

# sum = 1, element = 3
# sum = 4, element = 5
# sum = 9, element = 7
# sum = 16

[1,3,5,6].inject { |product, element| product*element } # => 105

If inject is called with no parameter, it uses the first element of the collections as the initial value and starts the iteration with the second value.

上面代码的另一种简便写法:

ruby[1,3,5,7].inject(:+) # => 16
[1,3,5,7]/inject(:*) # => 105

Iterator 和 I/O 系统的交互

Iterators 不仅仅能够访问 Array 和 Hash 中的数据, 和可以和 I/O 系统交互

rubyf = File.open("testfile")
f.each do |line|
  puts "The line is: #{line}"
end
f.close

produces:
The line is: This is line one
The line is: This is line two
The line is: This is line three

Parameter 和 Argument

目前,我的理解是 Parameter 是实际参数,而 Argument 是形式参数

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Oracle 统计表空间和对象历史增长量
最近7天内每天(某个)表空间的增长量colTS_NAMEfora15SELECTa.snap_id,a.rtime,c.tablespace_namets_name,round(a.tablespace_sizec.block_size/1024/1024/1024,
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这