Skip to main content Link Search Menu Expand Document (external link)

Block

A block is the same thing as a method, but it does not belong to an object. Blocks are called closures in other programming languages. There are some important points about Blocks in Ruby:

  1. Block can accept arguments and returns a value.
  2. Block does not have their own name.
  3. Block consist of chunks of code.
  4. A block is always invoked with a function or can say passed to a method call.
  5. To call a block within a method with a value, yield statement is used.
  6. Blocks can be called just like methods from inside the method that it is passed to.

For example, between do and end is a block, i is block variable.

[1, 2, 3, 4, 5].each do |i|
  puts i ** 2
end

Table of contents

  1. Block syntax
  2. Block usage
    1. iterator
    2. Sorting
  3. define a block method
    1. Block params and value
    2. Block control

Block syntax

object.method(variable) do | block variable |
  # code block
end

# or

object.method(variable) { | block variable |
 # code block
}

Examples:

  1. Array#each
    # ary is object
    # each is method
    # obj is block variable
    irb(main):001:0> ary = ['a', 'b', 'c']
    => ["a", "b", "c"]
    irb(main):002:0> ary.each { |obj| p obj}  
    "a"
    "b"
    "c"
    => ["a", "b", "c"]
    
  2. Array#each_with_index
    # ary is object
    # each_with_index is method
    # it has two block variable, object and index vlaue 
    irb(main):001:0> ary = ['a', 'b', 'c']
    => ["a", "b", "c"]
    irb(main):002:0> ary.each_with_index{ |obj, idx| p "array index is #{idx}, object is #{obj}"}
    "array index is 0, object is a"
    "array index is 1, object is b"
    "array index is 2, object is c"
    => ["a", "b", "c"]
    

Block usage

iterator

Ruby use block to handle iterator, each is a typical iterator.

Array#each

irb(main):001:0> alphabet = ["a", "b", "c", "d", "e"]
=> ["a", "b", "c", "d", "e"]
irb(main):002:0> alphabet.each do |i|
irb(main):003:1*   puts i.upcase
irb(main):004:1> end
A
B
C
D
E
=> ["a", "b", "c", "d", "e"]

Hash#each

Hash#each block variable is an Array hash key is array index value 0 pair[0] hash value is array index value 1 pair[1]

irb(main):001:0> sum = 0
=> 0
irb(main):002:0> outcome = {foo: 1000, bar: 1000, baz: 4000}
=> {:foo=>1000, :bar=>1000, :baz=>4000}
irb(main):003:0> outcome.each do |pair|
irb(main):004:1*   puts "#{pair.class}, pair[0] is #{pair[0]}, pair[1] is #{pair[1]}"
irb(main):005:1>   sum += pair[1]
irb(main):006:1> end
Array, pair[0] is foo, pair[1] is 1000
Array, pair[0] is bar, pair[1] is 1000
Array, pair[0] is baz, pair[1] is 4000
=> {:foo=>1000, :bar=>1000, :baz=>4000}
irb(main):007:0> puts "total is #{sum}"
total is 6000
=> nil

Sorting

The most basic form of sorting is provided by the Ruby sort method

  1. Sort by string alphabet
  2. Sort by string number
  3. Sort by string length

Array#sort

array = ["ruby", "Perl", "PHP", "Python"]
p array.sort    #=> ["PHP", "Perl", "Python", "ruby"]

Comparable uses <=> to implement the conventional comparison operators (<, <=, ==, >=, and >) and the method between?. compare a and b in <=>

a < b-1
a == b0
a > b1
a and b are not comparablenil

sort will base on those return value to resort element

array = ["ruby", "Perl", "PHP", "Python"]
p array.sort    #=> ["PHP", "Perl", "Python", "ruby"]
# equal

p array.sort{|a, b| a <=> b}    #=> ["PHP", "Perl", "Python", "ruby"]

Ruby also can sort by string length

array = ["ruby", "Perl", "PHP", "Python"]
p array.sort{|a, b| a.length <=> b.length}    #=> ["PHP", "ruby", "Perl", "Python"]

Ruby sort a array, the block will use two elements compare it. let try how many times compare each element.

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
)
# %w same with ["Ruby", "is", "a", "open", ......]
call_num = 0
sorted = ary.sort do |a, b|
  call_num += 1
  a.length <=> b.length
end
puts "result is #{sorted}"                      #=> result is ["a", "a", "is", "to", "to", "an", "It", "is", "on", "and", "and", "has", "Ruby", "open", "with", "that", "read", "easy", "focus", "write", "syntax", "source", "elegant", "natural", "language", "simplicity", "programming", "productivity."]
puts "number of array elements #{ary.length}"   #=> number of array elements 28
puts "#{call_num} times call block"             #=> 72 times call block

define a block method

Block params and value

def total(from, to)
  result = 0
  from.upto(to) do |num|    # handle from to value
    if block_given?         # if the code with block, ex: {|num| num ** 2 }
      result += yield(num)  # use block code to add
    else                    # if no block
      result += num         # add the current number
    end
  end
  return result             # return the value
end

p total(1, 10)                  # SUM 1 to 10 = 55
p total(1, 10){|num| num ** 2 } # SUM 1 to 10 in 2 exponents = 385

in the example above yield(num) will put the number into block code num ** 2, num ** 2 return value will add to result

block arguments test

# block_args_test.rb
def block_args_test
  yield()             # 0 block variable
  yield(1)            # 1 block variable
  yield(1, 2, 3)      # 3 block variable
end

puts "send block variable by |a|"
block_args_test do |a|
  p [a]
end
puts

puts "send block variables by |a, b, c|"
block_args_test do |a, b, c|
  p [a, b, c]
end
puts

puts "send block variables by |*a|"
block_args_test do |*a|
  p [a]
end
puts

result

> ruby block_args_test.rb
send block variable by |a|
[nil]
[1]
[1]

send block variables by |a, b, c|
[nil, nil, nil]
[1, nil, nil]
[1, 2, 3]

send block variables by |*a|
[[]]
[[1]]
[[1, 2, 3]]

block args not exist, it will show nil

block args more than block variables, only show number of block variables, like case send block variable by |a| only show [1]

block args less than block variables, it will show nil, like case send block variables by |a, b, c|

Ruby will let the block arg to an array when use *a.

Ruby can let parameter grouping in yield

# param_grouping.rb
hash = {a: 100, b: 200, c: 300}
hash.each_with_index do |(key, value), index|
  p [key, value, index]
end

> ruby param_grouping.rb
[:a, 100, 0]
[:b, 200, 1]
[:c, 300, 2]

Block control

if we use break in total

n = total(1, 10) do |num|
  if num == 5
    break
  end
  num
end
p n     #=> ??

The answer is nil. Same with loop or each, Ruby can use break to stop iterate

break will stop the total

break 0 will stop the total and return 0

we can use next to adjust this code, same with break, next 0 mean 0 will be the return value.

n = total(1, 10) do |num|
  if num % 2 != 0
    next 0
  end
  num
end
p n     #=> 30

Back to top

Copyright © 2020-2022 Secure Smarter Service, Inc. This site is powered by KodaCamp.