Operadores em Ruby e seus métodos
Leia em: 11 minutosRuby esta se tornando uma línguagem popularmente conhecida na web. Neste post tentarei mostrar a fundo o que são os operadores no Ruby, e por que eles são tão diferentes de qualquer coisa que já viu antes.
Operadores
O exemplo abaixo mostra dois operadores familiares.
1 + 2 # 3
person[:name] = "Joe"
Nas expressões acima denominamos dois operadores, um com o sinal de mais (operador aritmético) e outro com um sinal de igual (operador de atribuição). Essa maneira não é diferente do que você já esta acostumado a utilizar em outras liguagens como: Javascript, Java, C#. Mas no Ruby os operadores são realmente chamados de métodos.
Vamos tentar executar o seguinte comando no ruby:
1.+(2)
# 3
Na execução, o operador +
, fez uma chamada de método no objeto 1
, passando o objeto 2
de parâmetro.
name = 'Saulo'
name.+(' Santiago') # O retorno será "Saulo Santiago", mas a variável `name` continua armazenando 'Saulo'
name += " Santiago" # Agora a variável `name` armazena 'Saulo Santiago'
Ficou claro que podemos fazer concatenação com o método +
. Como bônus o Ruby define o operador +=
com base no operador +
(note que você não pode utilizar o +=
como um método), sendo assim conseguimos uma sintaxe mais enxuta.
Isso nos dá um poder incrível. Nós podemos personalizar o significado de adição, subtração e atribuir objetos em nossas classes personalizadas.
Vamos tomar um passo adiante.
Construindo nossos próprios métodos de operação
Vamos tentar estabelecer e criar um método.
Para este exemplo, vamos pensar em um objeto chamado Geladeira, que pode adicionar as coisas via o operador +
e retirar as coisas via o operador -
. Vamos iniciar nossa classe:
class Fridge
def initialize (beverages=[], foods=[])
@beverages = beverages
@foods = foods
end
def + (item)
end
def - (item)
end
end
Nossa função do initialize
é bem simples: nós tomamos dois parâmetros (ambos por padrão retornam matrizes vazias, se nada for enviado), que são atribuidos a variáveis de instância.
def + (item)
if item.is_a? Beverage
@beverages.push item
else
@foods.push item
end
end
Isso é muito simples. Cada objeto tem um método is_a?
que leva um único parâmetro: a classe. Se o objeto é uma instância dessa classe, ele irá retornar true
, caso contrário, retornará false
.
Assim, este diz que se o item que estamos adicionando na Geladeira é uma bebida, nós vamos adicioná-lo à matriz @beverages
. Caso contrário, nós vamos adicioná-lo à matriz @foods
.
Agora, que tal levarmos as coisas para fora da geladeira? Este método ficará um pouco mais complexo.
def - (item)
ret = @beverages.find do |beverage|
beverage.name.downcase == item.downcase
end
return @beverages.delete ret unless ret.nil?
ret = @foods.find do |food|
food.name.downcase == item.downcase
end
@foods.delete ret
end
No código acima recebemos uma string
de parâmetro, que representará o nome do objeto que queremos remover.
Usamos o método find
para interar sobre a matriz e buscar um ou mais objetos que tenham o mesmo valor que o parâmetro que foi enviado.
Se houver algum item que corresponde na matriz, ele será armazenadas no variável ret
, a seguir será removido com @beverages.delete ret
e retornado, caso contrário, ret
será nula.
Você deve estar pensando, por que usamos o return
no meio do método?
Se não estivessemos utilizando o return
o código continuaria sendo executado, e neste caso não queriamos este comportamento.
Então para parar a execução de um bloco de código em um determinado lugar no método que não seja a última instrução, colocamos um return
.
Caso nada seja retornado, isso significa que o item não foi encontrado no @beverages
. Portanto, nós assumimos que ele está em @foods
. Nós fizemos a mesma coisa para encontrar o item em @foods
e em seguida, devolvemos.
Note que nas duas verificações estamos convertendo as strings
todas para minúsculas, isso garante uma verificação mais segura.
Antes de testarmos isso, vamos precisar de duas classes: Food
e Beverage
:
class Beverage
attr_accessor :name
def initialize name
@name = name
@time = Time.now
end
end
class Food
attr_accessor :name
def initialize name
@name = name
@time = Time.now
end
end
Depois de ter criado as classes; vamos testa-ló no IRB
.
2.2.2 :001 > f = Fridge.new
=> #<Fridge:0x007fa9d305d140 @beverages=[], @foods=[]>
2.2.2 :002 > f + Beverage.new("water")
=> [#<Beverage:0x007fa9d3054158 @name="water", @time=2015-07-20 01:01:04 -0300>]
2.2.2 :003 > f + Beverage.new("beer")
=> [#<Beverage:0x007fa9d3054158 @name="water", @time=2015-07-20 01:01:04 -0300>, #<Beverage:0x007fa9d3046aa8 @name="beer", @time=2015-07-20 01:01:18 -0300>]
2.2.2 :004 > f + Food.new("bread")
=> [#<Food:0x007fa9d303e1c8 @name="bread", @time=2015-07-20 01:01:27 -0300>]
2.2.2 :005 > f + Food.new("eggs")
=> [#<Food:0x007fa9d303e1c8 @name="bread", @time=2015-07-20 01:01:27 -0300>, #<Food:0x007fa9d3034ba0 @name="eggs", @time=2015-07-20 01:01:30 -0300>]
2.2.2 :006 > f
=> #<Fridge:0x007fa9d305d140 @beverages=[#<Beverage:0x007fa9d3054158 @name="water", @time=2015-07-20 01:01:04 -0300>, #<Beverage:0x007fa9d3046aa8 @name="beer", @time=2015-07-20 01:01:18 -0300>], @foods=[#<Food:0x007fa9d303e1c8 @name="bread", @time=2015-07-20 01:01:27 -0300>, #<Food:0x007fa9d3034ba0 @name="eggs", @time=2015-07-20 01:01:30 -0300>]>
2.2.2 :007 > f - 'water'
=> #<Beverage:0x007fa9d3054158 @name="water", @time=2015-07-20 01:01:04 -0300>
2.2.2 :008 > f
=> #<Fridge:0x007fa9d305d140 @beverages=[#<Beverage:0x007fa9d3046aa8 @name="beer", @time=2015-07-20 01:01:18 -0300>], @foods=[#<Food:0x007fa9d303e1c8 @name="bread", @time=2015-07-20 01:01:27 -0300>, #<Food:0x007fa9d3034ba0 @name="eggs", @time=2015-07-20 01:01:30 -0300>]>
2.2.2 :053 >
Como você observou, criei o objeto Fridge
e adicionei nele várias classes de Beverage
e Food
, e no final retirei um objeto pelo nome (water
).
Pronto, conseguimos personalizar os operadores de adição e subtração para nossa classe Fridge
.
Operadores de Get e Set
Agora vamos escrever métodos para a definir e obter operadores utilizando os hashes
. Mas antes iremos analisarmos o seguinte trecho de código:
person = {}
person[:name] = "Saulo"
Mas uma vez que os operadores são métodos, podemos fazê-lo desta maneira:
person.[]=(:age, 24) # to set
person.[](:name) # to get
Tudo isto funciona perfeitamente pois estes são métodos normais, tendo apenas uma sintaxe especial.
Vamos fazermos uma classe chamada Club
. Nosso clube tem diferentes membros com diferentes funções. No entanto, a gente pode querer ter mais do que um membro com uma determinada função.
Então, o nosso exemplo irá ser composto por um hash
de membros e seus papéis. Se atribuirmos um segundo membro a uma função, em vez de substituir o primeiro, iremos adicioná-lo.
class Club
def initialize
@members = {}
end
def [] (role)
@members[role]
end
def []= (role, member)
end
end
O método responsável por trazer um membro da matriz é simples. Mas o método para adicionar um novo membro é mais complexo:
def []= (role, member)
if @members[role].nil?
@members[role] = member
elsif @members[role].is_a? String
@members[role] = [ @members[role], member ]
else
@members[role].push member
end
end
Se não houver membros com função definida, o membro é atribuido para uma nova função dentro do hash
members
.
Se a função esta definido como uma string
, é feito uma conversão em uma nova matriz com os membros originais e o novo membro.
Finalmente, se nenhuma dessas opções forem verdadeiras, significa que a função já existe na matriz, e por isso, o novo membro é apenas adicionado.
Podemos testar isto da seguinte maneira:
c = Club.new
c[:developer] = "Saulo"
c[:engineer] = "Foo"
c[:engineer] = "Bar"
c[:developer] # "Saulo"
c[:engingeer] # [ "Foo", "Bar" ]
Listando todos os Operadores
Assumindo que a variável a
seja 10
e a b
seja 20
. Segue a lista completa dos operadores:
Operadores aritiméticos
Operador | O que faz? | Exemplo | Método? |
---|---|---|---|
+ | Adiciona valores da esquerda com o da direita | a + b irá retornar 30 |
Sim |
- | Subtrai os valores da esquerda com o da direita | b - a irá retornar 10 |
Sim |
* | Multiplica os valores | a * b irá retornar 200 |
Sim |
/ | Divide o valor da esquerda com o da direita | b / a irá retornar 2 |
Sim |
% | Divide o valor da esquerda com o da direita e retorna a sobra da divisão | b % a irá retornar 0 |
Sim |
Operadores de comparações
Operador | O que faz? | Exemplo | Método? |
---|---|---|---|
== | Verifica se o valor dos dois operandos são iguais ou não, se sim, então a condição se torna verdadeira. | a == b não é verdadeiro |
Sim |
!= | Verifica se o valor dos dois operandos são diferentes ou não, se sim, então a condição se torna verdadeira. | a != b é verdadeiro |
Sim |
> | verifica se o valor da esquerda é maior que o da direita, se sim retorna verdadeiro | a > b não é verdadeiro |
Sim |
< | verifica se o valor da esquerda é menor que o da direita, se sim retorna verdadeiro | a > b é verdadeiro |
Sim |
>= | verifica se o valor da esquerda é maior ou igual ao da direita, se sim retorna verdadeiro | a >= b não é verdadeiro |
Sim |
<= | verifica se o valor da esquerda é menor ou igual ao da direita, se sim retorna verdadeiro | a <= b é verdadeiro |
Sim |
<=> | Operador de comparação combinada. Retorna 0 se primeiro operando é igual ao segundo, retorna 1 se o primeiro operando for maior que o segundo e retorna -1 se o primeiro operando for menor que o segundo. |
a <=> b retorna -1 |
Sim |
=== | Usado para testar a igualdade dentro de uma cláusula que contenha instruções. | (1...10) === 5 é verdadeiro |
Sim |
.eql? | Verdadeiro se o receptor e o argumento têm o mesmo tipo e valor. | 1 == 1.0 retorna verdadeiro, mas em 1.eql?(1.0) não é verdadeiro |
Sim |
.equal? | Verdadeiro se o receptor e o argumento tem a mesma identificação do objeto. | se foo_obj é duplicado para bar_obj então foo_obj == bar_obj é verdade, a.equal? bar_obj é falso, mas a.equal? foo_obj é verdade. |
Sim |
Operador de Set e Get
Assumimos que a variável y = {}
sempre esta inicializada com um hash
vazio.
Operador | O que faz? | Exemplo | Método? |
---|---|---|---|
[]= | Operador de Set | y[]=(:red, '#ff0000') irá atribuir um hash que contém a chave red com o valor #ff0000 |
Sim |
[] | Operador de Get | y[:red] irá retornar o valor da chave red |
Sim |
Operadores binários
Imagine que que x = 18
e y = 20
, em binário os valores são:
(x = 18).to_s(2) #=> "10010"
(y = 20).to_s(2) #=> "10100"
Operador | O que faz? | Exemplo | Método? |
---|---|---|---|
& | Operador AND | (x & y).to_s(2) irá retornar "10000" |
Sim |
pipe | Operador OR | (x pipe y).to_s(2) irá retornar "10110" |
Sim |
^ | Operador XOR | (x ^ y).to_s(2) irá retornar "110" (zeros à esquerda são omitidos) |
Sim |
~ | Operador NOT | (~x).to_s(2) irá retornar "-10011" |
Sim |
<< | Operador com desvio a esquerda | (x << 2).to_s(2) irá retornar "1001000" |
Sim |
>> | Operador com desvio a direita | (x >> 2).to_s(2) irá retornar "100" |
Sim |
Operadores lógicos
Operador | O que faz? | Exemplo | Método? |
---|---|---|---|
and ou && | Se ambas condições forem verdadeiras, então é retornado true |
(a && b) é verdadeiro |
Não |
or ou double pipe | Se algumas das condições forem diferente de zero, o retorno é true |
(a or b) é verdadeiro |
Não |
not ou ! | Inverte o estado da lógica do operando | !(a && b) é falso |
Não |
Operadores de atribuições
Operador | O que faz? | Exemplo | Método? |
---|---|---|---|
= | Operador de atribuição simples, atribui valores da direita para o lado esquerdo | c = a + b é atribuido o valor da soma de a + b em c |
Não |
+= | Adiciona e faz a atribuição | c += a é equivalente a c = c + a |
Não |
-= | Subtrai e faz a atribuição | c -= a é equivalente a c = c - a |
Não |
*= | Multiplica e faz a atribuição | c *= a é equivalente a c = c * a |
Não |
/= | Divide e faz a atribuição | c /= a é equivalente a c = c / a |
Não |
%= | Divide e faz a atribuição do resto | c %= a é equivalente a c = c % a |
Não |
**= | Exponencia e faz a atribuição | c **= a é equivalente a c = c ** a |
Não |
Operador ternário
Operador | O que faz? | Exemplo | Método? |
---|---|---|---|
? : | Expressão condicional | Se a condição é verdadeira? então o valor é x caso contrário o valor é y |
Não |
Operadores de alcance
Operador | O que faz? | Exemplo | Método? |
---|---|---|---|
.. | Cria um alcance do ponto de partida até o ponto final inclusivo | 1..10 cria um intervalo de 1 a 10 |
Não |
... | Cria um alcance do ponto de partida até o ponto final exclusivo | 1..10 cria um intervalo de 1 a 9 |
Não |
Nota: Operadores com um Sim na coluna Método?
, na verdade, são métodos que podem serem subscritos.
Comentários:
Deixe sua dúvida, sugestão ou crítica, estou ansioso para saber tudo o que você achou sobre este post: