How to use blocks in Ruby

6

I'm having a hard time understanding blocks in Ruby. Could someone explain to me why we use it?

    
asked by anonymous 10.04.2015 / 22:53

2 answers

6

You are familiar with closures ( in English )? It's the same thing . You can see more in another question made here on the site. It is not the same language but the idea is the same. Java 8 has the same feature - albeit with slightly different little details. p>

The block is a piece of code that, roughly speaking, can be bound to a variable. And obviously can be passed as parameter. Then you define a code at any given time but your invocation will only happen at another time, in another location.

This is widely used to customize behaviors. So you may have some method that needs to perform a task, but it does not know how to run all the parts. So it can be more flexible. These parts will be defined by another part of the application code. In general, this method takes what it should perform per parameter or takes it through a member of the class or even calling another method that provides the desired stretch.

Of course during the execution we are not talking about the source code being transported back and forth. Once compiled this snippet of code will be available in a memory address and in fact it is this address that will be used to indicate the snippet. But this is implementation detail, you do not need to know this to use.

Even more so. The block has this address and a data structure that holds possible variables that can have their values stored there. Because these blocks are closures, they can contain the values of variables declared in the scope where the block was defined. So these values will be available when the block is executed effectively, even if the variable that gave rise to the value is no longer available.

So in the background what is called a code block is an object that contains values of enclosed variables and the address of a compiled code snippet.

These blocks can be considered unnamed methods. Since the method has no name, its execution is done through the variable where it is stored.

Then:

codigo = {|x| x + 1 }

This is a method that receives has a parameter called x´ e dentro dele ele pega o valor passado por x'and sum 1, this is the result of the code block.

Other syntax:

do |texto|
    puts texto
end

A practical example:

[1,2,3].each {|x| puts "(#{x})" }

This will print the 2 numbers in parentheses. Note that anyone who scans the data collection is the each method that you do not need to know how it works, it's an existing method that you just need to know that you can call to execute something on every element of any collection and that parameter this wait method is a block of code. This block will tell you what the method should perform on each element.

Of course you can get the same result in a different way, but you may need larger code, you may forget to handle all the necessary aspects manually, especially when you have to do more complicated things. Then you encapsulate a logic in a method and let it do something specific that will be defined by the block.

Another example:

def metodo(exec)
    exec.call "Vinicius"
end
codigo = proc do |nome|
    puts "Ola, #{nome}"
end
#ainda não executou nada aqui
metodo(codigo)
#agora executou a impressão do nome

See running on ideone .

Notice that the metodo method does not know what it will do, it only knows that it will do something to be defined by the received parameter. The setting was made outside of it, independently.

Now look at a cloistered variable:

def metodo(exec)
    exec.call "Vinicius"
end
idade = 20
codigo = proc do |nome|
    puts "Ola, #{nome}, voce tem #{idade} anos"
end
metodo(codigo)

See running on ideone .

The code is just an example. In this case the name will be set inside metodo but age is already going along with codigo . Then nome is block parameter, idade is enclosed variable.

A general explanation is this. Depending on your baggage of learning programming, it may not yet be so clear but you will be reading more about the same subject that you are consolidating and clarifying. Of course you can have specifics that you do not understand and can open up new questions.

    
11.04.2015 / 00:07
1

You can create blocks using keyword lambda .

diga_ola = lambda {|nome| puts "Olá, #{nome}!" }
diga_ola.call 'Fulano' # "Olá, Fulano!"

This allows you to pass a block to a method:

def executar(bloco)
  # faça algo
  bloco.call
  # faça mais algo
end

bloco = lambda { puts 'foo' }
executar bloco

But please do not use it that way in Ruby, as there is a syntax sugar that makes everything more elegant, using keyword yield .

def executar
  puts 'Antes do bloco'
  yield 'Fulano'
  puts 'Depois do bloco'
end

executar do |nome|
  puts "Olá, #{nome}!" # "Olá, Fulano!"
end

# ou
# executar {|nome| puts "Olá, #{nome}!" }

yield executes the block, being able to pass parameters to it. If you want to access the block as a variable, you can also add &block as the last parameter of the method:

def cumprimente(cumprimento, &block)
  nome = block.call
  puts "#{cumprimento}, #{nome}"
end

cumprimente 'Olá' do
  return 'Fulano'
end

block_given? will return if a block was passed as a parameter:

def do_something
  puts 'Hello'
  if block_given?
    yield
  end
  puts 'Bye
end

There is actually a difference between a lambda , a proc and a block , but this is complex and I will not try to explain here.

Ruby blocks are powerful, and various libraries and frameworks use it to create Domain Specific Languages (DSL). ( Ref1 , Ref2 ).

An example of this is Rails migrations:

create_table :pessoas do |t|
  t.string :nome, null: false
  t.integer :idade
  t.belongs_to :organizacao, foreign_key: true
end

The above execution is a normal method ( create_table is a method). But the syntax was so well thought out that it does not even look like Ruby, it sounds like a "language" to create migrations, almost poetry.

Blocks are also used to functionally program Ruby:

numeros = [1, 2, 3, 4, 5]
dobro = numeros.map {|n| n*2 } # [2, 4, 6, 8, 10]
    
13.04.2015 / 14:25