Machetazo Ruby
Machetazo Ruby
Machetazo Ruby
retorna false
Norita == Pepita
retorna false
Norita == Norita
retorna true
"hola" == "chau"
retorna false
***************************************************
Se le envia un mensaje:
Pepita.cantar!
=> "pri pri pri"
****************************************************
Mensajes definidos para Pepita
Pepita.energia
=> 100
Pepita.comer_lombriz!
=> nil
Pepita.volar_en_circulos!
=> nil
**************************************************
Como vimos, un objeto puede entender múltiples mensajes; a este conjunto de
mensajes que podemos enviarle lo denominamos interfaz. Por ejemplo, la interfaz de
Pepita es:
el objeto receptor es 2;
el mensaje es +;
el argumento es 3.
5.+ 6
Retorna 11
3.< 27
Retorna true
Pepita.== Norita
Retorna False
************************************************************
En un mundo de objetos, todo lo que tenemos son objetos y mensajes. A estos
últimos, podemos distinguirlos según la forma en que se escriben:
Mensajes de palabra clave. Su nombre está compuesto por una o varias palabras,
puede terminar con un signo de exclamación ! o de pregunta ?, y se envía mediante
un punto. Además,
argumentos
Pepita.comer_alpiste! 90
Pepita.volar_hacia! Iruya
Pepita.comer_alpiste! (Pepita.energia*0.1)
*************************************************************
Definicion de objeto
module Pepita
end
******************************************************************
module Pepita
def self.cantar!
end
end
Un método es, entonces, la descripción de qué hacer cuando se recibe un mensaje del
mismo nombre.
Todos los métodos comienzan con def y terminan con end. Si nos falta alguna de
estos dos la computadora no va a entender nuestra solución.
Todos los métodos que pertenezcan al mismo objeto van dentro del mismo module.
********************************************************************
Definiendo el efecto de los mensajes
module Pepita
@energia = 100
def self.volar_en_circulos!
@energia -= 10
end
end
*****************************************************************
Los metodos solo devuelven una cosa pero pueden tener varios efectos
def self.comprar_libro!
@plata -= 300
@libros += 1
end
********************************************************************
Los parametros pueden tener nombre propio
module Pepita
@energia = 100
@ciudad=Iruya
def self.volar_en_circulos!
@energia -= 10
end
def self.comer_lombriz!
@energia += 20
end
def self.volar_hacia! lugar
@energia -=100
@ciudad=lugar
end
# Seguí por acá...
end
***************************************************************
Los atributos no son mensajes
module Pepita
#...atributos y métodos anteriores...
def energia
@energia
end
end
******************************************************
estado_pepita = %w(
energia
ciudad
)
estado_kiano1100 = %w(
)
estado_rolamotoC115 = %w(
)
estado_enrique = %w(
celular
dinero_en_billetera
frase_favorita
)
****************************************************
Mensaje de valor absoluto
17.abs
=> 17
(-17).abs
=> 17
(1710 - 1040).abs
=> 670
(1040 - 1710).abs
=> 670
(1040 - 1710).abs / 2
=> 335
***************************************************************
usar metodos en otros metodos
module Obera
def self.kilometro
1040
end
end
module Iruya
def self.kilometro
1710
end
end
module BuenosAires
def self.kilometro
0
end
end
module Pepita
@energia = 1000
@ciudad = Obera
def self.energia
@energia
end
def self.ciudad
@ciudad
end
def self.cantar!
'pri pri pri'
end
def self.comer_lombriz!
@energia += 20
end
def self.volar_en_circulos!
@energia -= 10
end
def self.volar_hacia!(destino)
@energia -= (destino.kilometro - ciudad.kilometro).abs/2
@ciudad = destino
end
end
*********************************************************************
Self distancia
module Pepita
@energia = 1000
@ciudad = Obera
def self.energia
@energia
end
def self.ciudad
@ciudad
end
def self.cantar!
'pri pri pri'
end
def self.comer_lombriz!
@energia += 20
end
def self.volar_en_circulos!
@energia -= 10
end
def self.volar_hacia!(destino)
self.gastar_energia!(destino)
@ciudad = destino
end
def self.gastar_energia!(destino)
@energia -= (self.distancia_a (destino) )/ 2
end
def self.distancia_a (city)
(@ciudad.kilometro-city.kilometro).abs
end
end
***************************************************************
Comunicacion entre modulos
module Obera
def self.kilometro
1040
end
def self.distancia_a (city)
(self.kilometro-city.kilometro).abs
end
end
module Iruya
def self.kilometro
1710
end
def self.distancia_a (city)
(self.kilometro-city.kilometro).abs
end
end
module BuenosAires
def self.kilometro
0
end
def self.distancia_a (city)
(self.kilometro-city.kilometro).abs
end
end
module Pepita
@energia = 1000
@ciudad = Obera
def self.energia
@energia
end
def self.ciudad
@ciudad
end
def self.cantar!
'pri pri pri'
end
def self.comer_lombriz!
@energia += 20
end
def self.volar_en_circulos!
@energia -= 10
end
def self.volar_hacia!(destino)
self.gastar_energia!(destino)
@ciudad = destino
end
def self.gastar_energia!(destino)
@energia -= (@ciudad.distancia_a (destino) )/ 2
end
end
*****************************************************************
En Ruby, es una convención que los mensajes que devuelven booleanos (o sea,
verdadero o falso) terminen con un ?.
********************************************************************
module Jose
def self.acomodar_habitacion!
self.ordenar!
if self.tiene_sabanas_sucias?
self.cambiar_sabanas!
end
self.tender_la_cama!
end
end
***************************************************************
module Jardinero
def self.cuidar!(planta)
if planta.necesita_agua?
3.times { self.regar! planta }
else
self.sacar_bichos! planta
end
end
end
*************************************************************************
module Pepita
@energia = 1000
def self.energia
@energia
end
def self.volar_en_circulos!
@energia -= 10
end
def self.comer_alpiste!(gramos)
@energia += gramos * 15
end
def self.debil?
@energia<100
end
def self.feliz?
@energia>1000
end
def self.hacer_lo_que_quiera!
if self.debil?
self.comer_alpiste! 10
else
3.times {self.volar_en_circulos!}
end
end
end
*******************************************************************
NO olvidar las llaves
module Pepita
@energia = 1000
def self.energia
@energia
end
def self.volar_en_circulos!
@energia -= 10
end
def self.comer_alpiste!(gramos)
@energia += gramos * 15
end
def self.debil?
@energia<100
end
def self.feliz?
@energia>1000
end
def self.hacer_lo_que_quiera!
if self.debil?
self.comer_alpiste! 10
else
if self.feliz?
5.times {self.volar_en_circulos!}
else
3.times {self.volar_en_circulos!}
end
end
end
end
*************************************************************************
module Pepo
2
@energia=1000
3
def self.energia
4
@energia
5
end
6
def self.comer_alpiste!(gramos)
7
@energia+= (gramos/2)
8
end
9
def self.pesado?
10
@energia>1100
11
end
12
13
def self.volar_en_circulos!
14
if self.pesado?
15
@energia-=15
16
else
17
@energia-=5
18
end
19
end
20
def self.hacer_lo_que_quiera!
21
self.comer_alpiste! 120
22
end
23
end
************************************************************************
Enviar ordenes a otro modulo
module Pachorra
def self.entrenar_ave!
10.times {Pepita.volar_en_circulos!}
Pepita.comer_alpiste! 30
5.times {Pepita.volar_en_circulos!}
Pepita.hacer_lo_que_quiera!
end
end
*******************************************************************
Antes de empezar a entrenar, debe firmar un contrato con el ave. Esto, por ejemplo,
lo haríamos de la siguiente manera:
********************************************************************
usar nombre de parametros adentro del metodo
module Pachorra
10.times {@ave.volar_en_circulos!}
@ave.comer_alpiste! 30
5.times {@ave.volar_en_circulos!}
@ave.hacer_lo_que_quiera!
end
end
************************************************************
Para probar en consola solo debo llamar
Emilce.firmar_contrato! Pepo
Emilce.entrenar_ave!
************************************************
Según las rutinas que definen, cada entrenador/a solo puede trabajar con ciertas
aves:
En este caso le agregamos a Norita un mensaje que no hace nada, con el único
objetivo de que sea polimórfica con sus compañeras aves.
*****************************************************************
module Pachorra
def self.ave=(ave_nueva)
@ave = ave_nueva
end
def self.entrenar_ave!
10.times { @ave.volar_en_circulos! }
@ave.comer_alpiste! 30
5.times { @ave.volar_en_circulos! }
@ave.hacer_lo_que_quiera!
end
end
***********************************************************************
Setters y Getters
Como ya te habíamos contado en una lección anterior, a estos métodos que solo
sirven para acceder o modificar un atributo los llamamos métodos de acceso o
accessors. Repasando, los setters son aquellos métodos que establecen el valor del
atributo. Mientras que los getters son aquellos que devuelven el valor del
atributo.
Los setters deben llevar el mismo nombre del atributo al que están asociados,
agregando un = al final.
Los getters usan exactamente el mismo nombre que el atributo del cual devuelven el
valor pero sin el @.
Aquellos getters que devuelven el valor de un atributo booleano llevan ? al final.
*************************************************************************
module Inodoro
@cafeina_en_sangre = 90
def self.cafeina_en_sangre
@cafeina_en_sangre
end
def self.compinche=(nuevocompinche)
@compinche=nuevocompinche
end
def self.compinche
@compinche
end
end
module Eulogia
@enojada = false
def self.enojada?
@enojada
end
end
module Mendieta
@ganas_de_hablar = 5
def self.ganas_de_hablar
@ganas_de_hablar
end
def self.ganas_de_hablar=(nuevasganas)
@ganas_de_hablar=nuevasganas
end
end
*********************************************************************
En esta lección le dimos nombre al polimorfismo una idea con la que ya venías
trabajando, pero sobre la que todavía no habíamos reflexionado. Este principio
fundamental del paradigma de objetos nos permite que podamos interactuar de igual
manera con diferentes objetos, con el único requisito de que todos ellos entiendan
el o los mensajes que necesitamos enviarles.
saludo = "hola"
saludo.upcase
=> "HOLA"
************************************************************************
saludo = "hola"
...lo que estamos haciendo es crear una referencia saludo que apunta al objeto
"hola", que representamos mediante una flechita:
Y cuando tenemos...
saludo.upcase
despedida="adiós"
despedida.size()
tambien sirve
"adiós".size()
4.abs.even?
En ambos casos el resultado fue false, dado que aquellos strings son objetos
distintos, a pesar de que tengan los mismos caracteres. Cada vez que escribimos un
string estamos creando un nuevo objeto. Sin embargo:
otro_saludo.equal? otro_saludo
=> true
despedida.equal? otro_saludo
=> true
******************************************************
Ya entendimos que dos strings con el mismo contenido no necesariamente son el mismo
objeto. Pero esto puede ser poco práctico . ¿Cómo hacemos si realmente queremos
saber si dos objetos, pese a no ser el mismo, tienen el mismo estado?
**********************************************************************
"hola" == "hola"
=> true
"hola" == "adiós"
=> false
"hola".equal? "hola"
=> false
**********************************************************************
¡Ojo! A diferencia de la identidad, que todos los objetos la entienden sin tener
que hacer nada especial, la equivalencia es un poco más complicada.
*********************************************************************
**************************************************************
¡Exacto! Si bien:
**********************************************************
Fito.amigo = Juli
AbueloGervasio.nieto = Juli
3.times { AbueloGervasio.alimentar_nieto! }
Y esto tiene sentido: si un objeto muta su estado, y lo expone de una u otra forma
a través de mensajes, todos los que lo observen podrán ver el cambio.
****************************************************************
Ejemplo de relacion entre objetos y TADS
module Fideos
@ajies = 0
def self.ajies
@ajies
end
def self.agregar_ajies!(cantidad)
@ajies+=cantidad
end
def self.quitar_ajies! (cantidad)
@ajies-=cantidad
end
def self.descartar_la_salsa!
@ajies = 0
end
def self.picantes?
@ajies>2
end
end
module Jor
def self.plato_del_dia
@platodia
end
def self.plato_del_dia=(plato)
@platodia=plato
end
def self.picantear!
@platodia.agregar_ajies!(5)
end
end
module Luchi
def self.suavizar! (platodeldia,quitar)
if platodeldia.ajies>10
platodeldia.descartar_la_salsa!
else
platodeldia.quitar_ajies!(quitar)
end
end
end
*************************************************************
module CarlosDuty
@cantidad_logros=0
def self.dificultad
30-@cantidad_logros*0.5
end
def self.violento?
TRUE
end
def self.jugar!(un_tiempo)
if un_tiempo>2
@cantidad_logros+=1
end
end
end
module TimbaElLeon
@dificultad=25
def self.violento?
FALSE
end
def self.jugar!(un_tiempo)
@dificultad+=un_tiempo
end
def self.dificultad
@dificultad
end
end
module Metroide
@nivel_espacial=3
def self.violento?
if @nivel_espacial>5
TRUE
end
end
def self.jugar!(un_tiempo)
@nivel_espacial+=1
end
def self.dificultad
100
end
end
*****************************************************************
Veamos si se entiende: definí un objeto Juegoteca que tenga un atributo juegos con
su correspondiente getter. La Juegoteca tiene que tener en primer lugar el juego
CarlosDuty, luego TimbaElLeon y por último Metroide.
module Juegoteca
@juegos=[CarlosDuty,TimbaElLeon,Metroide]
def self.juegos
@juegos
end
end
*****************************************************************
Operaciones con listas push y delete
numeros_de_la_suerte = [6, 7, 42]
numeros_de_la_suerte.push 9
# Agrega el 9 a la lista...
numeros_de_la_suerte.delete 7
# ...y quita el 7.
************************************************************
También podemos saber saber si un elemento está en la colección usando include?:
numeros_de_la_suerte.include? 6
# Devuelve true, porque contiene al 6...
numeros_de_la_suerte.include? 8
# ...devuelve false, porque no contiene al 8.
*********************************************************************
Finalmente, podemos saber la cantidad de elementos que tiene enviando size:
numeros_de_la_suerte.size
# Devuelve 3, porque contiene al 6, 42 y 9
**************************************************************
numeros_de_la_suerte = [6, 7, 42]
numeros_de_la_suerte.first
# Nos retorna el primer elemento de la lista
numeros_de_la_suerte.last
# Nos retorna el último de la lista
numeros_de_la_suerte.index 7
# Nos retorna la posición de un elemento en la lista
*********************************************************************
Otro tipo muy común de colecciones son los sets (conjuntos), los cuales tienen
algunas diferencias con las listas:
***********************************************************
Con call se ejecuta, sin call queda sin ejecutarse
Los bloques son objetos que representan un mensaje o una secuencia de envíos de
mensajes, sin ejecutar, lista para ser evaluada cuando corresponda. La palabra con
la que se definen los bloques en Ruby es proc. Por ejemplo, en este caso le
asignamos un bloque a incrementador:
un_numero = 7
incrementador = proc { un_numero = un_numero + 1 }
Ahora avancemos un pasito: en este segundo ejemplo, al bloque { otro_numero =
otro_numero * 2 } le enviamos el mensaje call, que le indica que evalúe la
secuencia de envíos de mensajes dentro de él.
otro_numero = 5
duplicador = proc { otro_numero = otro_numero * 2 }.call
*************************************************************
Los bloques también pueden recibir argumentos para su aplicación. Por ejemplo,
sumar_a_otros_dos recibe dos argumentos, escritos entre barras verticales | y
separados por comas:
un_numero = 3
sumar_a_otros_dos = proc { |un_sumando, otro_sumando| un_numero = un_numero +
un_sumando + otro_sumando }
Para aplicar el bloque sumar_a_otros_dos, se le pasan los argumentos deseados al
mensaje call:
sumar_a_otros_dos.call(1,2)
=> 6
****************************************************************
jugar_a_timba = proc{| un_tiempo| TimbaElLeon.jugar!(un_tiempo/60) }
************************************************************
¿Qué pasa cuando queremos todos aquellos objetos que cumplan con una condición
determinada en una cierta colección? Por ejemplo, si de una lista de números
queremos los mayores a 3.
Lo que usamos es el mensaje select de las colecciones. select recibe un bloque con
un parámetro que representa un elemento de la colección y una condición booleana
como código, y lo que devuelve es una nueva colección con los elementos que la
cumplen.
algunos_numeros = [1, 2, 3, 4, 5]
mayores_a_3 = algunos_numeros.select { |un_numero| un_numero > 3 }
¿Y cuándo se aplica ese bloque que recibe el select? ¡El select es quien decide!
La colección va a aplicarlo con cada uno de los objetos (un_numero) cuando
corresponda durante el seleccionado (o filtrado) de elementos.
mayores_a_3
=> [4, 5]
****************************************************************
module Juegoteca
@juegos=[CarlosDuty,TimbaElLeon,Metroide]
def self.juegos
@juegos
end
def self.juegos_violentos
juegos_violentos=juegos.select{|juego| juego.violento?}
end
end
**********************************************************
¿Y si en vez de todos los elementos que cumplan una condición, sólo queremos uno?
¡Usamos find!
algunos_numeros = [1, 2, 3, 4, 5]
uno_mayor_a_3 = algunos_numeros.find { |un_numero| un_numero > 3 }
Mientras que select devuelve una colección, find devuelve únicamente un elemento.
uno_mayor_a_3
=> 4
¿Y si ningún elemento de la colección cumple la condición? Devuelve nil, que, como
aprendiste antes, es un objeto que representa la nada - o en este caso, que ninguno
cumple la condición.
***********************************************************
Es lo mismo repitiendo el nombre de la variable o no
module Juegoteca
@juegos=[CarlosDuty,TimbaElLeon,Metroide]
def self.juegos
@juegos
end
def self.juego_mas_dificil_que(una_dificultad)
juego_mas_dificil_que=@juegos.find{|juego|juego.dificultad>una_dificultad}
end
end
*******************************************************
Para saber si todos los elementos de una colección cumplen un cierto criterio
podemos usar el mensaje all?, que también recibe un bloque. Por ejemplo, si tenemos
una colección de estudiantes, podemos saber si todo el grupo aprueba de la
siguiente forma:
*********************************************************
El mensaje map nos permite, a partir de una colección, obtener otra colección con
cada uno de los resultados que retorna un envío de mensaje a cada elemento.
***************************************************************
Volviendo a nuestra colección de estudiantes. Ya preguntamos si todo el grupo
aprobó o si al menos alguien aprobó utilizando all?y any?. ¿Y si queremos saber
cuántos aprobaron? Usamos count:
Hasta ahora, todos los mensajes que vimos de colecciones (con la excepción de push
y delete) no están pensados para producir efectos sobre el sistema. ¿Qué ocurre,
entonces, cuando queremos hacer algo con cada elemento? A diferencia del map, no
nos interesan los resultados de enviar el mismo mensaje a cada objeto, sino
mandarle un mensaje a cada uno con la intención de producir un efecto.
Por ejemplo, si queremos que de una colección de golondrinas, aquellas con energía
mayor a 100 vuelen a Iruya, podríamos combinar select y each para hacer:
golondrinas
.select { |una_golondrina| una_golondrina.energia > 100 }
.each { |una_golondrina| una_golondrina.volar_hacia! Iruya }
**************************************************************
CLASES
class Zombi
def initialize
@salud = 100
end
def gritar
"¡agrrrg!"
end
def salud
@salud
end
def sabe_correr?
FALSE
end
def recibir_danio!(
puntos)
@salud=[(@salud-=(2*puntos)),0].max
end
def sin_vida?
@salud==0
end
end
*****************************************************************
Como habrás visto, definir una clase es muy similar a definir un objeto. Tiene
métodos, atributos... ¿cuál es su particularidad, entonces? La clase es un objeto
que nos sirve como molde para crear nuevos objetos.
¡Así es! Aprovechemos la clase Celular para instanciar los celulares de María y
Lucrecia:
celular_de_maría = Celular.new
celular_de_lucrecia = Celular.new
Celular, al igual que todas las clases, entiende el mensaje new, que crea una nueva
instancia de esa clase.
*****************************************************************
Como viste recién, la salud no se comparte entre bouba y kiki a pesar de que ambos
sean instancias de Zombi.
Pero nos quedó un método misterioso por aclarar: initialize. Al trabajar con clases
tenemos que inicializar los atributos en algún lugar. ¡Para eso es que existe ese
método!
****************************************************************
Veamos si se entiende: Agregale veinte nuevos zombis a la colección caminantes. ¡No
olvides que los números entienden el mensaje times!
class Sobreviviente
def initialize
@energia=1000
end
def energia
@energia
end
def atacar!(zombie,
puntos)
zombie.recibir_danio!(puntos)
end
def ataque_masivo!(caminantes)
caminantes.each{|caminante| atacar!(caminante,15)}
end
end
juliana = Sobreviviente.new
anastasia = Sobreviviente.new
caminantes = []
20.times{ caminantes.push (Zombi.new)}
*******************************************************
class Celular
def initialize
@bateria=100
end
def utilizar!(minutos)
@bateria=@bateria-(minutos)/2
end
def cargar_a_tope!
@bateria=100
end
end
*****************************************************
SuperClase
El símbolo < significa "hereda de": por ejemplo, Condor hereda de Ave, que está más
arriba en la jerarquía. Otra manera de decirlo es que cada Condor es una Ave.
La herencia nos permite que las subclases (Condor y Halcon) posean los mismos
métodos y atributos que la superclase Ave. Es decir, las instancias de Condor y de
Halcon van a saber volar! de la misma forma, pero cuando les enviemos el mensaje
dormir! cada una hará algo diferente.
***********************************************************
Sabiendo que contamos con las clases Celular y Notebook, ¿alguna vez instanciaremos
un objeto de la clase Dispositivo? ¡Probablemente no! ¿Por qué querríamos crear
algo tan genérico si podemos crear algo más específico?
A este tipo de clases, como Dispositivo o Ave en el ejemplo del ejercicio anterior,
se las llama clases abstractas porque, a diferencia de las clases concretas (como
Celular o Notebook), nunca las instanciamos. En otras palabras, no creamos objetos
con esa clase, solo nos sirven para proveer comportamiento a sus subclases.
***********************************************************
class MedioDeTransporte
def initialize(litros)
@combustible=(litros)
end
def cargar_combustible!(litros)
@combustible+=litros
end
def entran?(cant)
self.maximo_personas >= (cant)
end
end
class Moto<MedioDeTransporte
def recorrer!(km)
@combustible=@combustible-(km)
end
def maximo_personas
2
end
end
class Auto<MedioDeTransporte
def maximo_personas
5
end
def recorrer!(km)
@combustible=@combustible-(km)/2
end
end
***********************************************************
.