[讀書筆記] Ruby 中的 Block 和 Iterator

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.express

Blocks for Transactions

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

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.ide

若是 method 的最後一個參數前面有 & 符號 (&action), 那麼當此 method 被調用時,Ruby 會找一個 code block, 這個 code block 被轉換成 class Proc 的一個對象。ui

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.newlua

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.code

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 parameterthree

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.element

  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.rem

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 是形式參數

相關文章
相關標籤/搜索