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:
- Block can accept arguments and returns a value.
- Block does not have their own name.
- Block consist of chunks of code.
- A block is always invoked with a function or can say passed to a method call.
- To call a block within a method with a value, yield statement is used.
- 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
Block syntax
object.method(variable) do | block variable |
# code block
end
# or
object.method(variable) { | block variable |
# code block
}
Examples:
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"]
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
- Sort by string alphabet
- Sort by string number
- 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 == b | 0 |
a > b | 1 |
a and b are not comparable | nil |
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