You are on page 1of 196

Capítulo 1: Fundamentos

1. Primeros Programas
Ejercicio 5: Que siga el movimiento
Todo programa tiene exactamente un program: una sección del código que declara los
comandos (acciones) que queremos que la máquina realice sobre el tablero inicial.
Al ejecutar un programa obtendremos un tablero final.
La sintaxis de un program es bastante simple:
1. escribimos una línea (renglón) que diga program, seguido de una llave de
apertura: {
2. a continuación, los comandos: uno por línea
3. y finalmente, una última llave que cierra la que abrimos anteriormente }
Vamos a ver algunos ejemplos de programs:
 uno que no hace nada

program {
}

 uno que mueve el cabezal una posición hacia el norte

program {
Mover(Norte)
}

 uno que mueve el cabezal dos posiciones hacia el norte

program {
Mover(Norte)
Mover(Norte) }

Creá un programa que en un tablero de 2x4 con el cabezal en el origen (la celda de abajo a la
izquierda), mueva el cabezal tres veces hacia el norte:
program {Mover(Norte)Mover(Norte)Mover(Norte)}
Los lenguajes de programación son creados con algunas palabras que solo se pueden
utilizar con un fin determinado. Se las llama palabras reservadas . En Gobstones, el
lenguaje que estamos utilizando, program es una palabra reservada.
Como ya sabemos que nuestros programas son ejecutados por la máquina, de ahora en
más diremos "creá un programa que haga ..." en vez de "creá un programa que provoque
que la máquina haga ...".
Ejercicio 6: Para todos lados
Como te imaginarás, el cabezal no sólo se puede mover hacia el Norte, y un programa
puede combinar cualquier tipo de movimientos.
Creá un programa que mueva el cabezal dos posiciones hacia el Este y una hacia el Sur,
produciendo el siguiente efecto:
program {Mover(Este) Mover(Este)Mover(Sur)}
Notá que estos dos programas hacen lo mismo:

program {
Mover(Este)
Mover(Este)
Mover(Sur) }

program {
Mover(Este)
Mover(Sur)
Mover(Este) }

Ejercicio 7: El orden de las cosas


Cuando trabajamos en Gobstones, hacemos las cosas en un cierto orden. Por ejemplo, si tenemos
este programa:

program {
Mover(Norte)
Mover(Este) }

una forma posible de leerlo (llamada operacional) es como lo haría una máquina, en orden, de
arriba hacia abajo:
1. primero se mueve al norte: Mover(Norte)
2. luego se mueve al este: Mover(Este)
Veamos si entendiste esto: creá otro programa que haga lo mismo que el de arriba (mover hacia el
noreste), pero de manera distinta. Ojo: tiene que funcionar en un tablero de 2x2.
Recordá que no existe la dirección Noreste, pero sí Norte y Este.
Probá invertir el orden de los comandos. Si bien la máquina lo ejecutará en un orden
diferente, el resultado final será el mismo. 
program {Mover(Este)Mover(Norte)}
Ejercicio 8: Sí, esto también se puede romper
Si bien nunca vamos a querer que nuestro programa se rompa, es algo que
definitivamente te va a pasar muchas veces
: partiendo del tablero que te mostramos acá abajo, creá un programa que provoque que el
cabezal se salga fuera de los límites.
program {Mover(Este)Mover(Este)Mover(Este)}
¡Muy bien! Tu solución pasó todas las pruebas
¡BOOM!
EXPLOSIÓN

[1:31]: No se puede mover hacia la dirección Este: cae afuera del tablero.

¡BOOOOOOOOOOOOOOOOOM!   
Ey, ¿qué pasó?
Tu programa falló, se rompió, o como lo llamamos en el universo Gobstones: hizo BOOM.
Y, ¿qué significa esto?
Que los comandos que le diste a la computadora no se pueden ejecutar, y hay algo que vas a tener
que cambiar para que funcione. En este ejercicio eso no tiene sentido porque lo hicimos a
propósito, pero tenelo en cuenta para cuando falles en el futuro.

Ejercicio 9: Nuestras primeras bolitas


En cualquier celda de nuestro tablero podemos poner bolitas. Las hay de distintos colores:
rojas (Rojo);
azules (Azul);
negras (Negro);
y verdes (Verde).
Por ejemplo, este es un tablero con una bolita roja y una negra:
Además de moverse, el cabezal también puede poner bolitas en la celda actual. Para eso
contamos con la operación Poner, que le dice al cabezal que deposite una bolita del color dado:

program { Poner(Rojo) }

¡Felicitaciones! Acabás de escribir un programa que pone una bolita roja en la celda actual.

Ejercicio 10: Más y más bolitas


Algo interesante de nuestros tableros es que en sus celdas podemos poner cualquier
cantidad de bolitas de cualquier color.
si tenemos este tablero:
y ejecutamos el siguiente programa:

program {
Poner(Rojo)
Poner(Rojo)
Poner(Azul)
Poner(Verde)
Poner(Rojo) }

el cabezal colocará en la celda actual tres bolitas rojas, una azul y una verde.
Notá que en este problema, si cambiamos el orden en que llamamos (usamos a) Poner, el
resultado no cambia: siempre nos terminará quedando un tablero con tres bolitas rojas, una azul y
una verde.

program {
Poner(Rojo)
Poner(Rojo)
Poner(Rojo)
Poner(Verde)
Poner(Azul) }
program {
Poner(Rojo)
Poner(Azul)
Poner(Rojo)
Poner(Verde)
Poner(Rojo) }

Ejercicio 11: Poné tus primeras bolitas


podemos poner en una celda tantas bolitas como queramos: 2, 4, 12, 50, 1000. ¡No hay
ningún límite!
ahora te toca a vos: creá un programa que ponga cuatro bolitas rojas y tres bolitas negras en la
celda actual.
program{Poner(Rojo)Poner(Rojo)Poner
(Rojo)Poner(Rojo)Poner(Negro)Poner(Negro)Poner(Negro)}

Ejercicio 12: Sacar Bolitas


De la misma forma que hay un "poner bolita" (Poner), tenemos un "sacar bolita" (Sacar),
que quita exactamente una bolita del color dado.
Por ejemplo, el siguiente programa saca dos bolitas de la posición inicial.

program { Sacar(Rojo) Sacar(Rojo)}

Sabiendo esto, creá un programa que elimine sólo la bolita roja de este


tablero. ¡Tené cuidado! Prestá atención a la posición del cabezal .
program {Mover (Sur) Sacar(Rojo)}
¡Muy bien! Tu solución pasó todas las pruebas
¿Y si no hubiera ninguna bolita para sacar?
Ejercicio 13: Cuando no hay bolitas
Cada vez que usamos Sacar, tenemos que tener más cuidado que con Poner, porque...
¿Querés saber por qué? Intentá sacar una bolita verde o azul de este tablero y descubrilo.
 ¡Muy bien! Tu solución pasó todas las pruebas

program{Sacar(Verde)}
¡BOOM!
DESASTRE
[1:9]: No se puede sacar una bolita de color Verde: no hay bolitas de ese color.
Claro, otra vez BOOM.
Esta vez lo que pasó fue que el cabezal intentó sacar una bolita de un color que no
había, y como no sabía qué hacer se autodestruyó.

Ejercicio 14: Sacar bolitas


Un último esfuercito: usando Sacar, creá un programa que elimine todas las bolitas de
este tablero:
¡Muy bien! Tu solución pasó todas las pruebas
program{Sacar(Verde)Sacar(Azul)Sacar(Rojo)Sacar(Negro)}

Programas
¡Felicitaciones, terminaste esta guía! Eso significa que ya sabés de bolitas, tableros y
cabezales, y que podés controlar a éste último con los comandos
 Poner
 Sacar
 Mover
Y además, sabés como definir un programa (program)
 Práctica Primeros Programas Ejercicio 1: Calentando motores
El siguiente programa coloca una bolita roja en la posición inicial y una negra al
este.

program { Poner(Rojo) Mover(Este) Poner(Negro)}

con el cabezal inicialmente en el origen.

program{Poner(Rojo)Mover(Este)Poner(Negro)}

program {
Poner(Rojo)
Mover(Este)
Poner(Negro) }

Operacionalmente:
1. pone una roja
2. luego se mueve al este
3. luego pone una negra
Es decir: pone una roja en la posicion inicial, y una negra al este
Y ahora mirá este otro:

program { Mover(Este) Poner(Rojo) Poner(Negro)}

Operacionalmente:
1. se mueve al este
2. luego pone una roja
3. luego pone una negra
Es decir: pone una roja y una negra al este de la posición inicial.
Moraleja: ¡no hacen lo mismo! Cambiar el orden nos cambió el qué.

Ejercicio 2: Combinando comandos


Creá un programa que ponga dos bolitas en la posición inicial, y otras dos en la celda de al lado
hacia el Este. Todas las bolitas deben ser rojas.
Acá te dejamos un ejemplo de cómo debería quedar el tablero:
program {
Poner(Rojo)
Poner(Rojo)
Mover(Este)
Poner(Rojo)
Poner(Rojo) }
 ¡Muy bien! Tu solución pasó todas las pruebas
Ejercicio 3: La fila roja
Creá un programa que a partir de un tablero vacío con el cabezal en el origen, dibuje una linea de
cuatro celdas hacia el Este. Las bolitas deben ser rojas y debe poner una bolita por celda.
Ademas, el cabezal debe quedar en el extremo final de la línea, como se ve en la imagen:
Program {
Poner(Rojo)
Mover(Este)
Poner(Rojo)
Mover(Este)
Poner(Rojo)
Mover(Este)
Poner(Rojo) }

Ejercicio 4: Una escalerita


Usando las herramientas que ya conocés, creá un programa que dibuje una escalera azul como la
que se ve en la imagen. El cabezal empieza en el origen (o sea, en el borde Sur-Oeste) y debe
quedar en el extremo inferior derecho de la escalera.
Mirá la imagen:
program{
Poner(Azul)
Mover(Norte)
Poner(Azul)
Mover(Norte)
Poner(Azul)
Mover(Sur)
Mover(Este)
Poner(Azul)
Mover(Sur)
Poner(Azul)
Mover(Este)
Poner(Azul) }
Ejercicio 5: Portugal
Creá un programa que dibuje una bandera portuguesa.
La bandera de Portugal se ve así:
Como no nos vamos a poner tan quisquillosos, te vamos a pedir una versión simplificada, que se
tiene que ver así: (tablero final) Ah, el cabezal empieza en el origen.

Ejercicio 6: Y ahora una de más cerquita


¿Te animás a dibujar la de Argentina?
Aunque como en Gobstones no hay amarillo, nos vamos a tomar el atrevimiento de
cambiarlo por rojo (perdón Belgrano, no nos queda otra 
Con el cabezal en el origen, tu tarea es dibujar esta pseudo-bandera argentina:
program{Poner (Azul) {
Mover(Norte)
Mover(Norte)
Poner(Azul)
Mover(Este)
Poner(Azul)
Mover(Sur)
Mover(Sur)
Poner(Azul)
Mover(Este)
Poner(Azul)
Mover(Norte)
Poner(Rojo)
Mover(Norte)
Poner(Azul)
Mover(Este)
Poner(Azul)
Mover(Sur)
Mover(Sur)
Poner(Azul)
Mover(Este)
Poner(Azul)
Mover(Norte)
Mover(Norte)
Poner(Azul) }
una particularidad de nuestra bandera es su simetría, la franja de arriba es exactamente
igual a la de abajo. Si pudieramos crear un comando que dibuje la franja celeste nuestro
programa quedaría mucho más simple...

Ejercicio 7: Limpiando el jardín


¡Necesitamos podar este jardín!
Con el cabezal en el origen, creá un programa que se encargue de "podar" el tablero de la
imagen: sacar todas las bolitas verdes. Al finalizar, el cabezal debe terminar donde
empezó.
program {
Sacar(Verde)
Mover(Norte)
Sacar(Verde)
Mover(Norte)
Sacar(Verde)
Mover(Este)
Sacar(Verde)
Mover(Este)
Sacar(Verde)
Mover(Sur)
Sacar(Verde)
Mover(Sur)
Sacar(Verde)
Mover(Oeste)
Sacar(Verde)
Mover(Oeste) }

Ejercicio 8: Reemplazar bolitas


¿Ya te estás durmiendo? 
Pasemos a algo un poco más difícil entonces. Te vamos a dar un tablero de 2x2 (o sea, con
4 celdas) donde cada una de ellas tiene una bolita roja.
Tu tarea es crear un programa que reemplace todas las bolitas rojas por verdes.
 ¡Dame una pista!
La idea de reemplazar una bolita por otra es algo que nosotros, los humanos,
podemos entender.
Por el contrario, Gobstones sólo entiende de Poner y Sacar. Pensá cómo podés
combinar esas operaciones para lograr el efecto deseado.
program{
Sacar(Rojo)
Poner(Verde)
Mover(Este)
Sacar(Rojo)
Poner(Verde)
Mover(Norte)
Sacar(Rojo)
Poner(Verde)
Mover(Oeste)
Sacar(Rojo)
Poner(Verde)}
¡Muy bien! Tu solución pasó todas las pruebas

Procedimientos
Si llegaste hasta acá, entonces ya sabés:
 cómo escribir un programa (program);
 cómo poner y sacar bolitas, usando Poner y Sacar;
 cómo desplazar el cabezal por el tablero, usando Mover.
Como te imaginarás, con estas 3 cosas ya podés resolver muchos problemas: dibujar banderas de
países , escribir tu nombre , hacer un plano de tu casa , etc, etc. Pero más temprano que tarde
esas herramientas no te van a alcanzar, y tu programa se va a volver tan complejo que no va a ser
fácil entenderlo. 

Ejercicio 1: ¿De qué se trata?


Tomate unos minutos (no más de 3 ) para tratar de descubrir qué es lo que hace el
programa a continuación.

program {
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Norte)
Poner(Negro)
Mover(Oeste)
Poner(Negro)
Mover(Oeste)
Poner(Negro)
Mover(Norte)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro) }

¿Lo pensaste? Decinos qué es lo que hace 

Llena cualquier tablero de bolitas negras

Dibuja una línea de bolitas negras

Dibuja una cruz de bolitas negras

Dibuja un cuadrado de bolitas negras

Pone 9 bolitas negras en una celda


 ¡La respuesta es correcta!

Ejercicio 2: Un programa un poco largo


Ahora tenés la posibilidad de ver en acción el programa.

program {
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Norte)
Poner(Negro)
Mover(Oeste)
Poner(Negro)
Mover(Oeste)
Poner(Negro)
Mover(Norte)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro) }

¡Muy bien!
Aunque ahora pudimos probarlo, sigue siendo un poco confuso saber de qué se trata el
programa con solo leerlo ¿no sería mejor si también fuera fácil de entender para un
humano?

Ejercicio 3: Las cosas por su nombre


Mirá esta nueva versión del mismo programa. Aunque todavía hay elementos de
la sintaxis que no conocés, confiamos en que vas a tardar mucho menos en descubrir qué
hace. Enviá el código, así nos aseguramos de que hace exactamente lo mismo que el
anterior.
procedure DibujarCuadradoNegroDeLado3() {
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Norte)
Poner(Negro)
Mover(Oeste)
Poner(Negro)
Mover(Oeste)
Poner(Negro)
Mover(Norte)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro)
}
program {
DibujarCuadradoNegroDeLado3() }
¡Muy bien! Tu solución pasó todas las pruebas
Mucho más fácil de entender, ¿no? 
Probablemente te estés preguntando ¿cómo supo la computadora lo que tenía que
hacer DibujarCuadradoNegroDeLado3 ? ¿Qué es eso de procedure? 
¡Vamos a averiguarlo!

Ejercicio 4: Enseñándole tareas a la computadora


Como viste en el ejemplo del cuadrado, se puede empezar a diferenciar dos tipos de comandos
dentro de un programa:
 los que vienen definidos por el lenguaje y nos sirven para expresar operaciones básicas,
como Mover, Poner y Sacar. A estos los llamaremos comandos primitivos, o
simplemente primitivas;
 y los que definimos nosotros, que nos sirven para expresar tareas más complejas. Como el
nombre de esta lección sugiere, estos son los procedimientos.
Cuando definimos un procedimiento estamos "enseñándole" a la computadora  a realizar una
tarea nueva, que originalmente no estaba incluida en el lenguaje.
 Prestale atención a la sintaxis del ejemplo para ver bien cómo definimos un procedimiento y
cómo lo invocamos en un program.

procedure Poner3Rojas() {
Poner(Rojo)
Poner(Rojo)
Poner(Rojo)
}
program { Poner3Rojas() }

¿Qué te parece que hace el nuevo procedimiento?  Copiá y enviá el código para ver qué pasa.
procedure Poner3Rojas() {
Poner(Rojo)
Poner(Rojo)
Poner(Rojo) }
program {
Poner3Rojas() }
¡Muy bien! Tu solución pasó todas las pruebas
Ahora que ya probamos cómo funcionan, podemos ver las diferencias entre las sintaxis
de programas y procedimientos.
El procedimiento se define con la palabra procedure seguida por un nombre y
paréntesis ( ). Luego escribimos entre llaves { } todas las acciones que incluya. Para ver un
procedimiento en acción hay que invocarlo dentro de un programa, sino sólo será una
descripción que nunca se va a ejecutar. 
El programa se crea con la palabra program seguida de llaves { }, y adentro de ellas lo que
queremos que haga la computadora. ¡No lleva nombre ni paréntesis!

Ejercicio 5: Procedimientos en acción


Si bien las palabras que utilizamos para crear programas (program) y definir
procedimientos (procedure) se escriben parecido son cosas muy distintas.
Cuando creamos un programa nuevo le estamos diciendo a la computadora lo que
queremos que suceda luego de tocar Enviar.
Pero si queremos crear una tarea nueva podemos agrupar las acciones que requiere en un
procedimiento. Los procedimientos se definen con un nombre que describa lo que hace.
Veamos cómo creamos un nuevo procedimiento llamado PonerVerdeYAzul. 

procedure PonerVerdeYAzul() {
Poner(Verde)
Poner(Azul) }

La computadora solo va a seguir las instrucciones dentro de un procedimiento cuando


sea invocado dentro de un program. ¿Cómo lo invocamos? Escribimos su nombre seguido
por paréntesis ( ).

program {
PonerVerdeYAzul() }

Completá el código para que además de definir el procedimiento PonerNegroYRojo luego


lo invoque en el program.
procedure PonerNegroYRojo( ) {
Poner(Negro)
Poner(Rojo) }
program {
PonerNegroYRojo ( ) }
¡Muy bien! Tu solución pasó todas las pruebas
A la hora de definir e invocar procedimientos tenemos que prestar mucha atención a la
sintaxis para no perder de vista el objetivo del problema por un error de escritura.

Ejercicio 6: Escribiendo procedimientos


Llegó el momento de programar desde cero. ¡No te preocupes! Como recién estamos
empezando, repasemos lo que aprendimos.
A esta altura ya sabemos que para programar siempre tenemos que tener en cuenta la
sintaxis y que para definir nuevos procedimientos también tenemos reglas:
 empezamos con la palabra reservada procedure;
 elegimos un nombre que lo describa y lo escribimos con mayúscula seguido de paréntesis (
);
 encerramos las acciones que queremos que haga entre llaves { }.
Entonces, un procedimiento que se mueve cuatro celdas al Norte se va a definir así:

procedure Mover4AlNorte() {
Mover(Norte)
Mover(Norte)
Mover(Norte)
Mover(Norte) }

Y si lo queremos utilizar, tenemos que invocarlo dentro del program escribiendo su


nombre tal cual y sin olvidar los paréntesis( ) ¡Prestá atención a la sintaxis!

program {
Mover4AlNorte( ) }

Definí un procedimiento Poner3Verdes que ponga 3 bolitas verdes en la celda actual


e invocalo en el program.
procedure Poner3Verdes( ){
Poner(Verde)
Poner(Verde)
Poner(Verde) }
program{
Poner3Verdes( ) }
¡Muy bien! Tu solución pasó todas las pruebas
Resumiendo, en lo que a un procedimiento respecta, se pueden distinguir dos momentos:
 la definición, que es cuando ponemos procedure Poner3Verdes() y el bloque de
código que especifica qué hace.
 el uso o invocación, que es cuando escribimos Poner3Verdes() en alguna parte
de program (o de otro procedimiento).
Ejercicio 7: Procedimiento, ¡te invoco!
Algo MUY importante es que un procedimiento se define una sola vez y luego se puede
usar todas las veces que necesitemos, como cualquier otro comando.
Por eso, su nombre debe ser único dentro de todo el programa. Recordá que la
computadora no hace más que seguir órdenes y si existiera más de un procedimiento con
el mismo nombre, no sabría cuál elegir. 
Para facilitarte las cosas, a veces te ofreceremos partes del problema resuelto, para que
sólo tengas que enfocarte en lo que falta resolver. ¿Y dónde están? Mirá el editor y fijate
si encontrás la pestaña Biblioteca . Lo que aparece en la Biblioteca no hace falta que lo
escribas en el código, ¡si está ahí podés invocarlo directamente! 
¡Vamos a probarlo! Queremos poner 3 bolitas verdes en dos celdas consecutivas como
muestra el tablero:
Creá un programa que lo haga invocando el procedimiento Poner3Verdes. Recordá que
ya te lo damos definido ¡no tenés que volver a escribirlo !
 Biblioteca

procedure Poner3Verdes() {
Poner(Verde)
Poner(Verde)
Poner(Verde) }

SOLUCION CORRECTA
program{
Poner3Verdes()
Mover(Este)
Poner3Verdes() }
Tablero Final logrado Bien con la casilla donde queda el cursor en amarillo
Repasemos:
 Para definir un procedimiento nuevo usamos procedure y le ponemos un nombre
que describa lo que hace seguido por paréntesis ( ). A continuación y entre
llaves { } lo que querramos que haga.
 Los procedimientos se definen una sola vez. Si ya están definidos en la Biblioteca o
en alguna parte del código no hay que volver a hacerlo (no se pueden repetir los
nombres,  si se define un procedimiento más de una vez nuestro programa va a
fallar).
 Para invocar un procedimiento escribimos su nombre (sin olvidar los
paréntesis( ) al final). ¡Y podemos hacerlo todas las veces que sean necesarias! 
 Aunque también se escribe entre llaves { }, el program nunca lleva nombre ni
paréntesis.

Ejercicio 8: Una definición, "infinitos" usos


Otro procedimiento que ya usamos antes es Poner3Rojas, que pone tres bolitas
rojas en una celda. Te ahorramos el trabajo de escribirlo, si lo buscás vas a ver que
está definido en la Biblioteca. ¡Ahora podés utilizarlo tantas veces como quieras!
Creá un programa que ponga 9 bolitas rojas en la celda actual invocando el
procedimiento Poner3Rojas todas las veces que sea necesario.
Biblioteca

procedure Poner3Rojas() {
Poner(Rojo)
Poner(Rojo)
Poner(Rojo) }

Solución Valida del programa


program{
Poner3Rojas()
Poner3Rojas()
Poner3Rojas() }
¡Muy bien! Tu solución pasó todas las pruebas
No te olvides de revisar las herramientas que nos ofrece la Biblioteca para saber cuáles
podemos aprovechar cuando resolvamos nuestro problema.
Ejercicio 9: Procedimientos dentro de otros
Cuando creamos procedimientos agrupamos varias acciones en una tarea que podemos
reconocer y nombrar. Eso hace nuestros programas más claros, legibles y nos ahorra
repeticiones innecesarias.
Ya vimos que un procedimiento puede ser invocado tantas veces como querramos dentro
de un programa , pero como su objetivo es agrupar los pasos de una tarea para usarla
cuando haga falta, también lo podemos invocar dentro de otros procedimientos. ¡Vamos a
probarlo!
Definí el procedimiento Poner9Rojas que, utilizando Poner3Rojas, ponga nueve bolitas rojas en
una celda. Una vez definido, invocá el nuevo procedimiento en un program.
 ¡Dame una pista!
No tenés que volver a definir Poner3Rojas(), recordá que si un procedimiento está en la
Biblioteca y lo querés utilizar solamente tenés que invocarlo.
Biblioteca
procedure Poner3Rojas() {
Poner(Rojo)
Poner(Rojo)
Poner(Rojo) }

Solución Final Resuelta


procedure Poner9Rojas(){
Poner3Rojas()
Poner3Rojas()
Poner3Rojas() }
program{
Poner9Rojas() }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
Bueno, ya sabemos cómo crear procedimientos, pero ¿por qué querríamos hacerlos? 
Algunas posibles respuestas:
 para simplificar código, escribiendo una sola vez y en un solo lugar cosas que vamos a
hacer muchas veces;
 para escribir menos, por qué querríamos hacer cosas de más; 
 para que el propósito de nuestro programa sea más entendible para los humanos, como
vimos en el ejemplo de DibujarCuadradoNegroDeLado3. Para esto es fundamental pensar
buenos nombres, que no sean muy largos
(DibujarCuadradoNegroDeLado3FormadoPor9BolistasDeArribaAAbajo), ni demasiado
cortos (DibCuaNeg), y sobre todo que dejen en claro qué hace nuestro procedimiento;
 para comunicar la estrategia que pensamos para resolver nuestro problema;
 y como consecuencia de todo esto: para poder escribir programas más poderosos.

Ejercicio 10: Dibujamos con imaginación


Vamos a usar un poco la imaginación y vamos a hacer un procedimiento que dibuje una
"punta" negra en la esquina inferior izquierda de esta forma:
procedure DibujarPuntaNegra( ){
  Poner(Negro) }
program{
  DibujarPuntaNegra( )
  Mover(Norte)
  DibujarPuntaNegra( )
  Mover(Sur)
  Mover(Este)
  DibujarPuntaNegra( ) }
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 11: De punta a punta


En el ejercicio anterior ya dibujamos una punta, ahora vamos a pensar cómo aprovechar el
procedimiento que hicimos para crear un tablero como este:
Definí el procedimiento DibujarDosPuntas e invocalo dentro un program. Acordate de
utilizar DibujarPuntaNegra.
Biblioteca
procedure DibujarPuntaNegra() {
Poner(Negro)
Mover(Norte)
Poner(Negro)
Mover(Sur)
Mover(Este)
Poner(Negro) }

SOLUCIÓN
procedure DibujarDosPuntas() {
DibujarPuntaNegra()
Mover(Norte)
Mover(Norte)
Mover(Este)
DibujarPuntaNegra() }
program{ DibujarDosPuntas( ) }
¡Muy bien! Tu solución pasó todas las pruebas
Para resolver este problema lo que hicimos fue separarlo en partes, identificando las tareas
más pequeñas que ya teníamos resueltas. 
Los procedimientos son muy útiles para esto, se ocupan de resolver una subtarea y nos
permiten repetirla o combinarla para solucionar un problema mayor que la incluya.
Ejercicio 12: Rojo al borde
Ya vimos que los comandos que vienen definidos por el lenguaje se llaman primitivas. Hay
una primitiva que no usaste hasta ahora que queremos presentarte.
Imaginate que no sabés ni dónde está el cabezal ni qué tamaño tiene el tablero pero
querés llegar a una esquina: La primitiva Mover no te va a ser de mucha ayuda. 
Por suerte  existe una primitiva  llamada IrAlBorde, que toma una dirección, y se mueve
todo lo que pueda en esa dirección, hasta llegar al borde.
¿Cómo? Mirá el resultado del siguiente programa:
program {
IrAlBorde(Este) }

¡Vamos a aprovecharlo!
Definí el procedimiento RojoAlBorde que ponga una bolita roja en la esquina superior izquierda
del tablero e invocalo en el program.
 ¡Dame una pista!
En este caso tenés que lograr que el cabezal quede en una esquina, por lo tanto tenés que
pensar en dos bordes: Norte y Oeste. 
La solución perfecta del ejercicio es:
procedure RojoAlBorde() {
IrAlBorde(Oeste)
IrAlBorde(Norte)
Poner(Rojo) }
program {
RojoAlBorde() }
¡Muy bien! Tu solución pasó todas las pruebas
¡Excelente! 
IrAlBorde es una primitiva muy útil para cuando no conocemos las condiciones de nuestro
tablero. 

Ejercicio 13: Adornando el tablero


Para resolver un problema nos conviene comprender bien de qué se trata para elegir una
estrategia. Es el momento de empezar a hacerlo aprovechando los procedimientos.
Uno de los objetivos al usar procedimientos es identificar y nombrar las subtareas  que
conforman un problema y combinar sus soluciones para poder resolverlo. Veamos un
ejemplo:
Queremos decorar con guirnaldas las dos esquinas superiores de cualquier tablero como
muestra la imagen.
Pensemos una estrategia distinguiendo subtareas:
Cada guirnalda se compone de 3 bolitas rojas y 3 bolitas verdes. Ya resolvimos cómo
hacerlo en otros ejercicios , hacer una guirnalda solo requerirá combinar esas soluciones.
Y ponerla donde corresponda, claro.
¿Y que más? el procedimiento que decore el tablero debería poder aprovechar la creación
de una guirnalda para usarla varias veces en las posiciones que querramos decorar. Nos
vendría muy bien alguna primitiva que nos ayude a llegar a los bordes.
¡Manos a la obra!
Definí dos procedimientos: el procedimiento PonerGuirnalda que coloque 3 bolitas rojas y 3
bolitas verdes en una celda y el procedimiento DecorarTablero que lo utilice y ponga una
guirnalda en cada esquina superior. Invocá DecorarTablero en el program. Tené en cuenta que
no sabemos la posición inicial donde se encontrará el cabezal.
 ¡Dame una pista!
En la Biblioteca ya tenés definidos procedimientos  que te pueden ayudar.
No te olvides de utilizar IrAlBorde.
biblioteca

procedure Poner3Verdes(){
Poner(Verde)
Poner(Verde)
Poner(Verde) }
procedure Poner3Rojas(){
Poner(Rojo)
Poner(Rojo)
Poner(Rojo) }

SOLUCIÓN
procedure PonerGuirnalda(){
Poner3Verdes()
Poner3Rojas()
}
procedure DecorarTablero(){
IrAlBorde(Norte)
IrAlBorde(Oeste)
PonerGuirnalda()
IrAlBorde(Norte)
IrAlBorde(Este)
PonerGuirnalda() }
program {
DecorarTablero() }
¡Muy bien! Tu solución pasó todas las pruebas
Cuanto más complejo sea el problema, más útil nos va a ser pensar una estrategia y
organizar la solución en subtareas ¡y los procedimientos están para ayudarnos!

Ejercicio 14: Colores, colores, colores


Vamos a darle un poco más de color a todo esto haciendo líneas multicolores como esta:
Como se ve en la imagen, cada celda de la línea debe tener una bolita de cada color (una roja, una
negra, una verde y una azul).
¿Cómo podemos dibujarla? ¿Cuál es la tarea que se repite? ¿Se puede definir un
nuevo procedimiento para resolverla y aprovecharlo para construir nuestra solución?
Definí un procedimiento DibujarLineaColorida que dibuje una línea multicolor de cuatro celdas
hacia el Este y al finalizarla ubique el cabezal en la celda inicial. Invocá el nuevo procedimiento en
un program.
 ¡Dame una pista!
Sería interesante definir un procedimiento que ponga una bolita de cada color en la celda
actual, ¿no?
procedure Color(){
  Poner(Rojo)
  Poner(Verde)
  Poner(Negro)
  Poner(Azul) }
procedure DibujarLineaColorida(){
  Color()
  Mover(Este)
  Color()
  Mover(Este)
  Color()
  Mover(Este)
  Color()
  IrAlBorde(Oeste) }
program{
  DibujarLineaColorida() }
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 15: Cuadrado de colores


Vamos a crear un procedimiento que nos permita dibujar un tablero como este:
Definí un procedimiento DibujarCuadradoColorido que dibuje un cuadrado de 4×4 celdas
en el que cada celda tenga una bolita de cada color e invocalo en el program. El cabezal debe
quedar en la celda inicial.
¡Dame una pista!
En la Biblioteca tenés el procedimiento DibujarLineaColorida que hiciste en el ejercicio
anterior, ¡no tenés que volver a definirlo! 
Biblioteca

procedure DibujarLineaColorida() {
PonerCeldaColorida()
Mover(Este)
PonerCeldaColorida()
Mover(Este)
PonerCeldaColorida()
Mover(Este)
PonerCeldaColorida()
IrAlBorde(Oeste) }
procedure PonerCeldaColorida() {
Poner(Verde)
Poner(Rojo)
Poner(Negro)
Poner(Azul) }

procedimiento DibujarLineaColorida del ejercicio anterior

procedure Color( ){
  Poner(Rojo)
  Poner(Verde)
  Poner(Negro)
  Poner(Azul) }
procedure DibujarLineaColorida( ){
  Color()
  Mover(Este)
  Color()
  Mover(Este)
  Color()
  Mover(Este)
  Color()
  IrAlBorde(Oeste) }
program{
  DibujarLineaColorida() }
Solución
procedure DibujarCuadradoColorido(){
DibujarLineaColorida()
Mover(Norte)
DibujarLineaColorida()
Mover(Norte)
DibujarLineaColorida()
Mover(Norte)
DibujarLineaColorida()
IrAlBorde(Sur) }
program {DibujarCuadradoColorido() }
 ¡Muy bien! Tu solución pasó todas las pruebas
Tablero final(Todas las celdas llenas con las 4 bolitas en cada una de colores:Azul-Negra-
Verde y Roja
A diferencia de la guía anterior, donde sólo usamos comandos que venían con Gobstones,
en esta lección aprendiste a crear tus propios comandos . Y eso es lo más lindo de
programar, poder inventar tus propias tareas para resolver los problemas. 
Esta poderosa herramienta nos va a permitir reutilizar más código, ya que al usar
un procedimiento podremos descomponer un problema en subproblemas más pequeños
y manejables. 
Repetición Simple
Como te contábamos cuando empezaste, programar nos da un gran poder: nos permite
automatizar tareas repetitivas y tediosas.
¿Y qué quiere decir eso de "repetitivas"? Pensemos, por ejemplo, cómo haríamos un programa
que ponga 5 bolitas azules:

program {
Poner(Azul)
Poner(Azul)
Poner(Azul)
Poner(Azul)
Poner(Azul) }

¿Notás qué es lo que se repite? Sí, estamos haciendo 5 veces lo mismo: poner una bolita
azul. Sin dudas, sería mucho más interesante que la computadora hiciera eso por
nosotros... ¡o si no te estaríamos mintiendo con lo de automatizar!
En esta guía vamos a aprender cómo decirle a la computadora que repita varias veces lo
mismo, y también algunos trucos más.
Ejercicio 1: MoverOeste10
Entremos en calor: definí un procedimiento  MoverOeste10 que mueva el cabezal 10 veces hacia el
Oeste.
 ¡Dame una pista!
Todavía no te dimos ninguna herramienta nueva, así que vas a tener que resolverlo con lo
que sabés hasta ahora. Y sí, es bastante fea la solución que podés hacer. 
procedure MoverOeste10( ){
Mover(Oeste)
Mover(Oeste)
Mover(Oeste)
Mover(Oeste)
Mover(Oeste)
Mover(Oeste)
Mover(Oeste)
Mover(Oeste)
Mover(Oeste)
Mover(Oeste) }
¡Muy bien! Tu solución pasó todas las pruebas
¿Te imaginás cómo hacer lo mismo pero 20, 100 o 5000 veces? Sería bastante
molesto, ¿no?
Evidentemente tiene que haber una forma mejor de hacerlo, si no eso de
"automatizar tareas repetitivas" sería una mentira. Y bueno, de hecho la hay:
¡vayamos al siguiente ejercicio!
Ejercicio 2: La computadora repite por nosotros
Como te adelantamos en el ejercicio anterior, en Gobstones existe una forma de
decir "quiero que estos comandos se repitan esta cantidad de veces".
Entonces, cuando es necesario repetir un comando
(como Mover, Poner, DibujarLineaNegra, etc) un cierto número de veces, en lugar de
copiar y pegar como veníamos haciendo hasta ahora, podemos utilizar la
sentencia repeat.
Sabiendo esto, así es como quedaría MoverOeste10 usando repeat:

procedure MoverOeste10() {
repeat(10) {
Mover(Oeste) } }

¡Muy bien! Tu solución pasó todas las pruebas


Ahora sí se empieza a poner interesante esto de la programación .

Ejercicio 3: MoverOeste5 usando repeat


Llegó tu turno de nuevo: definí un procedimiento MoverOeste5 que se mueva 5 veces al Oeste.
Obvio, esta vez tenés que usar repeat.
 ¡Dame una pista!
El código del ejercicio anterior puede ayudarte un poco 

procedure MoverOeste10() {
repeat(10) {
Mover(Oeste) } }

Solución
procedure MoverOeste5() {
repeat(5) {
Mover(Oeste) } }

 ¡Muy bien! Tu solución pasó todas las pruebas


Como ya descubriste, el comando repeat consta básicamente de dos elementos:
Un número entero (o sea, sin decimales), que indica cuántas veces hay que repetir. Este número va entre
paréntesis (( )) luego de la palabra repeat.
Y un bloque de código, que va encerrado entre llaves ({ }) y especifica qué comandos se
quieren repetir. Es MUY importante que no te los olvides, porque sino la computadora no
va a saber qué es lo que quisiste repetir (y fallará ).

Ejercicio 4: No todo es repetir


Los ejemplos que hiciste en los ejercicios anteriores se solucionaban simplemente repitiendo
cosas. Pero no todo es repetir, también podemos poner comandos
tanto antes como después del repeat, al igual que veníamos haciendo hasta ahora.
Por ejemplo, este es un programa que se mueve al Sur, luego pone 4 bolitas de
color Rojo y por último vuelve a moverse al Norte:

program {
Mover(Sur)
repeat(4) {
Poner(Rojo) }
Mover(Norte) }

Fijate que Mover(Sur) lo pusimos antes del repeat y Mover(Norte) lo pusimos después. Por lo tanto cada


movimiento se ejecuta solo una vez. Teniendo en cuenta esto:
Definí el procedimiento Poner3AlNoreste(), que ponga 3 bolitas negras en la primera celda al
Noreste del cabezal.
 ¡Dame una pista! Recordá que no existe la dirección Noreste  en Gobstones, pero si
tenemos Norte y Este. Solución
procedure Poner3AlNoreste(){
Mover(Norte)
Mover(Este)
repeat(3){
Poner(Negro) } }
 ¡Muy bien! Tu solución pasó todas las pruebas
¿Viste qué importante es definir bien qué comandos hay que repetir y cuáles no?
Es muy común, al principio, olvidarse de colocar las llaves o incluso pensar que no son
importantes. Pero tené mucho cuidado: poner las llaves en el lugar erróneo puede cambiar por
completo lo que hace tu programa. Mirá qué distinto sería el resultado si hubieras puesto
el Mover(Este) adentro del repeat:

procedure Poner3AlNoreste() {
Mover(Norte)
repeat(3) {
Mover(Este)
Poner(Negro) } }

Ejercicio 5: También vale después


Definí el procedimiento PonerAzulLejos, que coloque una bolita Azul 4 celdas hacia el Este:
¡Dame una pista! Como te dijimos, también es posible ejecutar
comandos después del repeat.
procedure PonerAzulLejos(){
repeat(4){
Mover(Este) }
Poner(Azul) }
¡Muy bien! Tu solución pasó todas las pruebas
Como ya experimentaste, pueden ponerse comandos tanto antes como después del repeat. En
definitiva... ¡es sólo un comando más!
Ejercicio 6: Repitiendo varios comandos
Hasta el momento los ejemplos que vimos sólo repetían un comando, pero como mencionamos al
comenzar es posible repetir cualquier secuencia de comandos - en definitiva lo que se repite es
un bloque y, como ya sabíamos, en un bloque puede haber tantos comandos como se nos ocurra.
Miremos el código de DibujarLineaNegra6 que podríamos haber hecho sin usar repeat, con
algunos espacios en blanco para ayudarnos a reconocer la secuencia que se repite:

procedure DibujarLineaNegra6() {
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este)
Poner(Negro)
Mover(Este) }

¿Notás qué es lo que se repite y cuántas veces? Bueno, eso es lo que tenés que poner en
el repeat.
Definí una versión superadora de DibujarLineaNegra6, esta vez usando repeat.
 ¡Dame una pista! Acordate que para escribir un repeat tenés que pensar dos cosas:
 Qué secuencia de comandos se repite. En este caso la secuencia consta de varios comandos,
eso es lo que tenés que poner entre las llaves ({ }) del repeat.
 Cuántas veces se repite esta secuencia. Eso va entre los paréntesis (( )) que van luego de la
palabra repeat; y como sabemos que sabés contar no te vamos a decir qué poner. 
Solución
 procedure DibujarLineaNegra6( ) {
 repeat(6) {
 Poner(Negro)
 Mover(Este) } } Muy bien! Tu solución pasó todas las pruebas
Ejercicio 7: ¿Dónde está el error?
Esta solución para LineaRoja4 no resuelve el problema como esperábamos, mirá:
procedure LineaRoja4() {
repeat(4) { Mover(Norte)
Poner(Rojo) } }
¿Nos ayudás a corregirla? Te dejamos el código en el editor.
 ¡Dame una pista!
Si te perdiste y querés volver a la solución inicial, podés presionar "Reiniciar" dentro del editor.
SOLUCION VALIDA
procedure LineaRoja4() {
repeat(4) {
Poner(Rojo)
Mover(Norte) } }  ¡Muy bien! Tu solución pasó todas las pruebas
¡Bien, corregiste el error! No fue tan fácil de encontrar, ¿no?  Ahora ya sabemos que el
orden dentro de un repeat también importa, y mucho.
Ejercicio 8: Diagonal con una bolita
Definí un procedimiento Diagonal4Azul que dibuje una diagonal de longitud 4 hacia el Noreste,
donde cada celda tenga una bolita azul. El cabezal debe quedar donde muestra la imagen.
SOLUCION VALIDA
procedure Diagonal4Azul(){
repeat(4){
Poner(Azul)
Mover(Este)
Mover(Norte) } } ¡Muy bien! Tu solución pasó todas las pruebas
Ejercicio 9: Diagonal "pesada"
Ahora vamos a hacer lo mismo, pero en versión "pesada".
¿Qué quiere decir esto? Que en vez de poner 1 bolita en cada celda, ahora hay que poner
21. Mirá la imagen:
Definí un procedimiento DiagonalPesada4Azul que resuelva el problema.
 ¡Dame una pista!
A diferencia del anterior, ahora lo que tenés que hacer en cada celda es más complejo
(tenés que hacer más que un simple Poner(Azul)).
Y, como realmente queremos que aprendas a dividir tu problema en subtareas, es
importante que en tu solución no anides estructuras de repetición. SOLUCION
procedure Bolitas(){
  repeat(21){
    Poner(Azul)   } }
procedure DiagonalPesada4Azul(){
  repeat(4){
    Bolitas()
    Mover(Norte)
    Mover(Este)   } }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  Con el cabezal en el origen Con el cabezal desplazado
Muy bien. Aunque el repeat es poderoso y nos ayuda a escribir menos código, sigue siendo igual de
importante la división en subtareas.
¡No vale olvidarse de lo aprendido hasta ahora!
Ejercicio 10: El caso borde
Muchas veces cuando usamos un repeat nos encontramos con que el último caso es levemente
distinto a los anteriores, situación que solemos llamar caso borde. Pero mejor, veamos un ejemplo.
El procedimiento LineaNegra4Este que te presentamos dibuja una línea negra hacia el Este dejando
el cabezal fuera de la línea, una celda hacia el Este.

procedure LineaNegra4Este() {
repeat(4) {
Poner(Negro)
Mover(Este) } }

Si ahora queremos hacer que deje el cabezal en la última celda de la línea, tenemos dos opciones:
 Mover el cabezal al Oeste luego de dibujar la línea. Un truco medio feo, porque para
funcionar necesita que haya al menos 5 espacios al Este de la posición inicial, cuando
nuestra línea sólo ocupará 4.
 Tratar el último caso de manera especial. Esta opción es más interesante y más fiel a lo
que queremos hacer: la última vez no queremos que el cabezal se mueva, simplemente nos
basta con poner la bolita negra.
Teniendo en cuenta esto último, definí una nueva versión de LineaNegra4Este que deje el cabezal
en la última celda de la línea.
¡Dame una pista!
Algunos ejercicios atrás comprobaste que se pueden poner cosas tanto antes como después de
un repeat. Podés usar esa misma idea para tratar el último caso de manera especial, sacándolo
del repeat.
Ojo: al "sacar un caso para afuera" también vas a tener que reducir el número de veces que repetís
los otros casos. 
Solución ejercicio perfectA
procedure LineaNegra4Este(){
  repeat(3){
    Poner(Negro)
    Mover(Este)   }
  Poner(Negro) }
 ¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
Siempre que tengas problemas como este vas a poder solucionarlos de la misma
manera: procesando el último caso por separado.
Otra variante menos común, y tal vez más difícil de construir también, es la de procesar
el primer caso aparte:

procedure LineaNegra4Este() {
Poner(Negro)
repeat(3) {
Mover(Este)
Poner(Negro) } }

Por convención, vamos a preferir la forma que procesa distinto al último caso, aunque a menudo
ambas sean equivalentes (es decir, produzcan el mismo resultado)

Ejercicio 11: De lado a lado, dibujamos un cuadrado


En el ejercicio anterior definimos el procedimiento LineaNegra4Este. Ahora vamos a
utilizarlo para dibujar un cuadrado negro igualito a los de los tableros de ejemplo:
Definí el procedimiento CuadradoNegro4 para dibujar un cuadrado de 4x4 con bolitas negras. Al
empezar, el cabezal se encuentra en la esquina inferior izquierda del cuadrado (no necesariamente
del tablero ) y cuando termine el programa el cabezal deberá quedar en el extremo superior
derecho del cuadrado. No te olvides de invocar LineaNegra4Este.
Tené en cuenta lo que hablamos en el ejercicio anterior sobre el caso borde. 
 ¡Dame una pista!
Podemos pensar a un cuadrado de 4x4 como cuatro líneas de longitud 4, una arriba de la
otra. Con el procedimiento que ya definiste tenés el problema de dibujar una línea
resuelto, aunque el cabezal queda en una posición poco conveniente para dibujar la
siguiente. ¿Qué deberíamos hacer después de dibujar cada línea? ¿En qué dirección me
conviene hacerlo?
Práctica Repetición simple Ejercicio 1: Entrando en calor...
¿Te animás a definir el procedimiento Diagonal4AzulVolviendo? Este procedimiento debería hacer
lo mismo que Diagonal4Azul, pero tiene que dejar el cabezal en la posición inicial. Recordá que
podés invocar todo lo que está en la Biblioteca sin necesidad de volver a definirlo. 
procedure Diagonal4Azul(){ (debería ir asi) procedure Diagonal4AzulVolviendo() {
repeat(4){ Diagonal4Azul()
Poner(Azul) repeat(4){
Mover(Este) Mover(Oeste)
Mover(Norte) Mover(Sur)
Diagonal4AzulVolviendo(){ }
repeat(4){ }
Mover(Oeste)
Mover(Sur)
Poner(Azul)
}
} } ESTA ES LA SOLUCION

Ejercicio 2: Una diagonal más ancha


Sigamos probando tus habilidades para reutilizar...
Ahora, tenés que hacer este dibujo:
El procedimiento debe llamarse BandaDiagonal4. ¡Ojo! prestá atención a la posición
final del cabezal.
 ¡Dame una pista!
De nuevo, pensá: ¿qué procedimiento que definiste antes puede ayudarte a resolver parte
de esta tarea?
BIBLIOTECA
procedure BandaDiagonal4(){ procedure Diagonal4AzulVolviendo(){
repeat(2){ Diagonal4AzulAvanzando()
Diagonal4AzulVolviendo() repeat(4){
Mover(Norte) Mover(Sur)
} Mover(Oeste)
{ }
Diagonal4AzulVolviendo() }
}
Mover(Sur)
Mover(Sur)
}
Ejercicio 3: Pongamos... ¡Todo lo que queramos!
Ahora que tenemos una idea de reutilización, y practicamos repetición, vamos a definir un
procedimiento que nos va a servir de acá en adelante.
Necesitamos un procedimiento que nos ayude a poner muchas bolitas. Sí, podríamos
simplemente usar un repeat para lograrlo, pero como es una tarea re-común que
vamos a hacer un montón de veces, vamos a preferir definir
un procedure llamado PonerN. Nuestro procedimiento debe poner la cantidad
de bolitas indicada de un color dado.
Definí el procedimiento PonerN(cantidad, color).
Aunque quizás no veas todavía la utilidad de este procedure que creamos, te contamos dos
aspectos que es importante tener en cuenta al programar:
 reutilización de código: como poner muchas bolitas es una tarea común, está
bueno tener un procedimiento que lo resuelva: lo escribimos una vez y lo usamos
para siempre;

 declaratividad: cuando tengamos que resolver un problema más complejo, tener


este procedimiento nos va a ayudar a pensar a más alto nivel, ya que no vamos a
tener que preocuparnos por cómo poner muchas bolitas sino en qué queremos
construir con ellas.

Ejercicio 4: Día de la Memoria


Muchas veces vamos a usar el tablero de Gobstones como memoria, o sea, para
recordar algo importante que vamos a necesitar más adelante.
¿Qué podríamos representar con bolitas? Por ejemplo una fecha. Una fecha que
debemos recordar es el 24 de marzo de 1976, hoy constituido Día de la Memoria
por la Verdad y la Justicia en Argentina.
El objetivo, entonces, es definir un procedimiento  DiaDeLaMemoria():
En la celda actual, poné 24 bolitas Azules, que representan el día.
En la celda inmediatamente al Este, poné 3 bolitas Verdes, que representan el mes.
En la celda a continuación, poné 1976 bolitas Negras, representando el año.

Expresion literal
 ¡Dame una pista! Argumentos entre parentesis
Ya que definimos PonerN(cantidad, color), ahora podemos invocarlo, ¿no?.
Solucion correcta del ejercicio
procedure DiaDeLaMemoria(){
PonerN(24,Azul)
Mover(Este)
PonerN(3,Verde)
Mover(Este)
PonerN(1976,Negro) }
¡Muy bien! Tu solución pasó todas las pruebas
¿Sabías que Azul es una expresión literal? ¡También 1976! También
son expresiones literales: Verde, Negro, 3 y 24.
Cuando usamos un procedimiento que tiene parámetros como
PonerN, PonerN(56, Rojo) tenemos que enviarle valores como argumento. ¡Y
las expresiones sirven para eso!
Ejercicio 5: Escribir cualquier fecha
Ahora que ya escribimos una fecha particular, hagamos un procedimiento que sirva para
escribir cualquier fecha.
Definí el procedimiento Fecha(dia, mes, anio), que recibe los tres valores correspondientes, y
escribe la fecha que representan, de esta manera:
En la celda actual, tantas bolitas azules para representar el día.
En la celda inmediatamente al Este, tantas bolitas Verdes para representar el mes.
En la celda a continuación, tantas bolitas Negras para representar el año.
Por ejemplo, Fecha(12, 8, 1990) produciría algo así:
 ¡Dame una pista!
¡Podés resolverlo muy parecido a como lo resolviste en el ejercicio anterior!
Recorda que el día, mes y año representan la cantidad de bolitas azules, verdes y negras
que hay que poner respectivamente.
Biblioteca
procedure PonerN(cantidad,color){
repeat(cantidad){
Poner(color) } }

SOLUCION CORRECTA DEL EJERCICIO


procedure Fecha (dia, mes, anio) {
PonerN (dia, Azul)
Mover (Este)
PonerN (mes, Verde)
Mover (Este)
PonerN (anio,Negro) }
 ¡Muy bien! Tu solución pasó todas las pruebas
Otros valores
Dos cuestiones teóricas para pensar de este ejercicio:
1. Puede parecerte que estás repitiendo código con el ejercicio anterior. ¡Es cierto! Para
arreglarlo, deberías volver al ejercicio anterior y allí usar el procedimiento Fecha. 
2. Acá vemos que hay otro tipo de expresiones: ¡Los parámetros! Al usar un procedimiento,
puedo enviarle tanto otros parámetros como literales: PonerN(dia, Azul). Recordá en este
caso que los nombres de los parámetros sólo nos sirven a los humanos, para la máquina
sólo importa el orden.

Ejercicio 6: Movamos... ¡Todo lo que queramos!


Definí un procedimiento MoverN(cantidad, direccion) que haga que el cabezal se desplace la
cantidad especificada de veces en la dirección indicada.
Por ejemplo, MoverN(3, Oeste) provocaría:
 ¡Dame una pista!
¡Es muy parecido al PonerN que hicimos hace poco!

procedure PonerN(cantidad,color){
repeat(cantidad){
Poner(color) } }

Solucion Correcta
procedure MoverN(cantidad, direccion){
repeat(cantidad){
Mover(direccion) } }
 ¡Muy bien! Tu solución pasó todas las pruebas
Recordamos entonces que entre los paréntesis del repeat no sólo pueden ir números
(como 7 o 42), sino también otros tipos de expresiones que denoten valores numéricos
(como cantidad en este caso).
Ejercicio 7: Los números del reloj
Ahora, tenés que mostrarnos que podés dibujar un reloj. Lo que haremos por ahora es solamente
poner los números que aparecen en un típico reloj de agujas:
 El 12 arriba,
 El 3 a la derecha,
 El 9 a la izquierda, y
 el 6 abajo.
Definí un procedimiento DibujarReloj(radio) que ponga los números del reloj como se indica
arriba: alrededor del casillero actual. El tamaño del reloj se indica con el radio que recibís como
parámetro: mientras más grande es el radio, más alejados están los números del centro.
Dado el siguiente program:

program {
DibujarReloj(2) }

El reloj resultante es así


¡Dame una pista!
¡Dividí en subtareas!
No vale hacer un procedimiento que sea un choclo, con muchos comandos uno abajo del
otro, tiene que ser legible y cortito.
Pensá como combinar PonerN y MoverN, los cuales podés encontrar en la siempre
útil Biblioteca.

procedure PonerN(cantidad,color){
repeat(cantidad){
Poner(color) } }
procedure MoverN(cantidad,direccion){
repeat(cantidad){
Mover(direccion) } }

SOLUCION CORRECTA
procedure Agregarhora(hora,direccionIda,direccionVuelta,radio){
MoverN(radio, direccionIda)
PonerN(hora, Rojo)
MoverN(radio, direccionVuelta) }
procedure DibujarReloj(radio){
Agregarhora(12, Norte, Sur, radio)
Agregarhora(3, Este, Oeste, radio)
Agregarhora(9, Oeste, Este, radio)
Agregarhora(6, Sur, Norte, radio) }
Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
¿Te fijaste? Estamos usando bolitas para representar la hora de un reloj. Al programar,
usamos las abstracciones que tenemos para modelar cosas del mundo real.
Y como siempre, es muy importante dividir el problema en subtareas. Y si puedo no
repetir código, ¡Aún mejor!
Antes de pasar al siguiente ejercicio preguntate: ¿repetí código?
Ejercicio 8: Una línea heavy
El procedimiento LineaEstePesada(peso, color, longitud) debe dibujar hacia
el Este una línea del color dado, poniendo en cada celda tantas bolitas como
indique el peso. La linea debe ser tan larga como la longitud.
A modo de ejemplo, LineaEstePesada(3, Verde, 5) debería dibujar una línea verde,
ocupando cinco celdas hacia el Este y poniendo tres bolitas en cada una de ellas:
Definí el procedimiento LineaEstePesada(peso, color, longitud). Tené en cuenta que el
cabezal debe regresar a la posición inicial. Para eso vas a tener que invocar MoverN.
Biblioteca
procedure PonerN(cantidad,color){
repeat(cantidad){
Poner(color) } }
procedure MoverN(cantidad,direccion){
repeat(cantidad){ Mover(direccion) } }

SOLUCION CORRECTA
1. procedure LineaEstePesada(peso, color, longitud){
2. repeat(longitud){
3. PonerN(peso, color)
4. MoverN(1,Este) }
5. MoverN(longitud, Oeste) }
¡Muy bien! Tu solución pasó todas las pruebas
¿Viste que se pueden reusar MoverN y PonerN en varios lugares? ¡Son muy útiles!

Ejercicio 9: Guarda con la guarda


Bueno, estamos en tiempo para algún ejercicio integrador...
Definí un procedimiento GuardaDe5(), que haga una "guarda" de 5 azulejos (como las que decoran
las paredes). Cada azulejo está conformado por 1 bolita verde, 5 negras y 9 rojas.
¡Dame una pista!
No te olvides de dividir en subtareas y además de considerar el caso borde.
¡A no repetir código! Cada azulejo puede resolverse con un procedimiento...
BIBLIOTECA

procedure PonerN(cantidad,color){
repeat(cantidad){
Poner(color) } }

SOLUCION VALIDA. EJERCICIO RESUELTO


1. procedure AzulejoTric(){
2. PonerN(5,Negro)
3. PonerN(9,Rojo)
4. PonerN(1,Verde) }
5. procedure GuardaDe5(){
6. repeat(4){
7. AzulejoTric()
8. Mover(Este) }
9. AzulejoTric() }
¡Muy bien! Tu solución pasó todas las pruebas
¡Bien! Recordaste cómo considerar el caso borde.
Además, en este ejercicio hay que dividir en subtareas para evitar la repetición de código.
Esto es muy importante a la hora de programar. ¡Asegurate que tu solución no repita
código!
Ejercicio 10: Una guarda en L
Definí un procedimiento GuardaEnL() que haga una guarda en L como muestra la figura,
pero dejando el cabezal en la posición inicial.
La ventaja ahora, es que te regalamos el procedimiento PonerAzulejo. ¡Pero ojo que
necesitás dividir en más subtareas!
 ¡Dame una pista!
Para dividir en subtareas vas a tener que pensar si podés partir el problema principal de
dibujar una L en problemas más chicos, y cada una de ellas será un procedimiento nuevo
que deberás usar desde GuardaEnL.
Biblioteca
procedure PonerAzulejo(){
Poner(Verde)
PonerN(5,Negro)
PonerN(9,Rojo) }
procedure PonerN(cantidad,color){
repeat(cantidad){
Poner(color) }
}
procedure MoverN(cantidad,direccion){
repeat(cantidad){
Mover(direccion) } }

Solucion correcta del ejercicio


1. procedure MoverAzulejo(num, dir){
2. repeat(num){
3. Mover(dir)
4. PonerAzulejo() } }
5. procedure GuardaEnL(){
6. PonerAzulejo()
7. MoverAzulejo(2, Este)
8. MoverN(2, Oeste)
9. MoverAzulejo(2, Norte)
10. MoverN(2, Sur) }
 ¡Muy bien! Tu solución pasó todas las pruebas
Terminaste Práctica Repetición simple!
En esta guía:
 Reusaste procedimientos definidos anteriormente.
 Usaste repetición.
 Consideraste los casos borde.
 Hiciste un repaso grande de parámetros.
 ¡Evitaste repetir código! 
 Empezaste a usar las bolitas para representar varios dominios: Fechas, Relojes y
Guardas.
 Aprendiste que los literales y los parámetros son expresiones que se pueden pasar
por argumento a los procedimientos.
Expresiones
¿Sepuedenhacerprogramasque sumen cosas? 
¿Y dibujar figuras en distintas direcciones sin tener que especificar cada una deellas?  
¿Y mirar lo que hay en el tablero? 
Todo eso y (no) mucho más en esta nueva entrega de "aprendiendo a programar con
bolitas de colores". ¡Arrancamos!

Ejercicio 1: Muchas formas de decir lo mismo


Cuando nos comunicamos con alguien más, usamos palabras o frases para describir una
idea. Por ejemplo, todas las siguientes expresiones hablan de lo mismo, aunque lo hacen
de distintas formas:
 el número 5; 
 la cantidad de dedos de una mano; 
 la suma entre 3 y 2;   
 el número de continentes que existen en el planeta, según la ONU. 
Todas las frases anteriores hablan del valor cinco, aunque no lo digan de forma explícita.
Con esta idea e invocando PonerN, creá un programa que ponga cinco bolitas negras, PERO sin
escribir el número 5.
 ¡Dame una pista! ¿Y cómo se escribe una suma o una resta en Gobstones?
Igual que en la vida real.
SOLUCION CORRECTA
Solucion Biblioteca

program { procedure PonerN(cantidad, color) {


PonerN(3+2,Negro) repeat(cantidad){
Poner(color)
} } }
 ¡Muy bien! Tu solución pasó todas las pruebas
Algunas variantes válidas:

program {
PonerN(4 + 1, Negro) }

program {
PonerN(12 - 7, Negro) }

Y así se nos pueden ocurrir infinitas formas de "decir 5" y sólo una de ellas lo hace de
manera literal (o sea, escribiendo 5).

Ejercicio 2: La suma de las partes


Juguemos un poco más con esto de hacer cuentas.
Definí un procedimiento PonerSuma(x, y) que reciba dos parámetros y ponga la cantidad de
bolitas rojas que surge de sumar x e y.
Ejemplo: PonerSuma(4, 2) debería poner 6 bolitas rojas en la celda actual (porque 6 es el
resultado de sumar 4 y 2).
SOLUCION CORRECTA
procedure PonerSuma(x,y){
PonerN(x+y,Rojo) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
Como ya descubriste, podemos escribir expresiones aritméticas (o sea, cuentas
matemáticas) en los mismos lugares donde hasta ahora veníamos escribiendo números.

Ejercicio 3: ¿Qué se hace antes?


De un conocido diario (no podemos revelar su nombre por temas de confidencialidad) nos
pidieron definir un procedimiento para contar, aproximadamente, cuánta gente asistió a una
determinada manifestación.
Contamos con la información de cuántos micros, autos y bicicletas asistieron y desde allí podemos
hacer un cálculo siguiendo estas reglas:
 en cada micro viajan 40 personas;
 en cada auto viajan 4 personas;
 en cada bicicleta viaja 1 persona.
Definí el procedimiento ContarGente(micros, autos, bicicletas) que a partir de la cantida de
micros, autos y bicicletas que recibe como parámetro, haga las cuentas necesarias y refleje el
resultado con bolitas de color verde.
Te dejamos un par de ejemplos que te pueden ayudar:
 ContarGente(1, 1, 1) generaría este tablero:
 ContarGente(1, 2, 3) generaría este tablero:
 ¡Dame una pista!
Ah, un detalle: la multiplicación en Gobstones se hace con el símbolo * (por
ejemplo, 3 * 2).
Biblioteca
procedure PonerN(cantidad, color) {
repeat(cantidad) {
Poner(color) } }

SOLUCION CORRECTA
procedure ContarGente(micros, autos, bicicletas){
PonerN(40*micros+4*autos+bicicletas, Verde) }
 ¡Muy bien! Tu solución pasó todas las pruebas Resultados de las pruebas:
En Gobstones, como en la matemática, existe la idea de precedencia de
operadores. En criollo, esto quiere decir que hay ciertas operaciones que se hacen
antes que otras, sin la necesidad de usar paréntesis para ello. En particular, el orden
es: primero las multiplicaciones y divisiones, luego las sumas y las restas (de nuevo,
como en matemática).
Por lo tanto, la expresión (10 * 4) + (8 * 7) es equivalente a 10 * 4 + 8 * 7.

Ejercicio 4: La carrera del salmón


Bueno, basta de números (por un ratito). Ahora vamos a aprender a hacer
"cuentas" con las direcciones.
Para hacer esto, simularemos el movimiento de un salmón: en contra de la
corriente. Nuestro objetivo será definir un
procedimiento MoverComoSalmon(direccion) que reciba una dirección y se mueva
exactamente una vez en la dirección opuesta. Veamos en una tabla cómo
debería comportarse este procedimiento:
 MoverComoSalmon(Norte)  se mueve hacia el Sur.
 MoverComoSalmon(Este)  se mueve hacia el Oeste.
 MoverComoSalmon(Sur)  se mueve hacia el Norte.
 MoverComoSalmon(Oeste)  se mueve hacia el Este.
Como la dirección va a ser un parámetro de nuestro procedimiento, necesitamos una
forma de decir "la dirección opuesta a X" para poder luego usar esto como argumento
de Mover. Gobstones nos provee un mecanismo para hacer esto, la primitiva opuesto(dir).
En criollo: opuesto (¡sí, en minúsculas!) nos dice la dirección contraria a la dir que nosotros
le pasemos.
Sabiendo esto, podríamos definir fácilmente el procedimiento que queríamos:

procedure MoverComoSalmon(direccion) {
Mover(opuesto(direccion)) }

Escribí la solución en el editor y dale Enviar. Vas a ver cómo se mueve el cabezal...
1. procedure MoverComoSalmon(direccion){
2. Mover(opuesto(direccion)) }
¡Muy bien! Tu solución pasó todas las pruebas
Las expresiones no sólo pueden denotar números, también nos sirven para
realizar transformaciones sobre direcciones. Más sobre esto en los próximos
ejercicios.
Ejercicio 5: Dos pasos adelante, un paso atrás
Tenemos un amigo llamado Carlos, que es bastante desconfiado. En su vida, eso se
manifiesta en muchos aspectos, pero el más notorio es su forma de caminar: sólo camina
hacia el Este y siempre que da dos pasos hacia adelante automáticamente da un paso
hacia atrás.
Por ejemplo, si le pidiéramos que diera 2 pasos, terminaría dando 1; si le pidiéramos 4,
daría 2; y así sucesivamente. En definitiva, lo que termina pasando es que nuestro amigo
da la mitad de los pasos que le pedimos.
Importante: en Gobstones usamos el operador div para dividir; por ejemplo "4 dividido
2" se escribe 4 div 2.
Definí el procedimiento CaminarDesconfiado(pasos) que simule el caminar de Carlos: debe recibir la
cantidad de pasos que debería dar y recorrer la mitad. Siempre se mueve al Este.
 ¡Dame una pista!
Acordate que podés invocar MoverN(cantidad, direccion). Biblioteca

procedure MoverN(cantidad, direccion) {


repeat(cantidad) {
Mover(direccion) } }

Solucion Correcta
procedure CaminarDesconfiado(pasos){
MoverN(pasos div 2, Este) }
¡Muy bien! Tu solución pasó todas las pruebas
Sobre el ejemplo de 4 pasos, no hay dudas: Carlos dio 2 pasos. Ahora, cuando le pedimos
que diera 7, ¿por qué dio 3?
En Gobstones, la división es entera: se ignoran los decimales. De esta forma, 7 div
2 termina dando 3 en vez de 3.5.

Ejercicio 6: Poner al lado


Para ver si entendiste lo anterior, te toca ahora resolver por tu cuenta.
Queremos definir un procedimiento que nos sirva para poner una bolita al lado de donde
se encuentre el cabezal, dejándolo en la posición original. Por ejemplo, al
invocar PonerAl(Norte, Verde) debería poner una bolita verde una posición hacia el
Norte, sin mover el cabezal (bueno, ya sabemos que en realidad sí se mueve, pero el
punto es que en el resultado final esto no se tiene que ver).
Definí el procedimiento PonerAl(direccion, color).
¡Dame una pista!
Para volver a dejar el cabezal donde empezó, necesitás lograr que se mueva en la
dirección opuesta a la inicial.
¿Será algo parecido a lo que hicimos en el ejercicio anterior? 
SOLUCION CORRECTA lo que hice yo con error
procedure PonerAl (direccion,color){ procedure PonerAl(dirección,color){
Mover (direccion) MoverN (Norte)
Poner (color) Poner (Verde)
Mover (opuesto(direccion)) Mover (opuesto(Norte))
} }
 ¡Muy bien! Tu solución pasó todas las pruebas
Tanto opuesto(dir) como otras herramientas que veremos más adelante forman parte de
lo que conocemos como funciones y, como las expresiones aritméticas que ya vimos, se
pueden usar en cualquier lugar donde hasta ahora poníamos valores o argumentos.
O sea, donde hasta ahora podrías usar dir ahora también podrías poner opuesto(dir), ya
que ambas expresiones denotan direcciones. Obviamente te queda a vos decidir en cada
caso si tiene sentido usar opuesto o no.

Ejercicio 7: La línea que vuelve


Ahora que sabés usar la función opuesto, podemos finalmente resolver el problema de
definir un procedimiento que dibuje una línea en cualquier dirección y deje el cabezal en
la posición inicial.
La versión que sabíamos hacer hasta ahora era esta:

procedure Linea(direccion, color, longitud) {


repeat(longitud) {
Poner(color)
Mover(direccion) } }

Valiéndote de tus nuevos conocimientos sobre expresiones, modificá el procedimiento Linea para


que el cabezal quede en el lugar donde empezó.
 ¡Dame una pista!
MoverN y opuesto parecieran ser buenos aliados para el problema que tenés que resolver.
SOLUCION CORRECTA
1. procedure Linea(direccion, color, longitud) {
2. repeat(longitud) {
3. Poner(color)
4. Mover(direccion) }
5. {MoverN(longitud,opuesto(direccion) ) } }

¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 8: Dibujando una L


Nuestro objetivo en este ejercicio será definir un procedimiento capaz de dibujar una letra L de
color Azul, pero con la posibilidad de elegir hacia dónde está orientada. A continuación, algunos
ejemplos de cómo debería comportarse:
Indudablemente, una L consta de dos líneas y dibujar una línea es la tarea que ya resolviste en el
ejercicio anterior. Así que por ese lado, tenemos la mitad del problema resuelto.
La primera línea es fácil, porque coincide con la dirección que recibimos como argumento... ¿pero la
segunda? Bueno, ahí viene lo interesante: además de opuesto, Gobstones nos provee dos funciones
más para operar sobre las direcciones, siguiente y previo. siguiente(direccion) retorna la
dirección siguiente a la especificada, mientras que previo(direccion) retorna la anterior, siempre
pensándolo en el sentido de las agujas del reloj:
Descubrí cuál de las funciones nuevas tenés que invocar y definí el
procedimiento Ele(direccion). No te preocupes por la posición inicial del cabezal, nosotros nos
encargaremos de ubicarlo en el lugar correspondiente para que la L se pueda dibujar.
 ¡Dame una pista!
El procedimiento Linea recibe tres argumentos: la dirección, el color y la longitud. En este caso, lo
único que varía es la dirección: el color es siempre Azul y la longitud es siempre 3.
Biblioteca

procedure Linea(direccion, color, longitud) {


repeat(longitud) {
Poner(color)
Mover(direccion) }
MoverN(longitud, opuesto(direccion)) }
procedure MoverN(cantidad, direccion) {
repeat(cantidad) {
Mover(direccion) } }

SOLUCION CORRECTA
1. procedure Ele(direccion){
2. Linea(direccion, Azul, 3)
3. Linea(siguiente(direccion), Azul, 3) }
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 9: Previo a lo siguiente


Ya te presentamos las funciones opuesto, previo y siguiente que nos permiten desplazarnos
en forma relativa a alguna dirección . Antes de seguir utilizándolas, vamos a conocerlas un
poco mejor.
Si partimos de este tablero inicial:
Y luego ejecutamos Mover(siguiente(Norte)) el tablero obtenido será así:

Porque siguiente(Norte) es Este. Si luego ejecutamos Mover(previo(Oeste)) lograremos el


siguiente tablero:

Porque previo(Oeste) es Sur. ¡Veamos si se entendió!


Si tenemos este tablero inicial:

¿Qué tablero se consigue luego de invocar el siguiente procedimiento ?

procedure PonerMisterioso() {
Mover(siguiente(Este))
Poner(Negro)
Mover(opuesto(Oeste))
Poner(Negro)
Mover(previo(Oeste))
Poner(Negro) }
ESTA ES

¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 10: Siga la flecha


Ya vimos distintas funciones que a partir de una dirección nos permiten obtener otra
distintas.
Como siempre en programación, lo interesante es combinar nuestras herramientas para lograr
nuevos objetivos . Por ejemplo podemos dibujar flechas en una dirección determinada de la
siguiente forma:
Definí el procedimiento Flecha(direccion) que dibuje una flecha roja en la dirección
correspondiente. El cabezal empieza y debe quedar siempre en el centro, como se ve en los
tableros de ejemplo.
 ¡Dame una pista!
Tené en cuenta que cada vez que nos movamos en una dirección para poner una bolita
vamos a tener que volver a la dirección anterior. Por suerte tenemos una función para eso,
¿cuál?
RESULTADO CORRECTO RESOLUCION MIA CON ERROR
1. procedure Flecha(direccion) { procedure Flecha(direccion){
2. Mover(previo(direccion)) Mover(siguiente(direccion))
3. Poner(Rojo) Poner(Rojo)
4. repeat(2) { Mover(opuesto(direccion))
5. Mover(siguiente(direccion)) Mover(previo(direccion))
6. }
7. Poner(Rojo) Poner(Rojo)
8. Mover(previo(direccion)) Mover(previo(dirección))
9. Mover(direccion) Mover(siguiente(direccion))
10. Poner(Rojo) Poner(Rojo)
11. Mover(opuesto(direccion)) Mover(previo(dirección))
12. } }
Respuesta: también correcta
procedure Flecha(direccion){
Mover(previo(direccion))
Poner(Rojo)
Mover(siguiente(direccion))
Mover(siguiente(direccion))
Poner(Rojo)
Mover(previo(direccion))

Mover(siguiente(direccion))
Mover(previo(direccion))
Mover(direccion)
Poner(Rojo)
Mover(opuesto(direccion)) }
te explica que "previo" te devuelve la primer dirección que está ubicada girando en
sentido contrario al movimiento de las agujas de un reloj
Lo mismo aplica para cualquiera de las direcciones que se elija. "previo" significa "buscar
la primer dirección girando hacia la izquierda", y "siguiente" significa "buscar la primer
dirección girando hacia la derecha".
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 11: Copiando bolitas


Supongamos ahora que queremos "copiar" las bolitas verdes, haciendo que haya la misma
cantidad de rojas y pensemos cómo podría ser ese procedimiento.
Una tarea que seguro tenemos que hacer es poner muchas bolitas, y para eso ya sabemos que
existe el procedimiento PonerN que construimos varios ejercicios atrás. El color de las bolitas que
tenemos que poner también lo sabemos: Rojo, pero... ¿cómo sabemos cuántas poner?
Miremos algunos ejemplos:
 Si hay 4 bolitas verdes, hay que poner 4 bolitas rojas.
 Si hay 2 bolitas verdes, hay que poner 2 bolitas rojas.
 Si no hay bolitas verdes, no hay que poner ninguna roja.
Lo que nos está faltando es una forma de contar cuántas bolitas verdes hay, y para eso
necesitamos otra función que nos da Gobstones: nroBolitas(color). Lo que hace es simple: nos
retorna la cantidad de bolitas de un color determinado en la posición actual.
Invocando nroBolitas, definí el procedimiento CopiarVerdesEnRojas.
Dame una pista!
Si necesitaramos saber las cantidad de bolitas negras deberíamos invocar nroBolitas(Negro) en
nuestro procedimiento. Pero necesitamos saber la cantidad de verdes...
Biblioteca
procedure PonerN(cantidad, color) {
repeat(cantidad) {
Poner(color) } }

¡Muy bien! Tu solución pasó todas las pruebas


Si no hay verdes, no hace nada
Si hay 2 verdes, pone 2 rojas
Si hay 7 verdes, pone 7 rojas
En este ejercicio, además de aprender una expresión nueva, hiciste algo que nunca habías
hecho hasta ahora: un programa cuyo efecto depende del estado del tablero inicial.
¿Qué quiere decir esto? Que el código de CopiarVerdesEnRojas() se comporta
diferente dependiendo de cómo estaba el tablero antes de ejecutarlo.
Ejercicio 12: Sacando bolitas
Siguiendo con esto de contar las bolitas, te toca ahora definir un procedimiento que sirva para
sacar todas las bolitas de un color.
Pensemos las subtareas necesarias:
1. Poder sacar muchas bolitas: ya está resuelto con SacarN.
2. Contar cuántas bolitas hay que sacar: se puede hacer con nroBolitas.
3. Sacar todas las bolitas de un color: hay que combinar las 2 anteriores.
Definí SacarTodas(color), que recibe un color y saca todas las bolitas que hay de ese color (no debe
hacer nada con el resto de los colores).
 ¡Dame una pista! SacarTodas(Rojo) 
Biblioteca

procedure SacarN(cantidad, color) {


repeat(cantidad) {
Sacar(color) } }

SOLUCION CORRECTA
1. procedure SacarTodas(color){
2. SacarN(nroBolitas(color),color) }
Otra solución valida
procedure SacarTodas(color){
  SacarN(nroBolitas(color), color) }
¡Terminaste Expresiones!
Repasemos todo lo que aprendiste en esta guía:
Además de los literales que ya venías usando, existen también otro tipo
de expresiones que realizan algún tipo de operación y pueden depender de dos cosas: de
sus parámetros y del estado del tablero.
En el camino, conociste unas cuantas expresiones nuevas:
 Aritméticas: +, -, *, div.
 De direcciones: opuesto, siguiente, previo.
 Númericas: nroBolitas.
Y de yapa, también te adentraste un poquito en el arte de los programas que hacen cosas
distintas según el tablero que haya.

Alternativa Condicional
Hasta ahora, todos los programas y procedimientos que hicimos fueron sobre
tableros conocidos, sabíamos exactamente de qué tamaño era el tablero, dónde estaba el
cabezal y cuántas bolitas había en cada celda.
Nos introduciremos ahora en el mundo de lo desconocido, donde la programación cobra
aún mucho más sentido. Con la herramienta que veremos podremos resolver problemas
nuevos y evitar errores que hasta ahora no podíamos: sacar una bolita Roja sólo si hay
alguna, movernos al Norte si eso no provoca que nos caigamos del tablero o agregar una
bolita Azul sólo si ya hay una Verde.
¿Y cómo haremos esto? Utilizando la capacidad de la computadora para decidir:
la alternativa condicional o sentencia if.
Ejercicio 1: Sacar con miedo
Vamos a definir un procedimiento que saque una bolita azul "con miedo": no tiene que producirse
un BOOM, aún cuando no haya ninguna bolita en la celda actual.
Con lo que sabés hasta ahora, probablemente tu primera idea sea hacer algo como esto:

procedure SacarAzulConMiedo() {
Sacar(Azul) }

¡Probalo! Copiá el código anterior en el editor y apretá Enviar.


SOLUCION VALIDA
1. procedure SacarAzulConMiedo() {
2. Sacar(Azul) }
¡Muy bien! Tu solución pasó todas las pruebas
 Cuando no hay ninguna bolita azul, hace BOOM
BOOM

[2:3]: No se puede sacar una bolita de color Azul: no hay bolitas de ese color.

¿Te diste cuenta qué pasó?


Funcionó para el primer tablero porque tenía una bolita azul, pero hizo BOOM para el
segundo porque estaba vacío, claro.

Ejercicio 2: Sacar con miedo, segundo intento


Ahora probá esta segunda versión que agrega una alternativa condicional. No te
preocupes por la sintaxis, ya te lo vamos a explicar. 

procedure SacarAzulConMiedo() {
if (hayBolitas(Azul)) {
Sacar(Azul) } }

Copiá el código anterior en el editor y apretá Enviar.


Solucion correcta
procedure SacarAzulConMiedo() {
if (hayBolitas(Azul)) {
Sacar(Azul) } }
 ¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
 Cuando hay una bolita azul, la saca
 Cuando no hay ninguna bolita azul, no hace nada
Hiciste tu primer procedimiento que decide antes de ejecutar. Seguí con nosotros para entender de
qué se trata esto...

Ejercicio 3: Eliminando la bolita roja


Analicemos el procedimiento del ejercicio anterior:
procedure SacarAzulConMiedo() {
if (hayBolitas(Azul)) {
Sacar(Azul) } }

Como notarás, introdujimos una nueva estructura de control: el if, que en castellano significa si;
entendiendo al si como condicional ("si tuviera hambre me comería una empanada") y no como
afirmación ("sí, yo rompí el teléfono").
Entonces, lo que le estamos diciendo a la computadora es "si hay bolitas azules, sacá una bolita
azul", que dicho así suena un poco tonto ¡y lo es!. Ya te dijimos que la computadora sólo sabe
cumplir órdenes.
¡Ahora te toca a vos! Modificá el procedimiento que te dimos para que saque una bolita roja,
sólo si hay alguna.
 ¡Dame una pista!
SacarAzulConMiedo ya no es un buen nombre para el procedimiento, esperamos que puedas
cambiarlo también.
Solucion
procedure SacarRojoConMiedo() {
if (hayBolitas(Rojo)) {
Sacar(Rojo) } }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  Cuando hay una bolita roja, la saca
 Cuando hay más de una bolita roja, saca una
 Cuando no hay ninguna bolita roja, no hace nada

Ejercicio 4: Un ejemplo medio rebuscado


Vamos a ponerle nombre a las partes del if.
En primer lugar, tenemos la condición. Por ahora siempre fue hayBolitas(color) pero podría ser
cualquier otra cosa, ya veremos más ejemplos. Lo importante acá es que eso es lo que decide si
la acción se va a ejecutar o no.
¿Y qué es la acción? Básicamente, cualquier cosa que queramos hacer sobre el tablero. Al igual
que en el repeat, podemos hacer cuantas cosas se nos ocurran, no necesariamente tiene que ser
una sola.
Resumiendo: La acción que está dentro de la estructura del if podrá realizarse solo
cuando la condición sea verdadera.
Para ejercitar esto ultimo, te vamos a pedir que definas un
procedimiento CompletarCelda() que, si ya hay alguna bolita negra, complete la celda poniendo una
roja, una azul y una verde.
 ¡Dame una pista! Como referencia, acá tenés la solución del problema anterior:

procedure SacarRojaConMiedo() {
if (hayBolitas(Rojo)) {
Sacar(Rojo) } }
Solucion
procedure CompletarCelda() {
if (hayBolitas(Negro)) {
Poner(Rojo)
Poner(Azul)
Poner(Verde) } }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
 Cuando hay alguna bolita negra, completa la celda con las otras bolitas de colores rojo, azul y
verde
Cuando no hay ninguna bolita negra, no hace nada

Ejercicio 5: ¿Y sólo sirve para ver si hay bolitas?


Claro que no, ¡por suerte! 
La condición puede ser cualquier expresión booleana. En criollo: cualquier cosa que represente una
"pregunta" que se pueda responder con sí o no. En Gobstones el sí se representa con el
valor True (Verdadero en castellano) y el no con el valor False (Falso en castellano).
En los ejercicios anteriores te mostramos una de las expresiones que trae
Gobstones, hayBolitas(color), que recibe un color y retorna True o False.
Otra que trae True o False (y que vas a tener que usar ahora) es puedeMover(direccion) que
nos sirve para saber si el cabezal puede moverse en una cierta dirección.
 puedeMover(Norte) será True.
 puedeMover(Sur) será True.
 puedeMover(Este) será True.
 Pero puedeMover(Oeste) será False
Creá un programa que se mueva al Este sólo si es posible. Recordá utilizar puedeMover(direccion)
 ¡Dame una pista!
Ya sabés como usar Mover, sólo te resta pensar cómo armar la condición para poner en
el if. Y no te olvidés, solo nos interesa movernos al Este. 
Solucion correcta
program {if (puedeMover(Este)){
Mover(Este)} }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  Si hay celdas al Este, se mueve
 Si no hay celdas al Este, no hace nada
 ¿Y si hubieramos querido movernos hacia el Norte en caso de que no hubiera celdas al
Este?
 program {if (puedeMover(Este)){
 Mover(Este)}
 Mover(Norte) }

Ejercicio 7: Cómo decirle que no...


En todos los problemas que hicimos hasta ahora, siempre preguntamos si una cierta condición se
cumplía: ¿hay alguna bolita roja? ¿me puedo mover al Este? ¿hay más de 3 bolitas azules?
Algo que también se puede hacer es negar una condición, algo que en castellano puede sonar
medio raro pero que en programación se hace un montón. Los ejemplos anteriores quedarían:
¿no hay alguna bolita roja? ¿no me puedo mover al Este? ¿no hay más de 3 bolitas azules?
¿Y cómo se hace en Gobstones? Fácil, se agrega la palabra clave not antes de la expresión que ya
teníamos.

Original Negada

hayBolitas(Rojo) not hayBolitas(Rojo)

puedeMover(Este) not puedeMover(Este)

nroBolitas(Azul) > 3 not nroBolitas(Azul) > 3

 Definí un procedimiento AsegurarUnaBolitaVerde() que se asegure que en la celda actual


hay al menos una bolita verde. Esto es: si ya hay bolitas verdes no hay que hacer nada,
pero si no hay tendría que poner una.

SOLUCION CORRECTA
procedure AsegurarUnaBolitaVerde(){
if (not hayBolitas(Verde)){
Poner(Verde)
}
}
EXPRESION
COMANDO PALABRA CLAVE
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  Si hay bolitas verdes, no hace nada
 Si no hay bolitas verdes, agrega una
A lo que acabás de hacer, en lógica se lo llama negación y al anteponer el not decimos
que se está negando una expresión. Cualquier expresión booleana (o sea, que
devuelve True o False) se puede negar.

Ejercicio 8: Dos caminos distintos


En lo cotidiano, se presentan muchas situaciones donde debemos elegir entre dos
acciones diferentes, dependiendo de si se cumple una cierta condición o no.
 Si la remera está limpia me la pongo, si no la lavo.
 Si tengo aceite para freir las milanesas lo uso, si no le pongo un poco de manteca.
 Si me puedo mover al Este lo hago, si no me muevo al Norte.
Para estos casos, en Gobstones tenemos una nueva palabra clave que nos ayuda a cumplir
nuestra tarea: el else. En castellano significa si no y hace justamente lo que necesitamos:
ejecuta una serie de acciones si no se cumple la condición que pusimos en el if.
Supongamos que queremos definir un procedimiento que se mueva al Oeste y, en caso de
que no pueda, lo haga hacia el Norte. Haciendo uso del else, podemos definirlo de la
siguiente manera:

procedure MoverComoSea() {
if (puedeMover(Oeste)) {
Mover(Oeste)
} else { Mover(Norte) } }

 ¡Muy bien! Tu solución pasó todas las pruebas


Resultados de las pruebas:
 Si hay celdas al Oeste, se mueve
 Si no hay celdas al Oeste, se mueve al Norte
Ya conocés la herramienta que usan todas las aplicaciones que conociste en tu vida para decidir
qué hacer, el viejo y querido if / if...else.

Ejercicio 9: Un tablero de luces


Como ejemplo final, imaginemos que nuestro tablero está lleno de luces que están
prendidas o apagadas. Vamos a decir que las celdas con una bolita verde están prendidas
y las celdas con una bolita negra están apagadas.
Definí un procedimiento PrenderOApagarLuz() que se encargue de prender las luces que estén
apagadas o apagar las luces encendidas, según corresponda. Tené en cuenta que en cada celda
solo puede haber bolitas de color verde o negro.
 ¡Dame una pista!
Apagar una celda implica dos cosas: sacar una bolita verde y agregar una negra; lo mismo
para prender pero en sentido inverso.
Para saber si está prendida o no, basta con fijarse si hay bolitas del color que querramos.
 Por ejemplo, acá la luz está prendida y al invocar  PrenderOApagarLuz() se apaga:
 En cambio, acá está apagada y con tu procedimiento se prende:
 Recordá que la estructura de la alternativa condicional es la siguiente:

if(condicion) {
//código si se cumple condicion
}
else {
//código si no se cumple condición
}

 El detalle está al momento de hacer el else. Al else lo escribimos sin ninguna


mayúscula y lo que ocurre en este va entre llaves {}. Por ejemplo:

if(condicion)
{
//hacemos algo
}
else
{
//hacemos otra cosa }

SOLUCION CORRECTA
procedure PrenderOApagarLuz(){
if (hayBolitas(Negro))
{ Sacar(Negro)
Poner(Verde)}
Else { Sacar(Verde)
Poner(Negro) } }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  Si la celda está apagada, la prende Si la celda está prendida, la apaga
¡Terminaste Alternativa Condicional!
Ahora conocés una de las herramientas más poderosas que tiene la computación:
la alternativa condicional. Aunque parezca algo sencillo, esto es lo que permite que los
programas puedan reaccionar diferente ante distintos estímulos, dando lugar así a que las
computadoras puedan tomar decisiones.
Algunos ejemplos de esto:
 en las redes sociales, probablemente exista un if que determine si podés ver el
perfil de alguien o no;
 cuando te tomás un colectivo y pagás con la SUBE (o tarjetas similares), la máquina
decide si podés viajar o no dependiendo de si te alcanza el saldo.
Te dejamos como ejercicio pensar (y por qué no intentar escribirlas) qué partes de los
sistemas con los que interactuás todos los días parecerían estar resueltas con un if.

Funciones
Cuando introdujimos la noción de procedimientos, dijimos que:
 son una forma de darle nombre a un grupo de comandos, logrando así que
nuestros programas fueran más entendibles;
 nos posiblitan la división en subtareas: para resolver un problema grande, basta
con dividirlo en problemas más chicos y luego combinarlos;
 nos ayudan a no repetir código, no volver a escribir lo mismo muchas veces.
Al trabajar con expresiones complejas, rápidamente surge la necesidad de contar con un
mecanismo similar, por los motivos que acabamos de esbozar.
¿Querés saber cuál es ese mecanismo? ¡Empecemos! 
Ejercicio 1: Y esto, ¿con qué se come?
Tomate unos pocos minutos y tratá de entender qué hace este procedimiento:
procedure MoverSegunBolitas() {
if (nroBolitas(Azul) + nroBolitas(Negro) + nroBolitas(Rojo) + nroBolitas(Verde) > 10) {
Mover(Este)
} else { Mover(Norte) } }

Cuando lo logres interpretar (o te canses ), presioná Enviar y mirá el resultado.


¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  Si hay más de 10 bolitas, se mueve al Este
Si hay menos de 10 bolitas, se mueve al Norte
Costó entender qué hace sin ejecutarlo, ¿no?
Eso es señal de que nos está faltando dividir en subtareas...

Ejercicio 2: La importancia de nombrar las cosas


Como vimos, el problema de lo anterior era la falta de división en subtareas:
la expresión que cuenta la cantidad de bolitas que hay en la celda es
demasiado compleja, y cuesta entender a simple vista que hace eso.
Entonces, lo que nos está faltando es algún mecanismo para poder darle un nombre a
esa expresión compleja; algo análogo a los procedimientos pero que sirva para
encapsular expresiones.
La buena noticia es que Gobstones nos permite hacer esto, y la herramienta para ello es
definir una función, que se escribe así:

function nroBolitasTotal() {
return (nroBolitas(Azul) + nroBolitas(Negro) + nroBolitas(Rojo) + nroBolitas(Verde)) }

Pegá el código anterior en el editor y observá el resultado.


1. function nroBolitasTotal() {
2. return (nroBolitas(Azul) + nroBolitas(Negro) + nroBolitas(Rojo) + nroBolitas(Verde)) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
 nroBolitasTotal() -> 12
 nroBolitasTotal() -> 5
Algunas aclaraciones sobre las funciones:
 son un caso particular de las expresiones, y por lo tanto siguen las mismas reglas
que ellas: se escriben con la primera letra minúscula y siempre denotan algún
valor (en este caso, un número);

Ejercicio 3: MoverSegunBolitas, versión 2


Ahora que ya logramos mover la cuenta de las bolitas a una subtarea, podemos mejorar el
procedimiento que habíamos hecho antes.
Modificá la primera versión de MoverSegunBolitas para que use la función nroBolitasTotal() en
vez de la expresión larga. (ES LA RESOLUCION DEL EJERCICIO 1)
1. procedure MoverSegunBolitas() {
2. if (nroBolitas(Azul) + nroBolitas(Negro) + nroBolitas(Rojo) + nroBolitas(Verde) > 10)
{
3. Mover(Este)
4. } else {
5. Mover(Norte) } }
Tu solución funcionó, pero hay cosas que mejorar
Objetivos que no se cumplieron:
  MoverSegunBolitas debe utilizar nroBolitasTotal
Resultados de las pruebas:
  Si hay más de 10 bolitas, se mueve al Este
Si hay menos de 10 bolitas, se mueve al Norte
MoverSegunBolitas, versión 2
1. procedure MoverSegunBolitas() {
2. if (nroBolitasTotal() > 10) {
3. Mover(Este)
4. } else {
5. Mover(Norte) } }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  Si hay más de 10 bolitas, se mueve al Este
Si hay menos de 10 bolitas, se mueve al Norte
Las funciones son una herramienta importantísima, que nos ayuda a escribir programas
de mayor calidad.
Sólo mirando el código de esta nueva versión del procedimiento podemos entender de
qué va nuestro problema, lo que reduce la distancia entre el problema real y
la estrategia que elegimos para resolverlo.

Ejercicio 4: todasExcepto
Te toca ahora definir tu primera función: todasExcepto(color). Lo que tiene que hacer es
sencillo, contar cuántas bolitas hay en la celda actual sin tener en cuenta las del color
recibido por parámetro.
Por ejemplo, todasExcepto(Verde) debería contar todas las bolitas azules, negras y rojas que
hay en la celda actual (o dicho de otra forma: todas las bolitas que hay menos las verdes).
Definí la función todasExcepto para que retorne la cantidad de bolitas que no sean del color que se
le pasa por parámetro.
 ¡Dame una pista!
Ya definimos una función para contar todas las bolitas (nroBolitasTotal()) y Gobstones ya
trae una para contar las de un color en particular (nroBolitas(color)).
Sólo te queda pensar cómo combinarlas. 
SOLUCION CORRECTA
1. function todasExcepto(color){
2. return(nroBolitasTotal()- nroBolitas(color)) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  todasExcepto() -> 8
 todasExcepto() -> 10
Las funciones, como cualquier otra expresión, se pueden usar para definir nuevas
funciones.
Y volvemos así a lo más lindo de la programación: la posibilidad de construir nuestras
propias herramientas parándonos sobre cosas que hicimos antes, logrando que lo que
hacemos sea cada vez más poderoso. 
Ejercicio 5: Una función de otro tipo
Como ya sabés, las expresiones no sólo sirven para operar con números. Vamos a definir
ahora una función que retorne un valor booleano (True / False).
Lo que queremos averiguar es si el color Rojo es dominante dentro de una celda. Veamos
algunos ejemplos.
En este casillero:
0

444 33
0 0
22 1

rojoEsDominante() retorna False (hay 2 bolitas rojas contra 8 de otros colores).


Pero en este otro:
0

444 33
0 0
9 1
0

rojoEsDominante() retorna True (hay 9 bolitas rojas contra 8 de otros colores)


Definí la función rojoEsDominante() que nos diga si la cantidad de bolitas rojas es mayor que la
suma de las bolitas de los otros colores. En la Biblioteca está todasExcepto(color) lista para ser
invocada
¡Dame una pista!
Otra forma de pensarlo: el color rojo es dominante cuando el número de bolitas rojas es
mayor que todas excepto las rojas.
Biblioteca
procedure PonerN(n, color) {
repeat(n) {
Poner(color)
}
}
procedure SacarN(n, color) {
repeat(n) {
Sacar(color)
}
}
procedure MoverN(n, dir) {
repeat(n) {
Mover(dir)
}
}
function nroBolitasTotal() {
/*
...
*/
}
/*
Nos dice cuantas bolitas hay si no contamos el color recibido por parámetro. Por ejemplo, si tenemos un
casillero con 1 bolita roja, 2 verdes, 3 negras y 4 azules todasExcepto(Negro) nos retornaría 7 (1 + 2 + 4).
*/
function todasExcepto(color) {
/*
...
*/
}

SOLUCION CORRECTA
1. function rojoEsDominante(){
2. return(nroBolitas(Rojo) > todasExcepto(Rojo)) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
 rojoEsDominante() -> False porque el rojo es menor
 rojoEsDominante() -> True porque el rojo es mayor
Las funciones pueden retornar distintos tipos: un color, una dirección, un número o un
booleano.
Básicamente, lo que diferencia a un tipo de otro son las operaciones que se pueden
hacer con sus elementos: tiene sentido sumar números, pero no colores ni direcciones;
tiene sentido usar Poner con un color, pero no con un booleano. Muchas veces, pensar en
el tipo de una función es un primer indicador útil de si lo que estamos haciendo está bien.

Ejercicio 6: En libertad
Queremos definir la función esLibreCostados(), que determine si el cabezal tiene libertad
para moverse hacia los costados (es decir, Este y Oeste).
Antes que nada, pensemos, ¿qué tipo tiene que denotar nuestra función? Será...
 ... ¿un color? No.
 ... ¿un número? Tampoco.
 ... ¿una dirección? Podría, pero no. Fijate que lo que pide es "saber si puede moverse" y no
hacia dónde.
 ... ¿un booleano? ¡Sí!  Cómo nos dimos cuenta: lo que está pidiendo tiene pinta
de pregunta que se responde con sí o no, y eso es exactamente lo que podemos
representar con un valor booleano: Verdadero o Falso.
Pero, ups, hay un problema más; hay que hacer DOS preguntas: ¿se puede mover al
Este? Y ¿se puede mover al Oeste?. 
Bueno, existe el operador && que sirve justamente para eso: toma dos expresiones booleanas y
devuelve True solo si ambas son verdaderas. Si sabés algo de lógica, esto es lo que comunmente se
denomina conjunción y se lo suele representar con el símbolo ∧.
Por ejemplo, si quisieramos saber si un casillero tiene más de 5 bolitas y el Rojo es el color
dominante podríamos escribir:

nroBolitasTotal() > 5 && rojoEsDominante()

Definí la función esLibreCostados() que indique si el cabezal puede moverse tanto al Este como al


Oeste.
Dame una pista!
¡No te olvides de que existe una función puedeMover(direccion)!
Solucion mia correcta
1. function esLibreCostados(){
2. return(puedeMover(Este)&&puedeMover(Oeste)) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
 esLibreCostados() -> False
esLibreCostados() -> True

Ejercicio 7: Cualquier bolita nos deja bien


Definí la función hayAlgunaBolita() que responda a la pregunta ¿hay alguna bolita en la celda
actual?
Otra vez una pregunta, por lo tanto hay que retornar un booleano. Además, podemos ver
que acá también hay que hacer más de una pregunta, en particular cuatro: una por cada
una de los colores.
A diferencia del ejercicio anterior, lo que queremos saber es si alguna de ellas es
verdadera, por lo tanto hay que usar otro operador: la disyunción, que se escribe
 | | y retorna verdadero si al menos alguna de las dos preguntas es verdadera.
De nuevo, si sabés algo de lógica, esta operación suele representarse con el símbolo ∨.
 ¡Dame una pista!
Recordá que existe la función hayBolitas(color), que indica si hay alguna bolita del color
especificado. Además, el operador | | se puede usar varias veces, como si fuera una
suma: unaCosa | | otraCosa | | otraCosaMas.
SOLUCION CORRECTA
function hayAlgunaBolita(){
return(hayBolitas(Rojo)||hayBolitas(Verde)||hayBolitas(Azul)||hayBolitas(Negro)) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  hayAlgunaBolita() -> False
 hayAlgunaBolita() -> True
  hayAlgunaBolita() -> True
  hayAlgunaBolita() -> True
Tanto && como | | pueden usarse varias veces sin la necesidad de usar paréntesis, siempre
y cuando tengan expresiones booleanas a ambos lados.

Ejercicio 8: Siempre al borde...


Te recordamos los operadores lógicos que vimos hasta ahora:
 Negación: "da vuelta" una expresión booleana - ejemplo: not
hayBolitas(Rojo).
 Conjunción: determina si se cumplen ambas condiciones -
ejemplo: puedeMover(Norte) && puedeMover(Sur).
 Disyunción: determina si se cumple alguna de las condiciones -
ejemplo: esInteligente() || tieneBuenaOnda().
Con la ayuda de esa tablita, definí la función estoyEnUnBorde() que determine si el cabezal está
parado en algún borde.
 ¡Dame una pista!
Si el cabezal está en un borde, no se puede mover en alguna dirección
SOLUCION CORRECTA
1. function estoyEnUnBorde(){
2. return(not puedeMover(Norte) || not puedeMover(Sur) || not puedeMover(Este)
|| not puedeMover(Oeste)) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
  estoyEnUnBorde() -> True El cabezal esta en el borde inferior izquierdo
 estoyEnUnBorde() -> True El cabezal esta en el borde medio derecho
 estoyEnUnBorde() -> False El cabezal esta en el medio de la celda
 Como en la aritmética, en la lógica también existe el concepto de precedencia y ciertas
operaciones se resuelven antes que otras: primero la negación (not), después la
conjunción (&&) y por último la disyunción (||).
 Por esta razón, la expresión not puedeMover(Norte) || not puedeMover(Este) || not
puedeMover(Sur) || not puedeMover(Oeste) se puede escribir sin tener que poner
paréntesis en el medio.

Ejercicio 9: Las compañeras ideales


Vamos a ver ahora funciones que hacen cosas antes de retornar un resultado. Para
ejemplificar esto, vamos a querer que definas una función que nos diga si hay una bolita
de un color específico, pero en la celda de al lado.
Definí la función hayBolitasAl(direccion, color) que informe si hay alguna bolita del color
especificado en la celda vecina hacia la dirección dada.
Ojo: como ya dijimos, la última línea siempre tiene que tener un return.
 ¡Dame una pista!
Acordate que el cabezal sólo puede mirar la celda actual. Por lo tanto, si lo que queremos
es ver si hay bolitas al lado primero habrá que moverse en esa dirección.
SOLUCION CORRECTA
1. function hayBolitasAl(direccion, color){
2. Mover (direccion)
3. return (hayBolitas (color)) }
 ¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
 hayBolitasAl() -> True
hayBolitasAl() -> False
¿Viste qué pasó? El cabezal "no se movió" y sin embargo la función devolvió el resultado
correcto.
Esto pasa porque en Gobstones las funciones son puras, no tienen efecto real sobre el
tablero. En ese sentido decimos que son las compañeras ideales: después de cumplir su
tarea dejan todo como lo encontraron.

Ejercicio 10: Lo ideal también se puede romper


Como en la definición de hayBolitasAl se usa Mover, es obvio que hay casos en los cuales
podría romperse: basta con posicionar el cabezal en el origen y preguntar si hayBolitas de
algún color al Oeste.
Pero, ¿no era que las funciones eran puras y no tenían efecto real? ¿Qué pasa si una
función hace BOOM?
Hagamos la prueba: vamos a probar la función hayBolitasAl del ejercicio anterior con casos donde
no pueda moverse el cabezal. Presioná Enviar y mirá el resultado.
Solución del ejercicio dada por mumuki
1. function hayBolitasAl(direccion, color) {
2. Mover(direccion)
3. return (hayBolitas(color)) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
BOOM

[2:3]: No se puede mover hacia la dirección Sur: cae afuera del tablero.

BOOM

[2:3]: No se puede mover hacia la dirección Oeste: cae afuera del tablero.

¡BOOM! 
Las funciones también pueden producir BOOM y por lo tanto tenés que tener el
mismo cuidado que al programar un procedimiento: que el cabezal no salga del
tablero, no intentar sacar bolitas de un color que no hay, etc.
Pensándolo así, podemos decir que las funciones deshacen sus efectos una vez
que terminan, pero para poder devolver un resultado necesitan que sus acciones
puedan ejecutarse.

Ejercicio 11: ¿Hay bolitas lejos?


Ejercitemos un poco más esto de las funciones con procesamiento.
Te toca programar una nueva versión de hayBolitasAl que mire si hay bolitas a cierta
distancia de la celda actual. A esta función la vamos a llamar hayBolitasLejosAl y recibirá
tres parámetros: una dirección hacia donde deberá moverse, un color por el cual
preguntar y una distancia que será la cantidad de veces que habrá que moverse.
Por ejemplo: hayBolitasLejosAl(Norte, Verde, 4) indica si hay alguna bolita Verde cuatro
celdas al Norte de la posición actual.
Definí la función hayBolitasLejosAl(direccion, color, distancia).
 ¡Dame una pista!
La idea de "mover el cabezal muchas veces" la resolvimos varias lecciones atrás con el
procedimiento MoverN. Podrías usarlo, ¿no? 
SOLUCION CORRECTA
1. function hayBolitasLejosAl(direccion,color,distancia){
2. MoverN(distancia,direccion)
3. return (hayBolitas(color)) }
¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
hayBolitasLejosAl()-> True Aquí esta a distancia hacia el norte la bolita del cabezal
hayBolitasLejosAl()->True Aquí esta la bolita a distancia y el cabezal en el este
hayBolitasLejosAl() -> False
Se puede realizar cualquier tipo de acción antes de retornar un valor, y nada de lo que
hagamos tendrá efecto real sobre el tablero.
Interesante esto de las funciones, ¿no?

Ejercicio 12: Estoy rodeado de viejas bolitas


Valiéndote de hayBolitasAl, definí la función estoyRodeadoDe(color) que indica si el cabezal está
rodeado de bolitas de ese color.
Decimos que el cabezal "está rodeado" si hay bolitas de ese color en las cuatro direcciones:
Norte, Este, Sur y Oeste.  ¡Dame una pista!
Ya tenés una forma de determinar si hay bolitas en una dirección. Combiná eso con el conectivo
lógico que corresponda y tendrás tu nueva función andando. Biblioteca
procedure PonerN(n, color) {
repeat(n) {
Poner(color) } }
procedure SacarN(n, color) {
repeat(n) {
Sacar(color) } }
procedure MoverN(n, dir) {
repeat(n) {
Mover(dir) } }
function hayBolitasAl(direccion, color) {
Mover(direccion)
return (hayBolitas(color)) }

SOLUCION CORRECTA
1. function estoyRodeadoDe(color) {
2. return (hayBolitasAl(Norte, color)&&
3. hayBolitasAl(Este,color)&&
4. hayBolitasAl(Oeste,color)&&
5. hayBolitasAl(Sur, color)) }
 ¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
 estoyRodeadoDe() -> True El cabezal esta en el medio rodeado de bolitas
estoyRodeadoDe() -> False El cabezal esta en el medio solo rodeado en 3 lados
 estoyRodeadoDe() -> False El cabezal esta en el medio solo rodeado en 2 lados
Por si todavía no nos creías: a pesar de que el cabezal se movió cuatro veces por cada prueba, al
finalizar la función vemos que siempre quedó en la posición inicial.

Ejercicio 13: Sin límites


Para cerrar, vamos a definir la función  hayLimite(), que determina si hay algún tipo de
límite a la hora de mover el cabezal.
El límite puede ser por alguno de dos factores: porque estoy en un borde y entonces no
me puedo mover en alguna dirección, o porque estoy rodeado de bolitas rojas que me
cortan el paso. Si ocurre alguna de esas dos condiciones, quiere decir que hay un límite.
Usando estoyEnUnBorde y estoyRodeadoDe, definí hayLimite
 ¡Dame una pista!
Cuidado con el orden de las expresiones: para poder preguntar si está rodeado, primero deberías
chequear si está en un borde.
Biblioteca
procedure PonerN(n, color) {
repeat(n) {
Poner(color) } }
procedure SacarN(n, color) {
repeat(n) {
Sacar(color) } }
procedure MoverN(n, dir) {
repeat(n) {
Mover(dir) } }
function hayBolitasAl(direccion, color) {
Mover(direccion)
return (hayBolitas(color)) }
function estoyRodeadoDe(color) {
return (hayBolitasAl(Norte, color) && hayBolitasAl(Este, color) && hayBolitasAl(Sur, color) &&
hayBolitasAl(Oeste, color)) }
function estoyEnUnBorde() {
return (not puedeMover(Norte) || not puedeMover(Este) || not puedeMover(Sur) || not
puedeMover(Oeste)) }

SOLUCION CORRECTA
1. function hayLimite(){
2. return (estoyEnUnBorde() || estoyRodeadoDe(Rojo)) }
 ¡Muy bien! Tu solución pasó todas las pruebas
Resultados de las pruebas:
 hayLimite() -> True Esta rodeado de rojas y el cabezal en el medio
 hayLimite() -> True No hay ninguna roja y el cabezal en la esquina inferior izquierda
 hayLimite() -> False Esta rodeado de 3 rojas y el cabezal en el medio
¡Terminaste Funciones!
Esta lección fue bastante intensa, aprendiste unas cuantas cosas:
 conociste las funciones, que son una buena forma de nombrar expresiones
compuestas;
 también vimos que se pueden usar funciones para calcular cosas que
necesitan salir de la celda actual y que el efecto desaparece una vez que la
función se ejecuta;
 ejercitamos los conectivos lógicos | | y &&, y vimos que ambos funcionan
con cortocircuito.

Apéndice
Conceptos generales
Programa
Un programa es la descripción de la solución a un problema que puede ser
ejecutada por una computadora. Está compuesto por la secuencia de instrucciones
necesarias para que pueda lograr su cometido. Al ejecutar el programa la
computadora llevará a cabo lo que le indiquen dichas instrucciones. El código
fuente (o simplemente, código) es la forma en que escribimos estas instrucciones
en un lenguaje de programación determinado.
Sintaxis
La sintaxis es el conjunto de reglas de escritura del lenguaje en el que estamos
programando.
Un programa que no cumpla con la sintaxis del lenguaje no va a poder ser
“comprendido” por la computadora. Por ejemplo, la sintaxis establece cuándo usar
mayúsculas o minúsculas, cuándo abrir y cerrar paréntesis, llaves o corchetes, qué
palabras tienen un uso reservado, etc.
Por otro lado, la semántica es el significado de las instrucciones que creamos
respetando la sintaxis del lenguaje, lo que interpreta y puede hacer la computadora
al ejecutar nuestro código.
Paradigma
Un paradigma de programación es un conjunto de conceptos, estrategias y buenas
prácticas que nos brindan un marco para resolver problemas de programación y
comunicar su solución a otras personas.
Lenguaje de programación
Tal como los seres humanos nos comunicamos utilizando distintos lenguajes, para
darle instrucciones a una computadora deberemos hacerlo utilizando algún
lenguaje de programación. Hay muchos y cada uno tiene sus características, reglas
sintácticas, convenciones y aplicaciones. Si bien cuando programamos podemos
encontrar palabras en inglés u otros idiomas, los lenguajes de programación siguen
reglas totalmente diferentes de las de aquellos.
Algunos lenguajes sirven para cualquier tipo de problema (lenguajes de propósito
general) y otros sirven para hacer cosas específicas. Por ejemplo:
o Gobstones es un lenguaje destinado únicamente a enseñar los conceptos
fundamentales de la programación;
o JavaScript es de propósito general y se puede usar en una gran variedad de
contextos;
o SQL es un lenguaje que sólo sirve para expresar soluciones a problemas de
manejo de información en una base de datos.
Computadora
Una computadora es cualquier máquina capaz de almacenar y procesar
información para ayudarnos a resolver problemas. Si bien solemos asociar el
término al clásico aparato con una pantalla y un teclado, los teléfonos inteligentes
(smartphones), televisores inteligentes (smart TV) y tablets también son
computadoras que cumplen funciones más específicas.
Error
Equivocarse es una parte importante de aprender a programar, los errores pueden
enseñarnos más sobre cómo plasmamos nuestras ideas en los programas y sobre
las particularidades del lenguaje elegido. Entre los errores más comunes podemos
encontrar:
o no respetar las reglas sintácticas del lenguaje de programación;
o no resolver el problema para el cual estamos creando nuestro programa;
o realizar operaciones que no están permitidas.
El lenguaje Gobstones
Gobstones es un lenguaje gráfico desarrollado por docentes e investigadores de la
Universidad Nacional de Quilmes, diseñado para la enseñanza de ideas básicas de
programación.
Su representación visual facilita la ejercitación de la abstracción y la
contextualización. Por eso es el lenguaje que elegimos para presentar los
fundamentos de la programación.
El entorno de Gobstones
Tablero
El tablero es una cuadrícula compuesta por celdas. Las distintas tareas que hacen
nuestros programas con las bolitas y el tablero nos permiten representar y resolver
problemas muy variados. Podemos ver su estado antes y después de ejecutar un
programa.
Bolitas
Cada celda del tablero puede contener un número ilimitado de bolitas que pueden
ser de cuatro colores diferentes: Negro, Verde, Rojo y Azul. Las herramientas que
nos provee Gobstones para manipular las bolitas nos permiten lograr diferentes
configuraciones del tablero para modelar situaciones y resolver problemas.
Cabezal
El cabezal es un punto de referencia que se ubica en una celda determinada (la
celda actual), y recibe y ejecuta las instrucciones que operan sobre el tablero y las
bolitas, como ponerlas, sacarlas, contar cuántas hay y moverse desde su celda
actual hacia otras del tablero.
Boom
En Gobstones nos toparemos frecuentemente con el ¡Boom! Esta explosión es una
representación gráfica para avisarnos que le estamos pidiendo a la computadora
que haga algo que no puede resolver, por ejemplo cuando intentamos hacer
movimientos fuera del tablero o sacar bolitas de una celda en la que no las hay.
Palabras reservadas
Los lenguajes cuentan con palabras reservadas para fines específicos, eso significa
que no podemos usarlas en cualquier situación. Algunos ejemplos de Gobstones
son program, procedure y function.
Fundamentos de programación
Programas
Para crear un programa en Gobstones debemos escribir la palabra program en
minúsculas y a continuación, entre llaves { }, sus instrucciones. Los programas en
Gobstones no llevan nombre. program es la palabra reservada de Gobstones para
indicar el comienzo de un programa.

program {
Mover(Este)
Poner(Azul) }

Procedimientos
Los procedimientos son una herramienta de los lenguajes que nos permiten
agrupar dentro de un bloque de código instrucciones que realizan una tarea
específica, y darle un nombre. Nos ayudan a crear programas más claros, porque al
utilizarlos organizamos mejor los pasos que resuelven un problema, describimos
mejor nuestra estrategia y evitamos repetir lógica.
Nombre
Todos los procedimientos llevan un nombre que explica la tarea que realiza. Es
importante que sea un nombre expresivo, es decir, que deje claro lo que hace. De
esa forma, cuando leemos su nombre podemos entender su objetivo a simple vista,
sin necesidad de profundizar en cómo está hecho.
Subtareas
Los procedimientos son una herramienta muy útil para organizar la resolución de
un problema. Identificar y separar las partes que componen un problema nos
permite resolver las pequeñas tareas que lo conforman y, al reunirlas, poder
solucionarlo. Al utilizar procedimientos reconocemos esas tareas, les ponemos
nombre y las resolvemos definiendo un nuevo procedimiento. De esta forma
tenemos el procedimiento creado y a disposición para invocarlo todas las veces
que sean necesarias.
Definición e invocaciones
La definición de un procedimiento es la porción de código que desarrolla la tarea
que resuelve. Esta definición no implica su uso, solo su existencia. Para definir un
procedimiento debemos utilizar la palabra reservada procedure luego el nombre
elegido en con la primera letra de cada palabra en mayúsculas (DeEstaForma)
seguido por paréntesis ( ). Por último, entre llaves, las acciones y comandos que lo
conforman. Por ejemplo:

procedure PonerUnaBolitaDeCadaColor(){
Poner(Rojo)
Poner(Verde)
Poner(Azul)
Poner(Negro) }

En otras palabras, al definir un procedimiento estamos indicando las instrucciones precisas


que ejecutará al ser invocado.
Invocar un procedimiento es utilizarlo. Para hacerlo hay que escribir su nombre y los
paréntesis dentro de un programa, función u otro procedimiento. Al ejecutar un programa
que incluya la invocación a uno o varios procedimientos la computadora podrá ponerlos en
acción. Por ejemplo, si quisieramos invocar el
procedimiento PonerUnaBolitaDeCadaColor dentro de un programa deberíamos escribir:

program {
PonerUnaBolitaDeCadaColor( ) }

El procedimiento no tendrá ningún efecto hasta que no lo invoquemos en al menos un


lugar.
Repetición simple
La repetición es una herramienta de programación que nos permite automatizar la
repetición de alguna tarea indicando el número de veces consecutivas que deseamos
hacerla.
La palabra reservada para la repetición simple es repeat, y va seguida por el número de
repeticiones entre paréntesis y los comandos a repetir entre llaves. Por ejemplo:
program{
repeat(5){
Poner(Azul) } }

Caso borde
Tenemos que prestar atención al orden en el que damos las instrucciones que reúne la
repetición para garantizar que al comienzo y al final (o en los “bordes”) se comporte como
esperamos. Si no consideramos los casos bordes en una repetición en la que combinamos
acciones y movimientos en el tablero es muy probable que pasemos celdas sin completar la
acción deseada o nos salgamos de los márgenes del tablero. Una buena estrategia para
evitarlo es considerar el último caso por fuera de la repetición.
Por ejemplo, queremos colocar una bolita azul en cada celda de este tablero:

Si intentamos repetir la acción de Poner(Azul) y Mover(Este) cuatro veces, en el último paso


vamos a estar fuera de los márgenes del tablero y las instrucciones no serán válidas. Si, en
cambio, repetimos esas acciones tres veces no colocaremos una bolita azul en la última
celda. La solución es hacer tres repeticiones y por fuera de su estructura nos encargamos de
poner la bolita en la última celda:

program{
repeat(3){
Poner(Azul)
Mover(Este)
}
Poner(Azul) }

De esa forma el tablero obtenido será este:

Parámetros y argumentos
Cuando queremos generalizar el comportamiento de un procedimiento para que se adecúe
a distintas situaciones de uso similares, podemos utilizar parámetros. Los parámetros no
tienen un valor determinado en el momento de la definición del procedimiento, pero
pueden tomar potencialmente cualquier valor (siempre que tenga sentido). El parámetro es
un "agujero" que debemos completar con la información particular que necesitemos al
invocar el procedimiento.
Al definir el procedimiento incluiremos entre paréntesis el parámetro (o parámetros) con un
nombre descriptivo en minúsculas. Por ejemplo:
// Este
procedimiento nos permitirá movernos dos celdas en cualquier dirección.
procedure Mover2PasosHaciaEl(dirección) {
Mover(dirección)
Mover(dirección) }

Al invocar el procedimiento tendremos que pasarle un argumento en la misma posición que


el parámetro correspondiente. Cada argumento es el valor específico que reemplazará al
parámetro que le corresponda en cada una de sus apariciones dentro de la definición de la
función o procedimiento invocado.

// Al invocar el procedimiento podemos elegir hacia qué dirección queremos


movernos. Por ejemplo, si quisiéramos un programa que nos permita movernos dos
celdas al Oeste deberíamos hacer:
program {
Mover2PasosHaciaEl(Oeste) }

Alternativa condicional
Cuando un problema presenta escenarios que pueden variar necesitamos algún tipo de
herramienta para evaluar el contexto y tomar decisiones. La alternativa condicional se
ocupa precisamente de esto. ¿Cómo lo hace? Primero evalúa el valor de verdad de una
expresión booleana, que actúa de condición y según se cumpla o no, realiza una acción
determinada. Por ejemplo, tenemos que decidir qué hacer según la cantidad de bolitas de
una celda, si un número es mayor que otro o si llegué a un extremo del tablero.
La alternativa condicional se compone de una estructura de control que utiliza la
palabra if (que significa “si” en inglés). Se escribe if, la condición y entre llaves las acciones
que se deben realizar si la condición se cumple. Si la condición no es verdadera no se van a
ejecutar las instrucciones entre las llaves, sino que el programa seguirá su curso normal con
los comandos fuera de éstas.

program {
if(hayBolitas(Azul)) {
Sacar(Azul)
} // en este caso si hay al menos una bolita azul va a sacar una. }

Si queremos hacer una acción específica cuando no se cumple la condición del if (y solo en
ese caso), podemos utilizar la sentencia else (si no en inglés).

program {
if(hayBolitas(Azul)) {
Sacar(Azul)
} else {
Poner(Azul)
} // en este caso si hay al menos una bolita azul va a sacar una. Si no hay ninguna bolita
azul va a poner una. }

Funciones
Al igual que los procedimientos, las funciones nos permiten dividir el problema en
subtareas para evitar repeticiones y hacer más fácil de entender la solución. La diferencia
entre ambos es que las funciones no describen acciones sino que encapsulan expresiones,
realizan algún tipo de procesamiento con información de su contexto y devuelven un
resultado, sin ningún efecto real sobre el tablero. Lo que retorne la función debería ir entre
paréntesis a continuación de la palabra return.
La definición e invocación de funciones también debe tener un nombre claro y descriptivo
que indique con precisión qué hace y cuál será el resultado que nos retornará esa función.
A diferencia de los procedimientos, las funciones se definen utilizando la
palabra function y su nombre con la primera letra de cada palabra en mayúsculas a
excepción de la primera (deEstaForma). Por ejemplo:

function nroBolitasTotal() {
return (nroBolitas(Azul) + nroBolitas(Negro) + nroBolitas(Rojo) + nroBolitas(Verde)) }

Procedimientos primitivos
Poner(color)
Pone una bolita del color indicado en la casilla actual. Ejemplo:

program {
Poner(Rojo) // pone una bolita roja en la casilla actual. }

Sacar(color)
Saca una bolita del color indicado de la casilla actual. Ejemplo:

program {
Sacar(Negro) // saca una bolita negra de las que hay en la casilla actual. }

Mover(direccion)
Mueve el cabezal indicador de la casilla actual un paso hacia la dirección indicada.
Ejemplo:

program {
Mover(Este) // mueve el cabezal una vez hacia el Este. }

IrAlBorde(direccion)
Lleva el cabezal todo lo que se puede hacia la dirección indicada. Ejemplo:

program {
IrAlBorde(Norte) // mueve el cabezal de la celda actual a la última celda en la dirección
Norte. }

VaciarTablero( )
Saca todas las bolitas del tablero, dejando el cabezal en la posición en la que
estaba. Ejemplo:

program {
VaciarTablero() // En un tablero con alguna o muchas bolitas, las saca todas. }
Funciones primitivas
nroBolitas(color)
Retorna un número: la cantidad de bolitas del color indicado que hay en la casilla
actual. Ejemplo, asumiendo la siguiende celda actual:

nroBolitas(Rojo) // retorna 4

opuesto(direccion)
Retorna una dirección: la dirección opuesta a la provista. Ejemplo:

opuesto(Norte) // retorna Sur

opuesto(numero)
Retorna un número: el original, negado. Ejemplo:

opuesto(59) // Retorna -59

siguiente(direccion)
Retorna una dirección: la siguiente a la recibida como argumento, es decir, la
próxima en sentido horario. Ejemplo:

siguiente(Oeste) // retorna Norte


siguiente(Norte) // retorna Este
siguiente(Este) // retorna Sur
siguiente(Sur) // retorna Oeste

previo(direccion)
Retorna una dirección: la anterior a la recibida como argumento, es decir, la
próxima en sentido anti horario. Ejemplo:

previo(Sur) // retorna Este

hayBolitas(color)
Retorna un booleano: es verdadero cuando en la casilla actual hay al menos una
bolita del valor indicado. Ejemplo, asumiendo la siguiende celda actual:

hayBolitas(Rojo) // retorna verdadero


hayBolitas(Verde) // retorna falso
puedeMover(direccion)
Retorna un booleano: es verdadero cuando el cabezal puede moverse en esa
dirección (o sea, no está en el borde). Por ejemplo, estando el cabezal en la esquina
de abajo a la izquierda:

puedeMover(Norte) // retorna verdadero


puedeMover(Oeste) // retorna falso

Bibliografía complementaria
o http://www.gobstones.org/bibliografia/Libros/
BasesConceptualesProg.pdf
Capítulo 2: Programación Imperativa
¿Ya estás para salir del tablero? ¡Acompañanos a aprender más sobre programación
imperativa y estructuras de datos de la mano del lenguaje JavaScript!

Funciones y tipos de datos


¡Hola! Quizás no te diste cuenta pero ya tenés las bases de la programación: ya sabés
declarar funciones y procedimientos, usar variables, tomar decisiones empleando la
estructura de control if, hacer tareas múltiples veces. 

Ejercicio 1: Introducción a JavaScript


¿Ya te cansaste de jugar con bolitas de colores?  Tenemos una buena noticia. En este capítulo
vamos a aprender programación imperativa de la mano de uno de los lenguajes de programación
más utilizados de la industria del software: JavaScript.

Ejercicio 2: Funciones, definición


Gobstones y JavaScript tienen mucho en común. Por ejemplo, en ambos lenguajes podemos
definir funciones y usarlas muchas veces.
Sin embargo, como siempre que aprendas un lenguaje nuevo, te vas a topar con un pequeño
detalle: tiene una sintaxis diferente . La buena noticia es que el cambio no será tan terrible como
suena, así que veamos nuestra primera función JavaScript:

function doble(numero) {
return 2 * numero; }

Diferente, pero no tanto. Si la comparás con su equivalente Gobstones...


function doble(numero) {
return (2* numero) }
...notarás que los paréntesis en el return no son necesarios, y que la última línea la

terminamos con ; .
Veamos si se va entendiendo: definí ahora la función mitad, que tome un número por
parámetro y retorne su mitad. Tené en cuenta que el operador de división en JavaScript es /.
 ¡Dame una pista!
Para saber la mitad de un número debemos dividirlo por 2. ¡Cuidado!  No es lo mismo
hacer un número dividido 2 que 2 dividido ese número. Por ejemplo:

10/2
5
2/10
0.2

CONSOLA
function mitad(numero){ return(1/2*numero)}
=> <function>
Solución
function mitad(numero){ return(1/2*numero)}
¡Muy bien! Tu solución pasó todas las pruebas
Perfecto, ¿viste que no era tan terrible? 
Si no le pusiste ; al final de la sentencia habrás visto que funciona igual. De todas formas ponelo, ya
que de esa manera evitamos posibles problemas.
function mitad(numero){ return(1/2*numero); }
Siempre que aprendamos un lenguaje nuevo vamos a tener que aprender una nueva sintaxis. Sin
embargo y por fortuna, si tenés los conceptos claros, no es nada del otro mundo .
Aprendamos ahora a usar estas funciones.

Ejercicio 3: Funciones, uso


¿Y esto con qué se come? Digo, ehm.... ¿cómo se usan estas funciones? ¿Cómo hago para pasarles
argumentos y obtener resultados?
Basta con poner el nombre de la función y, entre paréntesis, sus argumentos. ¡Es igual que en
Gobstones!

doble(3)

Y además podemos usarlas dentro de otras funciones. Por ejemplo:

function doble(numero) {
return 2 * numero; }
function siguienteDelDoble(numero) {
return doble(numero) + 1; }

O incluso mejor:

function doble(numero) {
return 2 * numero; }
function siguiente(numero) {
return numero + 1; }
function siguienteDelDoble(numero) {
return siguiente(doble(numero)); }

Veamos si se entiende; definí las siguientes funciones:


anterior: toma un número y devuelve ese número menos uno
triple: devuelve el triple de un número
anteriorDelTriple, que combina las dos funciones anteriores: multiplica a un número por 3
y le resta 1
 SOLUCION CORRECTA
 function anterior(numero) {
 return numero - 1; }
 function triple(numero) {
 return numero * 3; }
 function anteriorDelTriple(numero) {
 return (anterior(triple(numero))); }
¡Muy bien! Tu solución pasó todas las pruebas
Quizás ahora estés pensando: si no tengo un tablero, ¿cómo sé si mi función hace lo que
debe? Acompañanos...

Ejercicio 4: Probando funciones


Quizás ya lo notaste pero, junto al editor, ahora aparece una solapa nueva: la consola.
La consola es una herramienta muy útil para hacer pruebas rápidas sobre lo que estás
haciendo: te permite, por ejemplo, probar expresiones, funciones que vengan con
JavaScript, o incluso funciones que vos definas en el editor.
La podés reconocer fácilmente porque arranca con el chirimbolito , que se llama prompt.
Para entender mejor cómo funciona, en qué puede ayudarnos y algunos consejos sobre su
uso, te recomendamos mirar este video
Veamos si se entiende, probá en la consola las siguientes expresiones:
 4+5
 Math.round(4.5)
 funcionMisteriosa(1, 2, 3) (ya la definimos por vos y la podés usar)
Veamos si se entiende, probá en la consola las siguientes expresiones:
  4 + 5
=> 9
  Math.round(4.5)
=> 5
  funcionMisteriosa(1, 2, 3)
=> 9   

Ejercicio 5: Haciendo cuentas


Además de los operadores matemáticos +, -, / y *, existen muchas otras
funciones matemáticas comunes, algunas de las cuales ya vienen con JavaScript y
están listas para ser usadas.
Sin embargo, la sintaxis de estas funciones matemáticas es apenitas diferente de lo
que veníamos haciendo hasta ahora: hay que prefijarlas con  Math.. Por ejemplo, la
función que nos sirve para redondear un número es  Math.round:

function cuantoSaleAproximadamente(precio, impuestos) {


return Math.round(precio * impuestos); }

Probá en la consola las siguientes expresiones:


Math.round(4.4)
Math.round(4.6)
Math.max(4, 7)
Math.min(4, 7)
CONSOLA
  Math.round(4.4)
=> 4
  Math.round(4.6)
=> 5
  Math.max(4, 7)
=> 7
  Math.min(4, 7)
=> 4

Ejercicio 6: Poniendo topes


Hagamos un alto en nuestro camino y miremos las funciones Math.max y Math.min,
que nos pueden ahorrar más trabajo del que parece.
Necesitamos una función que diga cuánta plata queda en tu cuenta (que tiene un cierto saldo) si
extráes un cierto monto:

// el saldo es $100, el monto a extraer, $30


extraer(100, 30)
70 //quedan $70 ($100 - $30 = $70)

Pero como no queremos quedarnos en negativo, si el monto a extraer es mayor al saldo,


nuestro saldo debe quedar en cero.

extraer(100, 120)
0 //Ups, quisimos sacar más plata de la que teníamos. //Nos quedamos con $0

Como ves, esto es casi una resta entre saldo y monto, con la salvedad de que estamos
poniendo un tope inferior: no puede dar menos de cero .
En otras palabras (¡preparate!, esto te puede volar la cabeza ) extraer devuelve el
máximo entre la resta saldo - monto y 0.
¿Te animás a completar la solución que está en el editor?
Solución correcta
1. function extraer(saldo, monto) {
2. return Math.max(saldo - monto, 0); }
¡Muy bien! Tu solución pasó todas las pruebas ¡Bien hecho! Ahora andá y probalo en
la consola 
Consola
  extraer(200, 150)
=> 50
  extraer(50, 100)
=> 0
   

Ejercicio 7: Libros de la buena memoria


¡Veamos más operadores! Dani ama el primer día de cada mes , y por eso definió
esta función...

function esDiaFavorito(diaDelMes) {
return diaDelMes = = = 1 ; }

...y la usa así (y la dejó en la biblioteca para que la pruebes):

esDiaFavorito(13)
false
esDiaFavorito(1)
true

Como ves, en JavaScript contamos con operadores como ===, >=, >, <,<= que nos dicen si dos
valores son iguales, mayores-o-iguales, mayores, etc. Los vamos a usar bastante .
¡Ahora te toca a vos! Dani también dice que a alguien leGustaLeer, cuando la cantidad de libros que
recuerda haber leído es mayor a 20. Por ejemplo:

leGustaLeer(15)
false
leGustaLeer(45)
true

Definí y probá en la consola la función leGustaLeer


SOLUCION MIA CORRECTA
function leGustaLeer(librosLeidos) {
return librosLeidos >= 20 ; }
Consola
 leGustaLeer(20)
=> true
  leGustaLeer(80)
=> true
  leGustaLeer(15)
=> false
¡Muy bien! Tu solución pasó todas las pruebas
Capaz pasó desapercibido, pero leGustaLeer devuelve true o false, es decir, es una función
que devuelve booleanos. Eso significa que en JavaScript, no sólo hay números sino que
también..... hay booleanos

Ejercicio 8: Booleanos
Ahora miremos a los booleanos con un poco más de detalle:
 Se pueden negar, mediante el operador !: !hayComida
 Se puede hacer la conjunción lógica entre dos booleanos (and, también conocido
en español como y lógico), mediante el operador &&: hayComida && hayBebida
 Se puede hacer la disyunción lógica entre dos booleanos (or, también conocido en
español como o lógico), mediante el operador ||: unaExpresion ||
otraExpresion
Veamos si se entiende; definí las siguientes funciones:
estaEntre, que tome tres números y diga si el primero es mayor al segundo y menor al
tercero.
estaFueraDeRango: que tome tres números y diga si el primero es menor al segundo o
mayor al tercero
Ejemplos:

estaEntre(3, 1, 10)
true
estaEntre(90, 1, 10)
false
estaEntre(10, 1, 10)
false
estaFueraDeRango(17, 1, 10)
true

SOLUCION CORRECTA
function estaEntre(num1, num2, num3){
return(num1 > num2 && num1 < num3) }
function estaFueraDeRango(num1, num2, num3){
return(num1 > num2 && num1 > num3) }
CONSOLA
 true
  estaEntre(40, 30, 60)
=> true
  estaFueraDeRango(3, 1, 10)
=> false
  estaFueraDeRango(60, 40, 30)
=> true
 ¡Muy bien! Tu solución pasó todas las pruebas
Ya fueron suficientes booleanos y cuentas por ahora, ¿no? Exploremos algo más
interesante: los strings.
Ejercicio 9: Palabras, sólo palabras
Muchas veces queremos escribir programas que trabajen con texto : queremos saber
cuántas palabras hay en un libro, o convertir minúsculas a mayúsculas, o saber en qué
parte de un texto está otro.
Para este tipo de problemas tenemos los strings, también llamados cadenas de
caracteres:
 "Ahora la bebé tiene que dormir en la cuna"
 'El hierro nos ayuda a jugar'
 "¡Hola Miguel!"
Como se observa, todos los strings están encerrados entre comillas simples o dobles. ¡Da
igual usar unas u otras! Pero sé consistente: por ejemplo, si abriste comilla doble, tenés
que cerrar comilla doble. Además, un string puede estar formado por (casi) cualquier
carácter: letras, números, símbolos, espacios, etc.
¿Y qué podemos hacer con los strings? Por ejemplo, compararlos, como a cualquier otro
valor:

"hola" === "Hola"


false
"todo el mundo" === "todo el mundo"
true

Veamos si queda claro: definí la función esFinDeSemana que tome un string que represente el


nombre de un día de la semana, y nos diga si es "sábado" o "domingo".

esFinDeSemana("sábado")
true
esFinDeSemana("martes")
false

 ¡Dame una pista!


Para saber si un día es fin de semana, ese día tiene que ser "sábado" o ese día
tiene que ser "domingo". Recordá que el "o lógico" opera booleanos, no
strings. 
SOLUCION MIA CORRECTA
function esFinDeSemana(feriado){
return feriado === "sabado" || feriado === "domingo" }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 esFinDeSemana ("sabado")
=> true
  esFinDeSemana ("lunes")
=> false
  esFinDeSemana ("domingo")
=> true

Ejercicio 10: Operando strings


¿Y qué podemos hacer con los strings, además de compararlos? ¡Varias cosas! Por ejemplo,
podemos preguntarles cuál es su cantidad de letras:

longitud("biblioteca")
10
longitud("babel") 5

O también podemos concatenarlos, es decir, obtener uno nuevo que junta dos strings:

"aa" + "bb"
"aabb"
"sus anaqueles " + "registran todas las combinaciones"
"sus anaqueles registran todas las combinaciones"

O podemos preguntarles si uno comienza con otro:

comienzaCon("una página", "una")


true
comienzaCon("la biblioteca", "todos los fuegos")
false

Veamos si queda claro: definí la función longitudNombreCompleto, que tome un nombre y un


apellido, y retorne su longitud total, contando un espacio extra para separar a ambos:

longitudNombreCompleto("Cosme", "Fulanito") 14

Biblioteca
function longitud(unString) /* ... */
// Retorna cuan largo es un string
//
// Por ejemplo:
//// longitud("hola")
// 4
function convertirEnMayuscula(unString) /* ... */
// Convierte una palabra en mayúsculas
//
// Por ejemplo:
// convertirEnMayuscula("hola")
// "HOLA"
function comienzaCon(unString, otroString) /* ... */
// Retorna un booleano que nos dice si unString empieza con otroString
//
// Por ejemplo:
// comienzaCon("hola todo el mundo", "hola todo")
// true

SOLUCION CORRECTA
function longitudNombreCompleto(nombre,apellido){
return longitud(nombre+apellido+1) } ESTE MAS 1 ES POR EL ESPACIO DE SEPARACION
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 longitudNombreCompleto("juan" , "perez")
=> 10

Ejercicio 11: ¡GRITAR!


Una conocida banda, para agregar gritos varios a su canción, nos pidió definir la
función gritar, que toma un string y lo devuelve en mayúsculas y entre signos de
exclamación.
Por ejemplo:

gritar("miguel")
"¡MIGUEL!"
gritar("benito")
"¡BENITO!"

Definí la función gritar. Te dejamos para que uses la función convertirEnMayuscula, que,


ehm... bueno... básicamente convierte en mayúsculas un string 
 ¡Dame una pista!
Tené en cuenta que los signos de admiración "¡" y "!" (al igual que los espacios y otros
signos de puntuación) son strings y que los strings se pueden concatenar usando el
operador +. Por ejemplo:

"todo" + "terreno"
"todoterreno"
"¿" + "Aló" + "?"
"¿Aló?"

BIBLIOTECA

function longitud(unString) /* ... */


// Retorna cuan largo es un string
//// Por ejemplo:
//// longitud("hola")
// 4
function convertirEnMayuscula(unString) /* ... */
// Convierte una palabra en mayúsculas
//// Por ejemplo:
//// convertirEnMayuscula("hola")
// "HOLA"
function comienzaCon(unString, otroString) /* ... */
// Retorna un booleano que nos dice si unString empieza con otroString
//// Por ejemplo:
//// comienzaCon("hola todo el mundo", "hola todo") // true

Esto se usa de la biblioteca para resolver el ejercicio


SOLUCION CORRECTA
function gritar(nombre){
return "¡"+ convertirEnMayuscula (nombre)+ "!"; }
¡Muy bien! Tu solución pasó todas las pruebas
Consola
gritar("pepe")
=> "¡PEPE!"
  gritar("bandera")
=> "¡BANDERA!"

Ejercicio 12: ¿Y qué tal si...?


Ninguna introducción al lenguaje JavaScript estaría completa sin mostrar al menos una estructura
de control que ya conocemos: la alternativa condicional. Veamos un ejemplo:

//Equivalente a Math.abs
function valorAbsoluto(unNumero) {
if (unNumero >= 0) {
return unNumero; } else { return -unNumero; } }

Veamos si se entiende: definí la función maximo, que funcione como Math.max (¡no vale usarla!) y


retorne el máximo entre dos números. Por ejemplo, el máximo entre 4 y 5 es 5, y el máximo entre
10 y 4, es 10
BIBLIOTECA

/**/
function longitud(unString) /* ... */
// Retorna cuan largo es un string
// Por ejemplo:
//
// longitud("hola")
// 4
function convertirEnMayuscula(unString) /* ... */
// Convierte una palabra en mayúsculas
// Por ejemplo:
//
// convertirEnMayuscula("hola")
// "HOLA"
function comienzaCon(unString, otroString) /* ... */
// Retorna un booleano que nos dice si unString empieza con otroString
// Por ejemplo:
//
// comienzaCon("hola todo el mundo", "hola todo")
// true

SOLUCION CORRECTA MIA


function maximo(numero1, numero2) {
if (numero1 >numero2) {
return numero1;
} else {
return numero2; } }  ¡Muy bien! Tu solución pasó todas las pruebas
USE COMO EJEMPLO LA ALTERNATIVA CONDICIONAL DE LA ESTRUCTURA DE CONTROL
CONSOLA
maximo(50, 30)
=> 50
  maximo(30, 50)
=> 50
  maximo(10, 60)=> 60
OTRA RESPUESTA CORRECTA DADA POR EJERCICIOS RESUELTO POR LA PAGINA
function maximo(numero1, numero2) {
   if (numero1 >= numero2) {
     return numero1;
   } else {
     return numero2;    } }

Ejercicio 13: ¿De qué signo sos?


Ya utilizamos la alternativa condicional para realizar una acción específica cuando se cumple una
condición y para cuando debemos elegir entre dos acciones diferentes (según se cumpla o no).
Pero... ¿Si necesitamos más de dos alternativas?  Veamos un ejemplo:
Agus se olvida siempre de como tiene que cuidar sus plantas , por eso definió la
función cuidadoSegun(dia) que le recuerda que los lunes tiene que fertilizarlas, los viernes las tiene
que fumigar y el resto de los días las tiene que regar.

function cuidadoSegun(dia) {
if (dia === "lunes") {
return "fertilizar";
} else if (dia === "viernes") {
return "fumigar";
} else {
return "regar"; } }

¡Ahora te toca a vos! Definí la función signo, que dado un número nos retorne:
1 si el número es positivo
0 si el número es cero
-1 si el número es negativo
  ¡Dame una pista!
 Un número es positivo cuando es mayor a 0 y negativo cuando es menor a 0.
 SOLUCION CORRECTA
function signo(numero) { if (numero > 0) return 1; else {if (numero === 0) return 0;
else if (numero < 0) return -1; }}
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 14: El retorno del booleano


Para cerrar, ahora que ya vimos cómo escribir la alternativa condicional, es momento de un
pequeño recordatorio: si usás adecuadamente las expresiones booleanas, ¡no es necesario utilizar
esta estructura de control!
Supongamos que queremos desarrollar una función esMayorDeEdad, que nos diga si alguien tiene
18 años o más. Una tentación es escribir lo siguiente:

function esMayorDeEdad(edad) {
if (edad >= 18) {
return true;
} else {
return false; } }

Sin embargo, este if es totalmente innecesario, dado que la expresión edad > = 18 ya es


booleana:

function esMayorDeEdad(edad) {
return edad >= 18; }

Mucho más simple, ¿no? 


Para Ema un número es de la suerte si:
es positivo, y
es menor a 100, y
no es el 15.
Definí la función esNumeroDeLaSuerte que dado un número diga si cumple la lógica anterior.
¡No vale usar if!
SOLUCION CORRECTA
function esNumeroDeLaSuerte(numero){
return (numero > 0) && (numero < 100) && (numero !== 15); }
¡Muy bien! Tu solución pasó todas las pruebas
En general, como regla práctica, si tenés ifs que devuelven trues o falses, probablemente lo
estás haciendo mal . Y si bien funcionará, habrás escrito código innecesariamente complejo
y/o extenso.
Recordá: ¡menos código, más felicidad! 
CONSOLA
50
  110
=> 110
  15
=> 15
  -40
=> -40

Ejercicio 15: Los premios


El jurado de un torneo nos pidió la función medallaSegunPuesto  que retorne la medalla que
le corresponde a los primeros puestos, según la siguiente lógica:
 primer puesto: le corresponde "oro"
 segundo puesto: le corresponde "plata"
 tercer puesto: le corresponde "bronce"
 otros puestos: le corresponde "nada"
Ejemplo:

medallaSegunPuesto(1)
"oro"
medallaSegunPuesto(5)
"nada"

Definí, y probá en la consola, la función medallaSegunPuesto


Solución correcta
function medallaSegunPuesto(numero){
if (numero === 1)return ("oro") else {
if (numero === 2)return ("plata") else if (numero === 3)return ("bronce") else return
("nada"); } }

function medallaSegunPuesto(lugar){
if(lugar === 1){
return("oro")
} else if(lugar === 2) {
return("plata")
} else if(lugar === 3) {
return("bronce")
} else {
return("nada") } }
Muy bien! Tu solución pasó todas las pruebas
CONSOLA
medallaSegunPuesto(1)
=> "oro"
  medallaSegunPuesto(3)
=> "bronce"
  medallaSegunPuesto(5)
=> "nada"

Ejercicio 16: Tipos de datos


Como acabamos de ver, en JavaScript existen números, booleanos y strings:
Ejempl
Tipo de dato Representa Operaciones
o
Números cantidades 4947 +, -, *, %, <, etc
Boolean valores de verdad true &&, !, etc
Strings texto "hola" longitud, comienzaCon, etc
Además, existen operaciones que sirven para todos los tipos de datos, por ejemplo:
 ===: nos dice si dos cosas son iguales
 !==: nos dice si dos cosas son diferentes
Es importante usar las operaciones correctas con los tipos de datos correctos, por ejemplo, no
tiene sentido sumar dos booleanos o hacer operaciones booleanas con los números.  Si usas
operaciones que no corresponden, cosas muy raras y malas pueden pasar. 
Probá en la consola las siguientes cosas:
5 + 6 (ok, los números se pueden sumar)
5 === 6 (ok, todas las cosas se pueden comparar)
8 > 6 (ok, los números se pueden ordenar)
!true (ok, los booleanos se pueden negar)
false / true (no está bien, ¡los booleanos no se pueden dividir!)
 CONSOLA
 => false
   8 > 6
 => true
   !true
 => false
   false / true
 => 0
    

Ejercicio 17: Datos de todo tipo


Uff, ¡vimos un montón de cosas!  Aprendimos sobre la sintaxis de las funciones
en JavaScript, los tipos de datos y sus operaciones, e incluso conocimos uno
nuevo: los strings.
¡Pero no tan rápido! Antes de terminar un último desafío: ¿Cuál es el valor de las
siguientes expresiones? ¡Marcá todas las correctas!
 SOLUCION CORRECTA
 4 + 4 vale 8

 "4" + "4" vale "44"

 4 + 4 vale "44"

 "on" + "ce" vale "once"

 true && false vale false

 true && false vale 0

 5 >= 6 vale false

 ! true vale fals

 ¡La respuesta es correcta!
Práctica Funciones y Tipos de Datos
Ejercicio 1: Comprando Hardware
Queremos comprar una computadora nueva, y nos gustaría saber cuánto nos va a salir.
Sabemos que:
 Los monitores cuestan $60 por cada pulgada
 La memoria cuesta $200 por cada GB
 El precio base estimado del resto de los componentes es de $1000
Definí la función cuantoCuesta que tome el número de pulgadas del monitor y la cantidad de
memoria, y calcule el costo estimado de nuestra computadora.

cuantoCuesta(25, 8)
4100

SOLUCION CORRECTA
function cuantoCuesta( pulgada, memoria) {
return (pulgada * 60 + memoria * 200 + 1000) }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
cuantoCuesta(25, 8)
=> 4100

Ejercicio 2: ¿Me conviene?


Ahora que sabemos cuantoCuesta una computadora, queremos saber si una
computadora me conviene. Esto ocurre cuando:
 sale menos de $6000, y
 tiene al menos un monitor de 32 pulgadas, y
 tiene al menos 8GB de memoria
Definí la función meConviene, que nuevamente tome el número de pulgadas y cantidad de
memoria y nos diga si nos conviene comprarla :

meConviene(25, 8)
false // porque el monitor es demasiado chico
meConviene(42, 12)
true // cumple las tres condiciones

En la Biblioteca ya está definida la función cuantoCuesta lista para ser invocada.


 ¡Dame una pista!
¿Y cómo invoco cuantoCuesta? Pasándole como primer argumento el número de pulgadas y
como segundo la cantidad de memoria.
SOLUCION CORRECTA
function meConviene (pulgadas,memoria){
return cuantoCuesta(pulgadas, memoria) < 6000 && (memoria>=8) &&
(pulgadas>=32); }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
meConviene(25,8)
=> false
  meConviene(35,8)
=> true
  meConviene(35,12)
=> true
  meConviene(35,6)
=> false

Ejercicio 3: Triangulos
¡Hora de hacer un poco de geometría! Queremos saber algunas cosas sobre un triángulo:
 perimetroTriangulo: dado los tres lados de un triángulo, queremos saber cuánto
mide su perímetro.
 areaTriangulo: dada la base y altura de un triángulo, queremos saber cuál es su
área.
Definí las funciones perimetroTriangulo y areaTriangulo
SOLUCION MIA CORRECTA
function perimetroTriangulo(lado1, lado2, lado3){
return lado1 + lado2 + lado3; }
function areaTriangulo(base, altura){
return (base * altura)/ 2; }
 ¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 perimetroTriangulo(20, 10, 5)
=> 35
  areaTriangulo(6, 2)
=> 6

Ejercicio 4: Cuadrados
Y ahora es el turno de los cuadrados; queremos saber
 perimetroCuadrado: dado un lado, queremos saber cuánto mide su
perímetro.
 areaCuadrado: dado un lado, queremos saber cuál es su area
Definí las funciones perimetroCuadrado y areaCuadrado
 ¡Dame una pista!
 Recordá que:
 el perímetro de un cuadrado se calcula como 4 veces su lado;
 el área de un cuadrado se calcula como su lado multiplicado por sí mismo.
SOLUCION MIA CORRECTA
function perimetroCuadrado(lado){
return lado * 4; }
function areaCuadrado(lado){
return lado * lado; }
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 5: ¿Está afinado?


Cuando presionamos una tecla de un piano, éste produce un sonido que tiene una
cierta frecuencia. Y cuando presionamos el la central del piano, si está afinado, vamos a
escuchar una nota cuya frecuencia es 440Hz.
Definí una función estaAfinado, que reciba la frecuencia (un número) del la central, y
retorne si dicha frecuencia es igual a 440Hz.

estaAfinado(440) true

SOLUCION MIA CORRECTA


function estaAfinado(frecuencia){
return frecuencia === 440; }
¡Muy bien! Tu solución pasó todas las pruebas
Consola
  estaAfinado(440)
=> true
  estaAfinado(560)
=> false
   

   Ejercicio 6: ¿Está cerca?


Ahora queremos saber si el la central del piano está cerca de estar afinado. Esto ocurre
cuando está entre 437Hz y 443Hz, pero NO es exactamente 440Hz. Por ejemplo:

estaCerca(443)
true //está en el rango 437-443
estaCerca(442)
true //ídem caso anterior
estaCerca(440)
false //está en el rango,
//pero es exactamente 440
estaCerca(430)
false //está fuera del rango

Definí la función estaCerca
SOLUCION CORRECTA
function estaCerca(frecuencia){
return frecuencia>=437 && frecuencia<=443 && frecuencia!==440; }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
estaCerca(450)
=> false
  estaCerca(420)
=> false
  estaCerca(439)
=> true

Ejercicio 7: Cartelitos
Para una importante conferencia, el comité organizador nos pidió que escribamos
cartelitos identificatorios que cada asistente va a tener.
SOLUCION CORRECTA
function escribirCartelito(titulo, nombre, apellido){
return(titulo + " " + nombre + " " + apellido) }
 ¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 escribirCartelito('licenciado','juan','Perez')
=> "licenciado juan Perez"
Ejercicio 8: Más Cartelitos
Ah, ¡pero no tan rápido! Algunas veces en nuestro cartelito  sólo queremos el título y el
apellido, sin el nombre. Por eso ahora nos toca mejorar nuestra función escribirCartelito de
forma que tenga 4 parámetros:
1. el título;
2. el nombre;
3. el apellido;
4.  un booleano que nos indique si queremos un cartelito corto con sólo título y
apellido, o uno largo, como hasta ahora.
Modificá la función escribirCartelito, de forma que se comporte como se describe arriba. Ejemplo:

// cartelito corto
escribirCartelito("Lic.", "Tomás", "Peralta", true)
"Lic. Peralta"

// cartelito largo
escribirCartelito("Ing.", "Dana", "Velázquez", false)
"Ing. Dana Velázquez"

solución mia correcta


function escribirCartelito (titulo,nombre,apellido,esCorto){
if (esCorto ){
return (titulo +" " + apellido)
}else {
return (titulo+" "+nombre+" "+apellido);
}
}
¡Muy bien! Tu solución pasó todas las pruebas

Es la alternativa condicional Es un booleano


Aquí nos dice que la solución funciono pero hay cosas que mejorar
escribirCartelito hace comparaciones booleanas innecesarias en amarillo

function escribirCartelito (titulo,nombre,apellido,esCorto){


if (esCorto === true ){
return (titulo +" " + apellido)
}else {
return (titulo+" "+nombre+" "+apellido); } }
Es pq se le agrego === true Comparando a un booleano
Ejercicio 9: Cartelitos óptimos
Ahora que ya podemos escribir nuestros cartelitos identificatorios grandes y chicos,
queremos una nueva función que nos dé el cartelito de tamaño óptimo:
 si nombre y apellido tienen, en total, más de 15 letras, queremos un cartelito corto;
 de lo contrario, queremos un cartelito largo.
Definí la función escribirCartelitoOptimo que tome un título, un nombre y un apellido, y
utilizando escribirCartelito genere un cartelito corto o largo, según las reglas anteriores.
Ejemplo:

escribirCartelitoOptimo("Ing.", "Carla", "Toledo")


"Ing. Carla Toledo"
escribirCartelitoOptimo("Dr.", "Estanislao", "Schwarzschild")
"Dr. Schwarzschild"

 Te dejamos en la biblioteca la función escribirCartelito definida. ¡Usala cuando necesites!


 ¡Dame una pista!
Recordá que contás con las funciones longitud y escribirCartelito, no tenés que
definirlas solo invocarlas. 
SOLUCION CORRECTA MIA
function escribirCartelitoOptimo(titulo, nombre, apellido){
return escribirCartelito(titulo, nombre, apellido, quiereCartelCorto(nombre,
apellido)) }
function quiereCartelCorto(nombre, apellido){
return longitud(nombre+apellido)> 15 }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 escribirCartelitoOptimo("Licenciado", "Pedro", "Perez")
=> "Licenciado Pedro Perez"
TAMBIEN ESTA BIEN ESTA RESPUESTA
function escribirCartelitoOptimo(titulo, nombre, apellido){
return escribirCartelito(titulo, nombre, apellido, longitud(nombre+apellido)> 15) }
CONSOLA
 escribirCartelitoOptimo("licenciado", "Juan", "Perez")
=> "licenciado Juan Perez"
Ejercicio 10: Cara o ceca
Hay veces en las que tenemos difíciles decisiones que tomar en nuestras vidas (como por ejemplo, si
comer pizzas o empanadas ), y no tenemos más remedio que dejarlas libradas a la suerte.
Es allí que tomamos una moneda y decimos: si sale cara, comemos pizzas, si no, empanadas.
Definí una función decisionConMoneda, que toma tres parámetros y retorna el segundo si el primero
es "cara", o el tercero, si sale "ceca". Por ejemplo:

decisionConMoneda("cara", "pizzas", "empanadas")


"pizzas"

SOLUCION CORRECTA
function decisionConMoneda(moneda, comida1, comida2) {
if (moneda === "cara") {
return comida1; } else {
return comida2; } }
Muy bien! Tu solución pasó todas las pruebas
OTRA SOLUCION CORRECTA
function decisionConMoneda(moneda, pizzas, empanadas) {
if (moneda === "cara") {
return pizzas; } else {
return empanadas; } }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
ecisionConMoneda("cara", "pizzas", "empanadas")
=> "pizzas"
  decisionConMoneda("seca", "pizzas", "empanadas")
=> "empanadas"

Ejercicio 11: ¡Envido!


Queremos saber el valor de las cartas de truco cuando jugamos al envido. Sabemos
que:
 todas las cartas del 1 al 7, inclusive, valen su numeración
 las cartas del 10 al 12, inclusive, valen 0
 no se juega con 8s ni con 9s
Definí una función valorEnvido, que tome un número de carta y retorne su valor de envido.

valorEnvido(12)
0
valorEnvido(3) 3

 ¡Dame una pista!


 Asumí que sólo te vamos a pasar cartas con valores dentro de los rangos del 1 al 7 y del
10 al 12
SOLUCION CORRECTA
function valorEnvido(numero) {
if(numero >= 1 && numero <= 7){
return numero;}
else if(numero >= 10 && numero <= 12) {
return 0; } }
¡Muy bien! Tu solución pasó todas las pruebas
OTRA SOLUCION CORRECTA
function valorEnvido(numero){
if(numero >= 1 && numero <= 7)
{return numero}
if(numero >= 10 && numero <= 12) {return 0} }
¡Muy bien! Tu solución pasó todas las pruebas
function valorEnvido(carta){
  if(carta >= 1 && carta <= 7)
    {return carta}
  if(carta >= 10 && carta <= 12)
    {return 0} }

Ejercicio 12: ¡Quiero retruco!


Bueno, ehm, no, pará, primero queremos calcular cuántos puntos de envido suma un jugador.
Sabemos que:
 Si las dos cartas son del mismo palo, el valor del envido es la suma de sus valores de envido
más 20.
 De lo contrario, el valor del envido es el mayor valor de envido entre ellas.
Utilizando la función valorEnvido (que ya definimos por vos), definí la
función puntosDeEnvidoTotales que tome los valores y palos de dos cartas y diga cuánto envido
suman en total. Ejemplo:

puntosDeEnvidoTotales(1, "espadas", 4, "espadas")


25
puntosDeEnvidoTotales(2, "copas", 3, "bastos")
3

 ¡Dame una pista!


Recordá que para obtener el número más grande entre dos números tenemos la función  Math.max.
SOLUCION CORRECTA
function puntosDeEnvidoTotales (numero1, palo1, numero2, palo2){
if (palo1 === palo2) {
return valorEnvido(numero1) + valorEnvido(numero2) + 20;}
else {return Math.max(valorEnvido(numero1), valorEnvido(numero2))
} }
¡Muy bien! Tu solución pasó todas las pruebas
function puntosDeEnvidoTotales(n1, p1, n2, p2){
  if(p1 === p2){
    return (valorEnvido(n1) + valorEnvido(n2) + 20)
  }else{
    return Math.max(n1, n2)   } }

Ejercicio 13: ¡Quiero vale cuatro!


Cuando se juega al truco, los equipos oponentes alternativamente pueden subir la
apuesta. Por ejemplo, si un jugador canta truco, otro jugador puede cantarle retruco.
Obviamente, los puntos que están en juego son cada vez mayores:

Canto Puntos en juego

truco 2

retruco 3

vale
4
cuatro

Definí la función valorCantoTruco, que tome el canto y retorne cuántos puntos vale.

valorCantoTruco("retruco")
3

 Asumí que sólo te van a pasar como argumento un string que represente un canto de
truco. Por ejemplo, no vamos a probar la función para el
caso valorCantoTruco("zaraza")
SOLUCION CORRECTA
function valorCantoTruco(canto){
if(canto==="truco"){return 2;}
else if(canto==="retruco"){return 3;}
if(canto==="vale cuatro"){return 4;} }
¡Muy bien! Tu solución pasó todas las pruebas
function valorCantoTruco(canto){
  if(canto === "truco"){
    return(2)   }
  if(canto === "retruco"){
    return(3)   }
  if(canto === "vale cuatro"){
    return(4)   } }
¡Terminaste Práctica Funciones y Tipos de Datos!
¡Seguinos para aprender nuevos conceptos de la programación imperativa! 

Variables y procedimientos Ejercicio 1: ¿Y el tablero?


Hasta ahora en esta película hay un gran personaje que está faltando: el tablero. Seguro
está por aparecer, de forma triunfal y rimbombante..., ¿no?
No. En JavaScript, lamentamos informarte, no hay tablero.
 Bueeeno, no llores, quizás fuimos un poco duros: en JavaScript no hay tablero, ¡porque no
lo necesitás!  Suceden dos cosas:
1. El tablero nos servía para ver lo que nuestro programa hacía y qué resultados
generaba. Nos permitía también observar los cambios de estado a causa del
programa. Pero ahora ya tenemos experiencia suficiente como para lanzarnos a
programar sin tener que "ver" lo que sucede.
2. Ahora contamos con la consola: una herramienta poderosa que nos permite hacer
pruebas más detalladas y flexibles.
¿No nos creés? Te presentamos un desafío: usando la consola, decí con tus propias
palabras qué hace la función funcionMisteriosa, que recibe dos números enteros como
argumentos. ¡Vas a ver que podés averiguarlo sin tener un tablero!
funcionMisteriosa(5, 2)
=> "woooooww!"
  funcionMisteriosa(2, 1)
=> "woow!"
  funcionMisteriosa(2, 5)
=> "woowwwww!"

Ejercicio 2: Impresión por pantalla


Ahora que ya te convencimos de que no necesitamos al tablero, vamos a mostrarte que sí hay algo
parecido en JavaScript : la impresión por pantalla. Veamos un ejemplo:

function funcionEgocentrica() {
imprimir("soy una función que imprime por pantalla");
imprimir("y estoy por devolver el valor 5");
return 5; }

Probá funcionEgocentrica en la consola.
funcionEgocentrica()
soy una función que imprime por pantalla
y estoy por devolver el valor 5
=> 5
  

Ejercicio 3: Martin Fierro


¿Qué acabamos de hacer con esto? Al igual que Poner(bolita), imprimir es una funcionalidad que
siempre está disponible. Si llamamos a la función anterior, veremos que, además de devolver el
valor 5, imprime dos líneas:

soy una función que imprime por pantalla


y estoy por devolver el valor 5

Sin embargo, sólo podemos escribir strings y, una vez que escribimos en la pantalla, no
hay vuelta atrás: no hay forma de retroceder o deshacer.
Veamos si va quedando claro, definí la function versosMartinFierro que imprima por pantalla los
primeros versos del Martín Fierro:

Aquí me pongo a cantar


Al compás de la vigüela;
Que el hombre que lo desvela
Una pena extraordinaria

Esta function debe retornar 0
 ¡Dame una pista!
Ah, ¿y cómo se hace para imprimir varias líneas? ¡Llamando a imprimir varias veces !

imprimir("esta es una linea");


imprimir("esta es otra linea");

SOLUCION CORRECTA
function versosMartinFierro() {
imprimir("Aquí me pongo a cantar");
imprimir("Al compás de la vigüela;");
imprimir("Que el hombre que lo desvela");
imprimir("Una pena extraordinaria");
return 0; }
 ¡Muy bien! Tu solución pasó todas las pruebas
Sin embargo, ¿tiene sentido que versosMartinFierro devuelva 0? ¿Usamos para algo este
resultado? 
Acá parecería que llamamos a esta function porque nos interesa su efecto de imprimir
líneas; nos da igual lo que retorna. Quizás más que una función, necesitamos definir un
procedimiento. ¿Se podrá hacer esto en JavaScript?
La respuesta, ¡en el siguiente ejercicio!
CONSOLA
  versosMartinFierro()
Aquí me pongo a cantar
Al compás de la vigüela;
Que el hombre que lo desvela
Una pena extraordinaria
=> 0

Ejercicio 4: ¿Y los procedimientos?


En el ejercicio anterior, construiste una function que se ejecutaba con el sólo fin de imprimir
por pantalla. Y por ello, tuvimos que devolver un valor cualquiera. ¿No te huele mal?
Además, hagamos memoria: cuando queremos reutilizar código, podíamos declarar:
 funciones, que siempre retornan algo y no producen ningún efecto
 procedimientos, que no retornan nada, y producen efectos
Entonces versosMartinFierro, no es una función... ¡sino un procedimiento! ¿Cómo se
declaran procedimientos en JavaScript?
¡De la misma forma que las funciones!: usando la palabra clave function.

function versosMartinFierro() {
imprimir("Aquí me pongo a cantar");
imprimir("Al compás de la vigüela;");
imprimir("Que el hombre que lo desvela");
imprimir("Una pena extraordinaria"); }

Envía esta nueva versión de versosMartinFierro


function versosMartinFierro( ) {
imprimir("Aquí me pongo a cantar");
imprimir("Al compás de la vigüela;");
imprimir("Que el hombre que lo desvela");
imprimir("Una pena extraordinaria"); }
Muy bien! Tu solución pasó todas las pruebas
Esto puede ser un poco perturbador : JavaScript no diferencia funciones de
procedimientos: todos pueden tener efectos y todos pueden o no tener retorno.
Vos sos responsable de escribir una function que tenga sentido y se comporte o bien como
un procedimiento (sin retorno y con efecto) o bien como una función (con retorno y sin
efecto).
Si empezás a mezclar funciones con retornos y efecto, funcionará, pero tu código se volverá de a
poco más difícil de entender. Esto nos va a pasar mucho en JavaScript: que puedas hacer algo no
significa que debas hacerlo.

Ejercicio 5: ¿Y el program?
Ahora bien, más allá de que podamos consultar el resultado de una función a través de la
consola, también aprendimos anteriormente que los programas tienen un punto de
entrada: el program. ¿Dónde quedó?
La respuesta es tan simple como sorprendente: en JavaScript todo lo que escribamos fuera
de una function será, implícitamente, dicho punto de entrada. Por ejemplo, si queremos un
programa que imprime por pantalla el clásico "Hola, mundo!", lo podremos escribir así:

imprimir("Hola, mundo!");

O si queremos un programa que tire tres veces los dados e imprima sus resultados,
podemos escribirlo así:

imprimir("Tirando dados");
imprimir("La primera tirada dio " + tirarDado());
imprimir("La segunda tirada dio " + tirarDado());
imprimir("La tercera tirada dio " + tirarDado());

Copiá y enviá este programa


imprimir("Tirando dados");
imprimir("La primera tirada dio " + tirarDado( ));
imprimir("La segunda tirada dio " + tirarDado( ));
imprimir("La tercera tirada dio " + tirarDado( ));
¡Muy bien! Tu solución pasó todas las pruebas
¿Ooooups, y el resultado? ¿Dónde está lo que imprimimos por pantalla? ¿Es que nuestro
programa no anduvo?
No, para nada, es que simplemente no te estamos mostrando lo que sale por pantalla .
¿Por qué? ¿Porque somos malvados? Bueno, quizás en parte , pero tenemos además una
buena razón: cuando escribís programas reales, es muy, muy frecuente que no sea fácil ver
lo que el imprimir imprime, por decenas de motivos. Entonces, como rara vez vas poder
ver a tiempo lo que se imprime en la pantalla, terminan siendo una técnica poco útil.
Moraleja: en los ejercicios que quedan, no uses imprimir salvo que te lo pidamos
explícitamente.
¡Nos vemos en el próximo ejercicio!
CONSOLA
 tirarDados()
Tirando dados
La primera tirada dio 5
La segunda tirada dio 4
La tercera tirada dio 5
tirarDados(); ^
ReferenceError: tirarDados is not defined

Ejercicio 6: Coerciones
Volvamos un momento al código anterior. ¿Notás algo extraño en esta expresión?

"La primera tirada dio " + primeraTirada

Utilizamos el operador + de una forma diferente, operando un string y un número, y lo que hizo fue
concatenar al string con la representación textual del número. Es decir que:
 si operamos dos números con +, se suman
 si operamos dos strings con +, se concatenan
 si operamos un string y un número +, se convierte implícitamente el número a string, y
luego se concatenan, al igual que antes
En JavaScript, estas conversiones implícitas, también llamadas coerciones, ocurren mucho.
¡Quizás incluso más de lo que nos gustaría! 
Veamos si queda claro, definí una función elefantesEquilibristas, que tome un número de elefantes
y devuelva una rima de una conocida canción:

elefantesEquilibristas(3)
"3 elefantes se balanceaban"
elefantesEquilibristas(462)

"462 elefantes se balanceaban"


SOLUCION CORRECTA
function elefantesEquilibristas(numero){
return(numero + " " + "elefantes se balanceaban"); }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
elefantesEquilibristas(5)
=> "5 elefantes se balanceaban"

Ejercicio 7: El círculo de la vida


En programación buscamos que resolver nuestros problemas usando… programas . Y entre
los problemas que casi nadie quiere resolver están los matemáticos. Sobre todo aquellos
que aparecen números como pi con infinitos decimales imposibles de recordar. 
Considerando al número pi igual a 3.14159265358979 (no es infinito pero lo
suficientemente preciso para nuestros cáculos):
Definí las funciones perimetroCirculo y areaCirculo que reciben el radio de un círculo y a
partir del mismo nos retornan su perímetro y su área.
 ¡Dame una pista!
El perímetro se calcula como dos veces pi por el radio de un círculo. El área, en cambio, es
el resultado de hacer pi por radio por radio.
SOLUCION CORRECTA
function perimetroCirculo(radio){
return 3.14159265358979*(radio*2); }
function areaCirculo(radio){
return 3.14159265358979*(radio*radio); }
OTRA SOLUCION CORRECTA
function perimetroCirculo(radio){
return(3.14159265358979 * 2 * radio) }
function areaCirculo(radio) {
return(3.14159265358979 * radio * radio) }
¡Muy bien! Tu solución pasó todas las pruebas
Excelente, la precisión de nuestros cálculos es innegable , pero tuvimos que escribir un número
larguísimo. Pensemos que pi aparece en un montón de fórmulas matemáticas. ¿Es necesario escribir
este número cada vez?¿No podemos hacer algo más cómodo? 
CONSOLA
 perimetroCirculo(20,3.14159265358979)
=> 125.6637061435916
  areaCirculo(20, 3.14159265358979)
=> 1256.637061435916

Ejercicio 8: PIenso que así es más fácil


Por suerte existe una herramienta que va a simplificar nuestra tarea de ahora en adelante:
las variables. 
Las variables nos permiten nombrar y reutilizar valores. Similar a cómo los procedimientos
y funciones nos permiten dar nombres y reutilizar soluciones a problemas más pequeños.
Por ejemplo, si hacemos...

let primerMes = "enero"

...estamos asignándole el valor "enero" a la variable primerMes. En criollo, estamos dándole


ese valor a la variable. 
Cambiá los lugares donde aparece 3.14159265358979 por la variable pi en las funciones que
tenemos definidas.
SOLUCION DADA POR MUMUKI A MODIFICAR POR LO PEDIDO
let pi = 3.14159265358979;
function perimetroCirculo(radio){
return(3.14159265358979 * 2 * radio) }
function areaCirculo(radio) {
return(3.14159265358979 * radio * radio) }
SOLUCION MODIFICADA CORRECTA
let pi = 3.14159265358979;
function perimetroCirculo(radio){
return(pi * 2 * radio) }
function areaCirculo(radio) {
return(pi * radio * radio) }
¡Muy bien! Tu solución pasó todas las pruebas
¡Excelente! Gracias a la variable pi no tuvimos que escribir el número cada vez que teníamos que
usarlo y ¡nuestro programa quedó mucho más entendible! 
 Ejercicio 9: Esto no tiene valor
Ya que vas entendiendo cómo se asignan las variables, te traemos algo para pensar: ¿qué pasa si
intento usar una variable a la que nunca le asigné un valor? 
Tenemos esta función definida:

function sumaSinSentido() {
return numero + 8; }

Probala en la consola y fijate qué sucede.


CONSOLA
  15
=> 15
  15 + 8
=> 23
  2 + 8
=> 10

Ejercicio 10: Variables globales


Entonces, ¿es necesario darle valor a nuestras variables antes de usarlas?
¡Sí!  Cuando declarás una variable tenés que darle un valor inicial, lo cual se conoce
como inicializar la variable.
¡Y sorpresa! Podemos declarar variables tanto directamente en el programa, como dentro
de una function:

function cuentaExtravagante(unNumero) {
let elDoble = unNumero * 2;
if (elDoble > 10) {
return elDoble;
} else {
return 0; } }

Las variables declaradas dentro de una function, conocidas como variables locales, no


presentan mayor misterio. Sin embargo, hay que tener un particular cuidado: sólo se
pueden utilizar desde dentro de la function en cuestión. Si quiero referenciarla desde un
programa:

let elCuadruple = elDoble * 4;

Kaboom, ¡se romperá! 


Sin embargo, las variables declaradas directamente en el programa, conocidas
como variables globales, pueden ser utilizadas desde cualquier function. Por ejemplo:

let pesoMaximoEquipajeEnGramos = 5000;


function puedeLlevar(pesoEquipaje) {
return pesoEquipaje <= pesoMaximoEquipajeEnGramos; }

Veamos si queda claro: definí una función ascensorSobrecargado, que toma una cantidad
de personas y retorna si entre todas superan la carga máxima de 300 kg.
Tené en cuenta que nuestra función va a utilizar dos variables globales:
pesoPromedioPersonaEnKilogramos, la cual ya está declarada,
cargaMaximaEnKilogramos que vas a tener que declarar.
SOLUCION CORRECTA
let cargaMaximaEnKilogramos=300
function ascensorSobrecargado(personas){
return
(pesoPromedioPersonaEnKilogramos*personas)>=cargaMaximaEnKilogramos; }
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 11: La buena fortuna


Las variables no serían tan interesantes si no se pudieran modificar. Afortunadamente,
JavaScript nos da nuevamente el gusto y nos lo permite:
function pasarUnDiaNormal() {
diasSinAccidentesConVelociraptores = diasSinAccidentesConVelociraptores + 1 }
function tenerAccidenteConVelociraptores() {
diasSinAccidentesConVelociraptores = 0; }

¡Ahora vamos a hacer algo de dinero !


Definí el procedimiento aumentarFortuna que duplique el valor de la variable
global pesosEnMiBilletera. No declares la variable, ya lo hicimos por vos (con una cantidad
secreta de dinero) .
 ¡Dame una pista!
¿Cómo usar aumentarFortuna? Por ejemplo así:

// Consulto la varible a ver cuanto tiene:


pesosEnMiBilletera
500 // ¡Ojo! Esto es a fines ilustrativos;
// ¡podría tener cualquier cantidad!
// Aumento mi fortuna:
aumentarFortuna()
// Consulto de nuevo mi fortuna:
pesosEnMiBilletera // ¡Aumentó! 1000

SOLUCION CORRECTA
function aumentarFortuna (){
pesosEnMiBilletera = pesosEnMiBilletera * 2;
}
¡Muy bien! Tu solución pasó todas las pruebas
Actualizaciones como duplicar, triplicar, incrementar en uno o en una cierta cantidad son
tan comunes que JavaScript presenta algunos atajos:

x += y; //equivalente a x = x + y;
x *= y; //equivalente a x = x * y;
x -= y; //equivalente a x = x - y;
x++; //equivalente a x = x + 1;

¡Usalos cuando quieras! 


CONSOLA
pesosEnMiBilletera
=> 0
  aumentarFortuna()
=> undefined
  pesosEnMiBilletera // ¡aumento!
=> 0

Ejercicio 12: ¿Y esto cuánto vale?


Vimos que una variable solo puede tener un valor, entonces cada vez que le asignamos
uno nuevo, perdemos el anterior. Entonces, dada la función:
function cuentaExtravagante() {
let numero = 8;
numero *= 2;
numero += 4;
return numero; }

¿Qué retorna cuentaExtravagante?

"numero"
8
16
20
true
 ¡La respuesta es correcta!

¡Terminaste Variables y procedimientos!


A lo largo de esta lección hiciste muchas cosas nuevas:
 imprimiste por pantalla; 
 conociste los procedimientos en el lenguaje JavaScript, que si bien se definen igual
que las funciones son bien distintos; 
 utilizaste los dos tipos de variables, locales y globales, y aprendiste sus diferencias. 

Ejercicio 1: ¡Que el último apague la luz!


Empecemos por algo sencillo, ¿te acordás del operador ! ? Se lo denomina negación, not o
complemento lógico y sirve para negar un valor booleano.
Si tengo el booleano representado por tieneHambre, el complemento será !tieneHambre.
¿Y esto para qué sirve?  Por ejemplo, para modelar casos de alternancia como prender y
apagar una luz :

let lamparaPrendida = true;


function apretarInterruptor() {
lamparaPrendida = !lamparaPrendida; }
¡Ahora te toca a vos!
Definí el procedimiento usarCierre para que podamos abrir y cerrar el cierre de una mochila.
SOLUCION CORRECTA
let mochilaAbierta = true;
function usarCierre (){
mochilaAbierta = !mochilaAbierta; }
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 2: Negar no cuesta nada


Por el momento no parece una idea muy interesante, pero nos puede servir para reutilizar
la lógica de una función que ya tenemos definida.
Por ejemplo, si contamos con una función esPar, basta con negarla para saber si un
número es impar.

function esImpar(numero) {
return !esPar(numero); }

¡Ahora te toca a vos! Definí esMayorDeEdad, que recibe una edad, y luego esMenorDeEdad a


partir de ella.
SOLUCION CORRECTA
function esMayorDeEdad (edad){
return edad >= 18; }
function esMenorDeEdad(edad){
return(!esMayorDeEdad(edad)); }
 ¡Muy bien! Tu solución pasó todas las pruebas
Cada una de las funciones representa un estado de dos posibles: ser mayor o ser menor de edad.
No se puede ser ambos al mismo tiempo y tampoco se puede evitar pertenecer a alguno de los dos
grupos. Es decir, ¡siempre sos uno u otro! 
Por eso decimos que son complementarios y que juntos forman el conjunto universal.

Ejercicio 3: Los peripatéticos


Otro de los operadores con el que ya te encontraste es la conjunción lógica (también llamada  y
lógico, o and por su nombre en inglés), que sólo retorna verdadero cuando todas las expresiones
que opera son verdaderas.
Podemos encadenar varias de ellas mediante el operador && y alcanza con que sólo una de ellas
sea falsa para que toda la expresión resulte falsa.
Por ejemplo, si cuento con la función:

function esCantanteProlifico (cdsEditados, recitalesRealizados, graboAlgunDVD) {


return cdsEditados >= 10 && recitalesRealizados > 250 && graboAlgunDVD; }

y tenemos un cantante que no grabó un DVD, entonces no se lo considera prolífico, incluso aunque


haya editado más de 10 CDs y dado más de 250 recitales.
Definí una función esPeripatetico que tome la profesión de una persona, su nacionalidad y la cantidad de
kilómetros que camina por día. Alguien es peripatético cuando es un filósofo griego y le gusta pasear
(camina más de 2 kilómetros por día). Ejemplo:

esPeripatetico("filósofo", "griego", 5)
true
esPeripatetico("profesor", "uruguayo", 1)
false

SOLUCION CORRECTA
function esPeripatetico(profesion, nacionalidad, kilometrospordia){
return profesion === "filosofo" && nacionalidad === "griego" && kilometrospordia > 2;
}
 ¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 esPeripatetico("contador", "ruso", 3)
=> false
  esPeripatetico("filosofo", "griego", 3)
=> true
   

Ejercicio 4: La verdad detrás de la conjunción


En la lógica booleana, se puede definir el comportamiento de un operador con una  tabla
de verdad donde A y B son las expresiones o valores de verdad a ser operados y el
símbolo ^ representa la conjunción. Cada celda tiene una V si representa verdadero o F si
representa falso.
Por ejemplo, supongamos que una casa consume poca energía si se usa el aire
acondicionado a 24 grados y tiene al menos 5 lamparitas bajo consumo.
Podemos representar las expresiones de la siguiente forma:
 A: En la casa se usa el aire acondicionado a 24 grados
 B: La casa tiene al menos 5 lamparitas bajo consumo
 A ^ B: La casa consume poca energía
Como indicamos, la casa consume poca energía (A^B) cuando tanto A como B son
verdaderos. Esto se puede representar mediante la siguiente tabla de verdad:
A B A^B
V V V
V F F
F V F
F F F
En el mundo de la lógica estas expresiones se llaman proposiciones. Pero… ¿qué cosas
pueden ser una proposición?  Sólo hace falta que porten un valor de verdad, es decir,
cualquier expresión booleana puede ser una proposición.
¿No nos creés? Probá en la consola la función consumePocaEnergia, que recibe una temperatura y
una cantidad de lamparitas, y comprobá si se comporta como en la tabla:
 consumePocaEnergia(24, 5)
 consumePocaEnergia(24, 0)
 consumePocaEnergia(21, 7)
 consumePocaEnergia(18, 1)

 consumePocaEnergia(24,5)
=> true
  consumePocaEnergia(24, 0)
=> false
  consumePocaEnergia(21, 7)
=> false
  consumePocaEnergia(18, 1)
=> false
Ejercicio 5: ¡Juguemos al T.E.G.!
¿Y si basta con que una de varias condiciones se cumpla para afirmar que una expresión es
verdadera? Podemos utilizar otro de los operadores que ya conocés, ¡la disyunción lógica! 
Recordá que se lo representa con el símbolo | | y también se lo conoce como el
operador or.
En el famoso juego T.E.G., un jugador puede ganar de dos formas: cumpliendo su objetivo
secreto o alcanzando el objetivo general de conquistar 30 países.

function gano(cumplioObjetivoSecreto, cantidadDePaisesConquistados) {


return cumplioObjetivoSecreto || cantidadDePaisesConquistados >= 30; }

Probá en la consola las siguientes expresiones:


 gano(true, 25)
 gano(false, 30)
 gano(false, 20)
 gano(true, 31)
¿Te animás a construir la tabla de verdad de la disyunción lógica?
CONSOLA
 gano(true, 25)
=> true
  gano(false, 30)
=> true
  gano(false, 20)
=> false
  gano(true, 31)
=> true

Ejercicio 6: Y ahora... ¿quién podrá ayudarnos?


Nuestra amiga Dory  necesitaba hacer algunos trámites en el banco, pero cuando llegó
notó que estaba cerrado. 
Para evitar que le ocurra nuevamente, vamos a definir una función que ayude a la gente
despistada como ella.
Sabemos que el banco está cerrado cuando:
 Es feriado, o
 Es fin de semana, o
 No estamos dentro del horario bancario.
La función dentroDeHorarioBancario ya la definimos por vos: recibe un horario  (una hora en
punto que puede ir desde las 0 hasta las 23) y nos dice si está comprendido en la franja de
atención del banco.
Definí las funciones esFinDeSemana y estaCerrado.
SOLUCION CORRECTA
function esFinDeSemana(dia) {
return dia === "sabado" || dia === "domingo" }
function estaCerrado(esFeriado, dia, horario) {
return esFeriado || !dentroDeHorarioBancario(horario) || esFinDeSemana(dia) }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 estaCerrado ("sabado")
=> "sabado"
  esFinDeSemana("sabado")
=> true
  esFinDeSemana("martes")
=> false
Ejercicio 7: ¡Buen día!
Sabemos que el seguimiento de árboles genealógicos puede tornarse complicado cuando
hay muchas personas y relaciones involucradas.
Por ejemplo, en la familia Buendía ocurre que:
 Arcadio es hijo de José Arcadio y de Pilar Ternera
 Aureliano José es hijo del Coronel Aureliano y Pilar Ternera
 Aureliano Segundo y Remedios son hijos de Arcadio y Sofía De La Piedad
Para empezar a analizar esta familia, ya definimos las funciones madreDe y padreDe:
CONSOLA

padreDe(aurelianoJose)
"Coronel Aureliano"
madreDe(aurelianoSegundo)
"Sofía De La Piedad"

Ahora te toca a vos definir la función sonMediosHermanos. Recordá que los medios hermanos
pueden compartir madre o padre pero no ambos porque... ¡en ese caso serían hermanos ! 
 ¡Dame una pista!
Quizás te sirva definir las funciones tienenLaMismaMadre y tienenElMismoPadre.
SOLUCION CORRECTA
function tienenLaMismaMadre (hijo1, hijo2 ) { return madreDe (hijo1) === madreDe (hijo2); }
function tienenElMismoPadre ( hijo1, hijo2 ) { return padreDe ( hijo1 ) === padreDe ( hijo2 ); }
function sonMediosHermanos ( hijo1 , hijo2 ) {
return tienenLaMismaMadre ( hijo1 , hijo2 ) && !tienenElMismoPadre ( hijo1 , hijo2 ) || !
tienenLaMismaMadre ( hijo1 , hijo2 ) && tienenElMismoPadre ( hijo1 , hijo2 ); }
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 8: La verdad es que no hay una verdad


Ahora pensemos cómo sería la tabla de verdad que representa el comportamiento de la
función que acabás de hacer.
Las proposiciones serán tienenLaMismaMadre y tienenElMismoPadre, y los valores de verdad
que porten dependerán de qué dos personas estén evaluando.
El booleano final resultará de operarlas mediante sonMediosHermanos:

tienen la misma madre tienen el mismo padre son medios hermanos


true true false

true false true

false true true

false false false

Probá tu función sonMediosHermanos con los siguientes valores y comprobá si se comporta


como la tabla:
 sonMediosHermanos(arcadio, aurelianoJose)
 sonMediosHermanos(aurelianoSegundo, remedios)
 sonMediosHermanos(aurelianoJose, remedios)
 ¡Dame una pista!
Recordá que en la familia Buendía:
 Arcadio es hijo de José Arcadio y de Pilar Ternera
 Aureliano José es hijo del Coronel Aureliano y Pilar Ternera
 Aureliano Segundo y Remedios son hijos de Arcadio y Sofía De La Piedad
 sonMediosHermanos(arcadio, aurelianoJose)
 => true
   sonMediosHermanos(aurelianoSegundo, remedios)
 => false
   sonMediosHermanos(aurelianoJose, remedios)
 => false

Ejercicio 9: ¡Hola! Mi nombre es Xor


Ahora cambiemos las funciones tienenLaMismaMadre y tienenElMismoPadre por
proposiciones genéricas A y B. Además, representemos la operación que
realiza sonMediosHermanos con el símbolo ⊻. Lo que obtenemos es... ¡una nueva tabla! 
A B A⊻B

V V F

V F V

F V V

F F F

Este comportamiento existe como un operador dentro de la lógica y se lo denomina xor o


disyunción lógica excluyente.
A diferencia del and, or y not, el xor no suele estar definido en los lenguajes.  Sin embargo,
ahora que sabés cómo funciona, si alguna vez lo necesitás podés definirlo a mano. 
SOLUCION CORRECTA
function xor(a, b){
return (a && !b)||(!a && b); }
OTRA SOLUCION CORRECTA
function xor(a, b){
  return(a !== b) }
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 10: Precedencia


Cuando una expresión matemática tiene varios operadores, sabemos que las
multiplicaciones y divisiones se efectuarán antes que las sumas y las restas:

5 * 3 + 8 / 4 - 3 = 14

Al igual que en matemática, cuando usamos operadores lógicos las expresiones se evalúan
en un orden determinado llamado precedencia.
¿Cuál es ese orden? ¡Hagamos la prueba!
Teniendo definida la siguiente función, según la cual las tarjetas de débito
ofrecen una única cuota, y las de crédito, seis:

function pagaConTarjeta(seCobraInteres, tarjeta, efectivoDisponible) {


return !seCobraInteres && cuotas(tarjeta) >= 3 | | efectivoDisponible < 100;
}

o logico
Probala en la consola con los valores:
 pagaConTarjeta(true, "crédito", 320)
 pagaConTarjeta(false, "crédito", 80)
 pagaConTarjeta(true, "débito", 215)
 pagaConTarjeta(true, "débito", 32)

 pagaConTarjeta(true, "crédito", 320)
=> false
  pagaConTarjeta(false, "crédito", 80)
=> true
  pagaConTarjeta(true, "débito", 215)
=> false
  pagaConTarjeta(true, "débito", 32)
=> true

Ejercicio 11: Un ejercicio sin precedentes


Si prestaste atención a la función anterior, habrás notado que la operación con
mayor precedencia es la negación !, seguida de la conjunción && y por último la
disyunción | |. ¿Pero qué pasa si quiero alterar el orden en que se resuelven? 
Al igual que en matemática, podemos usar paréntesis para agrupar las operaciones
que queremos que se realicen primero.
Definí la función puedeJubilarse que recibe la edad y el sexo de una persona, además de los
años de aportes jubilatorios que posee:
puedeJubilarse(62, 'F', 34)
true

El mínimo de edad para realizar el trámite para las mujeres es de 60 años, mientras que para los
hombres es 65. En ambos casos, se deben contar con al menos 30 años de aportes.
¡Intentá resolverlo en una única función! Después vamos a ver cómo quedaría si delegamos. 
SOLUCION CORRECTA
function puedeJubilarse (edad,sexo,aportes) {
return (edad >=60 && sexo === "F" || edad >=65 && sexo === "M") && aportes >=30;
}
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
puedeJubilarse(67, "M", 29)
=> false
  puedeJubilarse(67, "M", 32)
=> true
  puedeJubilarse(59, "F", 30)
=> false
  puedeJubilarse(60, "F", 25)
=> false
  puedeJubilarse(60, "F", 30)
=> true
¿Y si delegamos? Podríamos separar la lógica de la siguiente manera:

function puedeJubilarse(edad, sexo, aniosAportes) {


return cumpleEdadMinima(edad, sexo) &&
tieneSuficientesAportes(aniosAportes); pero para resolverlo hay
que definir esta función }

Al delegar correctamente, hay veces en las que no es necesario alterar el


orden de precedencia, ¡otro punto a favor de la delegación! 
Ejercicio 12: ¿Puedo subir?
En un parque de diversiones de la ciudad instalaron una nueva montaña rusa  y nos
pidieron ayuda para que le digamos a las personas si pueden subirse o no antes de hacer
la fila. Los requisitos para subir a la atracción son:
 Alcanzar la altura mínima de 1.5m (o 1.2m si está acompañada por una persona
adulta)
 No tener ninguna afección cardíaca
Definí la función de 3 parámetros puedeSubirse que recibe una altura de una persona en metros,
si está acompañada y si tiene alguna afección cardíaca. Ejemplo:

puedeSubirse(1.7, false, true)


false // no puede subirse
// porque aunque tiene mas de 1.5m,
// tiene una afección cardíaca

SOLUCION CORRECTA
function puedeSubirse(alturamínima, acompañadaadulto, afeccióncardíaca){
return alturamínima >= 1.5 && !afeccióncardíaca || alturamínima >= 1.2 && acompañadaadulto;
} ¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 puedeSubirse(1.5, false, false)
=> true
  puedeSubirse(1.7, true, false)
=> true
  puedeSubirse(1.7,false, true)
=> false
¡Terminaste Lógica booleana!
En esta guía aprendiste tres tipos de operadores lógicos: conjunción &&, que es verdadero si todas
sus proposiciones son verdaderas; disyunción | |, que es verdadero si alguna de sus proposiciones es
verdadera; y xor, que es verdadero si sólo una de sus proposiciones es verdadera.
Además, aprendiste acerca de la precedencia de estos operadores, y cómo los paréntesis pueden
ayudarnos a alterar esa precedencia según nuestras necesidades. 

Listas
La programación no sería tan divertida y poderosa si sólo pudieramos trabajar con una cosa por
vez: muchas veces no vamos a querer simplemente operar un string, un booleano o un número, sino
varios a la vez.
¡Llegó entonces el momento de aprender a tratar conjuntos de cosas! Presentamos a... ¡las listas! 

Ejercicio 1: Series favoritas


Supongamos que queremos representar al conjunto de nuestras series favoritas. ¿Cómo
podríamos hacerlo?

let seriesFavoritasDeAna = ["Game of Thrones", "Breaking Bad", "House of Cards"];


let seriesFavoritasDeHector = ["En Terapia", "Recordando el Show de Alejandro Molina"]

Como ves, para representar a un conjunto de strings, colocamos todos esos strings que
nos interesan, entre corchetes ([ y ]) separados por comas. Fácil, ¿no?
Probá en la consola las siguientes consultas:
seriesFavoritasDeAna
seriesFavoritasDeHector
["hola","mundo!"]
["hola","hola"]
CONSOLA
["hola", "mundo!"]
=> ["hola","mundo!"]
  [seriesFavoritasDeAna]
=> [["Game of Thrones","Breaking Bad","House of Cards"]]
  [seriesFavoritasDeHector]
=> [["En Terapia","Recordando el Show de Alejandro Molina"]]
  ["hola","hola"]
=> ["hola","hola"]

Ejercicio 2: Y esto, es una lista


Lo que acabamos de ver es cómo modelar fácilmente conjuntos de cosas. Mediante el uso de [ ], en
JavaScript contamos con una manera simple de agrupar esos elementos en listas.
¿Acaso hay una cantidad máxima de elementos? ¡No, no hay límite! Las listas pueden tener
cualquier cantidad de elementos.
Y no sólo eso, sino que además, el orden es importante. Por ejemplo, no es lo
mismo ["hola", "mundo"] que ["mundo", "hola"]: ambos tienen los mismos elementos, pero
en posiciones diferentes.
Probá en la consola las siguientes consultas:
listasIguales(["hola", "mundo"], ["mundo", "hola"])
listasIguales(["hola", "mundo"], ["hola", "mundo"])
listasIguales(["hola", "mundo"], ["hola", "todo", "el", "mundo"])
listasIguales(["hola"], ["hola", "mundo"])
["hola", "mundo"] === ["mundo", "hola"]
personas
["mara", "julian"] === personas
personas === personas
¿Qué conclusiones podés sacar? 
consola
listasIguales(["hola", "mundo"], ["mundo", "hola"])
=> false
  listasIguales(["hola", "mundo"], ["hola", "mundo"])
=> true
  listasIguales(["hola", "mundo"], ["hola", "todo", "el", "mundo"])
=> false
  listasIguales(["hola"], ["hola", "mundo"])
=> false
  ["hola", "mundo"] === ["mundo", "hola"]
=> false
  personas
=> ["mara","julian"]
  ["mara", "julian"] === personas
=> false
  personas === personas
=> true

Ejercicio 3: Juegos de azar


Pero, pero, ¿sólo podemos crear listas de strings? ¿Y si quiero, por ejemplo, representar los números
de la lotería que salieron la semana pasada? ¿O las tiradas sucesivas de un dado? ¿O si salió cara o
ceca en tiradas sucesivas de una moneda?

let numerosDeLoteria = [2, 11, 17, 32, 36, 39];


let tiradasDelDado = [1, 6, 6, 2, 2, 4];
let salioCara = [false, false, true, false];
Como ves, también podemos representar conjuntos de números o booleanos, de igual forma:
escribiéndolos entre corchetes y separados por comas. Podemos tener listas de números, de strings,
de booleanos, etc. ¡Incluso podríamos tener listas de listas!
Veamos si queda claro. Probá en la consola las siguientes consultas:
numerosDeLoteria
salioCara
[[1, 2, 3], [4, 5, 6]]
CONSOLA
 numerosDeLoteria
=> [2,11,17,32,36,39]
  salioCara
=> [false,false,true,false]
  [[1, 2, 3], [4, 5, 6]]
=> [[1,2,3],[4,5,6]]

Ejercicio 4: Listas vacías


Genial, ¡parece que una lista puede contener cualquier tipo de elemento! Podemos tener listas de
booleanos, de números, de strings, de listas...
Y no sólo eso, sino que además pueden contener cualquier cantidad de elementos: uno, dos,
quince, cientos.
¿Podremos entonces tener listas vacías, es decir, que no tengan elementos? ¡Por supuesto!

let unaListaVacia = [ ]

Probá escribir en la consola unaListaVacia


CONSOLA
  unaListaVacia
=> []
Ejercicio 5: ¿Cuántos elementos tenés?
Por el momento ya sabemos qué cosas podemos representar con listas, y cómo hacerlo. Pero, ¿qué
podemos hacer con ellas?
Empecemos por lo fácil: saber cuántos elementos hay en la lista. Esto lo podemos hacer utilizando
la función longitud, de forma similar a lo que hacíamos con los strings.
Realizá las siguientes consultas en la consola:
longitud([ ])
longitud(numerosDeLoteria)
longitud([4, 3])
CONSOLA
longitud([])
=> 0
  longitud(numerosDeLoteria)
=> 6
  longitud([4, 3])
=> 2
En biblioteca var numerosDeLoteria = [2, 11, 17, 32, 36, 39];

Ejercicio 6: Agregando sabor


Las listas son muy útiles para contener múltiples elementos. ¡Pero hay más! También podemos
agregarle elementos en cualquier momento, utilizando la función agregar, que recibe dos
parámetros: la lista y el elemento. Por ejemplo:

let pertenencias = ["espada", "escudo", "antorcha"];


//longitud(pertenencias) devuelve 3;

agregar(pertenencias, "amuleto mágico");


//ahora longitud(pertenencias) devuelve 4

Como vemos, agregar suma un elemento a la lista, lo cual hace que su tamaño


aumente. ¿Pero en qué parte de la lista lo agrega? ¿Al principio? ¿Al final? ¿En el
medio?
Averigualo vos mismo: inspeccioná en la consola qué elementos contiene pertenencias,
agregale una "ballesta" y volvé a inspeccionar pertenencias.
Además existe un procedimiento remover, que sólo recibe la lista por parámetro. Investigá en la consola qué
hace. 
CONSOLA
  agregar (pertenencias, "ballesta")
=> 4
  remover(pertenencias, "ballesta")
=> "ballesta"

Ejercicio 7: Trasladar
Bueno, ya hablamos bastante; ¡es hora de la acción !
Definí un procedimiento trasladar, que tome dos listas, saque el último elemento de la
primera y lo agregue a la segunda.
Ejemplo:

let unaLista = [1, 2, 3];


let otraLista = [4, 5];
trasladar(unaLista, otraLista);
unaLista //debería ser [1, 2]
otraLista //debería ser [4, 5, 3]

 ¡Dame una pista!


¿Tenés dudas sobre cómo quitar y agregar elementos? Repasemos agregar y remover:
 agregar(lista, elemento): agrega elemento al final de lista.
 remover(lista): saca el último elemento de lista y lo devuelve.
SOLUCION CORRECTA
function trasladar(unaLista, otraLista){
agregar(otraLista, (remover(unaLista))) }
¡Muy bien! Tu solución pasó todas las pruebas
OTRA SOLUCION CORRECTA
function trasladar(unaLista, otraLista){
  agregar(otraLista, remover(unaLista)) }
¡Muy bien! Tu solución pasó todas las pruebas
Hasta ahora anduvimos agregando, quitando y consultando longitudes. ¿Qué más
podemos hacer con las listas? ¡Seguinos!

Ejercicio 8: ¿Y dónde está?


Otra cosa que queremos hacer con las listas es saber en qué posición se encuentra un
elemento. Para ello utilizamos la función posicion de la siguiente manera:

posicion(["a", "la", "grande", "le", "puse", "cuca"], "grande"); //devuelve 2


let diasLaborales = ["lunes", "martes", "miercoles", "jueves", "viernes"]
posicion(diasLaborales, "lunes"); //devuelve 0

Como ves, lo curioso de esta función es que pareciera devolver siempre uno menos de lo
esperado. Por ejemplo, la palabra "grande" aparece tercera, no segunda; y "lunes" es el
primer día laboral, no el cero. ¿Es que los creadores de JavaScript se equivocaron? 
¡No! Se trata de que en JavaScript, al igual que en muchos lenguajes, las posiciones de las
listas arrancan en 0: el primer elemento está en la posición 0, el segundo en la 1, el tercero
en la 2, y así.
¿Y qué sucede si le pasás como argumento a posicion un elemento que no tiene?
¡Averigualo!
Probá lo siguiente:

posicion(diasLaborales, "osvaldo")

  posicion(diasLaborales, "osvaldo")
=> -1
   Ejercicio 9: Contiene
Definí la función contiene que nos diga si una lista contiene un cierto elemento.

contiene([1, 6, 7, 6], 7)
true
contiene([1, 6, 7, 6], 6)
true
contiene([], 7)
false
contiene([8, 5], 7)
false

 ¡Dame una pista!


La función posicion que vimos en el ejercicio anterior nos puede ayudar a resolver el problema. Te
recordamos como se usa :
posicion(["africa", "america", "asia", "europa", "oceania"]
, "asia")
2 // porque "africa" en esa lista está en la posición 0, "america" en la 1, "asia" en la 2, etc

Ah, ojo  porque como vimos posicion devuelve algo particular cuando la lista no contiene el


elemento. ¿No te acordás? ¡Volvé a probar en el ejercicio anterior!
SOLUCION CORRECTA
function contiene(lista, numero) {
return posicion(lista, numero) >= 0; }
function contiene(lista, numero) {
return posicion(lista, numero) !== -1; } ¡Muy bien! Tu solución pasó todas las pruebas
OTRA SOLUCION CORRECTA
function contiene(lista, numero) {
return posicion(lista, numero) >= 0; }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
contiene([1, 6, 7, 6], 7)
=> true
  contiene([], 7)
=> false
  contiene([9, 4], 3)
=> false
Si venís prestando atención a los ejemplos de consulta, habrás notado que las listas
también pueden tener elementos duplicados: [1, 2, 1], ["hola", "hola"], etc.
Por tanto, posicion en realidad devuelve la posición de la primera aparición del elemento
en la lista. Por ejemplo:

posicion(["qué", "es", "eso", "eso", "es", "queso"], "es")


1 //devuelve 1 porque si bien "es" también está en la posición 4, aparece primero en la
posición 1.

Por tanto, posicion en realidad devuelve la posición de la primera aparición del elemento en la


lista.
Respuesta:
function contiene(array, buscar){
  return(posicion(array, buscar) >- 1) }

Ejercicio 10: Enésimo elemento


Así como existe una función para averiguar en qué posición está un elemento, también
puede ocurrir que queramos saber lo contrario: qué elemento está en una cierta posición. 
Para averiguarlo podemos usar el operador de indexación, escribiendo después de la
colección y entre corchetes [ ] la posición que queremos para averiguar:

mesesDelAnio[0]
"enero"
["ese", "perro", "tiene", "la", "cola", "peluda"][1]
"perro"

¡Ojo! El número que le pases, formalmente llamado índice, debe ser menor a la longitud
de la lista, o cosas malas pueden suceder. 
Probalo en la consola: ¿qué sucede si le pedís el elemento 0 a una lista vacía? ¿O si le pedís el
elemento 48 a una lista de 2 elementos?
CONSOLA
 0
=> 0
  48
=> 48
let lista =[ ]

lista[0]
Ejercicio 11: Más premios
Si le pedís un elemento en una posición igual o mayor al tamaño de la lista, vas a
obtener undefined. No parece algo terrible, pero el problema es que con undefined no
podés hacer nada realmente útil.
Así que la advertencia es: ¡no te pases de índice! 
Teniendo esto en cuenta, va un desafío: definí nuevamente la
función medallaSegunPuesto, pero esta vez usando como máximo un único if. Quizás las
listas te pueden ser útiles acá .
Te recordamos qué hace la función: tiene que devolver la medalla que le corresponde a
los primeros puestos de una competencia.

medallaSegunPuesto(1)
"oro"
medallaSegunPuesto(2)
"plata"
medallaSegunPuesto(3)
"bronce"
medallaSegunPuesto(4)
"nada"
medallaSegunPuesto(5)
"nada"

 ¡Dame una pista!


¿En qué nos puede ser útil una lista aquí? Pensá que la medalla que recibe la persona
está directamente relacionada con la posición en la que sale en la competencia. 
SOLUCION CORRECTA
let medalla= ["oro", "plata", "bronce"];
function medallaSegunPuesto (puesto) {
if (puesto >=1 && puesto <=3) {
return medalla[puesto-1]; }
else {
return "nada" } }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
medallaSegunPuesto(1)
=> "oro"
  medallaSegunPuesto(3)
=> "bronce"
  medallaSegunPuesto(5)
=> "nada"
let medalla = ["nada", "oro", "plata", "bronce"]
function medallaSegunPuesto(puesto){
  if(puesto <= 3){
    return(medalla[puesto])
  } else {
    return(medalla[0])
  } }

Ejercicio 12: No te olvides de saludar


Vamos a conocer una manera de recorrer los elementos de una lista con un nuevo amigo:
el for. 
Completá la función saludar que recibe una lista de personas e imprime un saludo para cada una de
ellas.

saludar(["Don Pepito", "Don Jose"])


hola Don Pepito
hola Don Jose
saludar(["Elena", "Hector", "Tita"])
hola Elena
hola Hector
hola Tita

 ¡Dame una pista!


Tené en cuenta que el saludo que imprimimos siempre es "hola" y luego el nombre
de la persona. ¡No te olvides del espacio!
SOLUCION CORRECTA
function saludar(personas) {
for(let persona of personas) {
imprimir ("hola" + " " + persona) } }¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 saludar(["Don Pepito", "Don Jose"])
hola Don Pepito
hola Don Jose
=> undefined
Imaginémonos que tenemos una lista con los precios de los productos que compramos en
el supermercado y queremos restar cada uno de ellos a plataEnBilletera .
Usando for podemos hacerlo así:

for(let precio of [10, 100, 87 ]) {


plataEnBilletera = plataEnBilletera – precio }

donde plataEnBilletera es una variable que se va modificando a medida que recorremos


los precios.
Si teníamos $500 en nuestra billetera, después del for nos van a quedar $303 porque:
 Al principio plataEnBilletera era 500 y el primer precio de la lista es 10. Luego de
hacer 500 - 10, plataEnBilletera es 490.
 A los 490 que quedaron en nuestra billetera, le restamos el segundo precio de la
lista: 100. Ahora plataEnBilletera es 390.
 El último precio a restar es 87, por lo que, al hacer 390 - 87, la
variable plataEnBilletera terminará siendo 303.
¡Terminaste Listas!
Acabas de conocer una estructura de datos que te permite agruparlos: ¡la lista! 
También aprendiste qué datos puede tener dentro, cómo agregarle o sacarle elementos, conocer
sus posiciones, obtener su longitud y los elementos por posición.
Por último viste una introducción a cómo recorrerla, aunque este tema lo profundizaremos un poco
más adelante. 

Registros
Muchas veces, cuando representamos cosas de la vida real en nuestros programas,
necesitamos poder agrupar múltiples características de esas cosas de alguna forma.
Te presentamos una estructura que nos va a ayudar en esa tarea: los registros. 

Ejercicio 1: Los primeros registros


Una historiadora está recopilando información acerca de distintos monumentos a lo largo
y ancho del mundo . En principio solo quiso saber el nombre, ubicación, y año de
construcción de cada monumento.  Para eso almacenó cada dato en una variable:

nombreEstatuaDeLaLibertad = "Estatua de la Libertad";


locacionEstatuaDeLaLibertad = "Nueva York";
anioDeConstruccionEstatuaDeLaLibertad = "1886";
nombreCristoRedentor = "Cristo Redentor";
locacionCristoRedentor = "Rio De Janeiro";
anioDeConstruccionCristoRedentor = "1931";

Ahí es cuando se dio cuenta que no era conveniente : si bien la información entre las
variables estaba relacionada, la estaba almacenando por separado. Entonces pensó: ¿no
existirá alguna forma de representar las distintas características o propiedades de una
misma cosa de forma agrupada?
Luego de investigar un poco, encontró una mejor manera para guardar la información de
los monumentos. Probá en la consola escribiendo:

estatuaDeLaLibertad
cristoRedentor
torreEiffel
tajMahal
coliseo

CONSOLA
 estatuaDeLaLibertad
=> {nombre:"Estatua de la Libertad",locacion:"Nueva York, Estados Unidos de
América",anioDeConstruccion:1886}
  cristoRedentor
=> {nombre:"Cristo Redentor",locacion:"Rio de Janeiro, Brasil",anioDeConstruccion:1931}
  torreEiffel
=> {nombre:"Torre Eiffel",locacion:"París, Francia",anioDeConstruccion:1889}
  tajMahal
=> {nombre:"Taj Mahal",locacion:"Agra, India",anioDeConstruccion:1653}
  coliseo
=> {nombre:"Coliseo",locacion:"Roma, Italia",anioDeConstruccion:80}
   

Ejercicio 2: Tu propio monumento


Los monumentos que probaste en el ejercicio anterior están representados como registros,
y cada una de sus características (nombre, locación, año de construcción) son campos del
registro. Por cierto, ¡podemos crear registros de cualquier cosa, con los campos que
querramos!
Por ejemplo, podríamos almacenar un libro de modo que cada campo del registro fuese
alguna característica: su título, su autor, su fecha de publicación, y más . 
¡Es tu momento del monumento! Guardá en las
variables torreAzadi y monumentoNacionalALaBandera registros de esos
monumentos, oriundos de las ciudades de Teherán, Irán y Rosario,
Argentina respectivamente. ¿Te animás a investigar en qué año se terminaron de
construir para completar ese campo? 
 ¡Dame una pista!
Quizá te sea útil ver cómo declaramos algún monumento en el ejercicio anterior.
Por ejemplo, esta es la Torre Eiffel:

let torreEiffel = { nombre: "Torre Eiffel", locacion: "París, Francia", anioDeConstruccion: 1889 };

SOLUCION CORRECTA
let torreAzadi = {nombre: "Torre Azadi", locacion: "Teherán, Irán", anioDeConstruccion:
1971}

let monumentoNacionalALaBandera = {nombre: "Monumento Nacional A La Bandera",


locacion: "Rosario, Argentina", anioDeConstruccion: 1957 }

¡Muy bien! Tu solución pasó todas las pruebas


¡Buenas habilidades de búsqueda!  Los registros, al igual que las listas, son una estructura
de datos porque nos permiten organizar información. Pero ¿en qué se diferencia un
registro de una lista?
En las listas podemos guardar muchos elementos de un mismo tipo que representen una
misma cosa (por ejemplo todos números, o todos strings). No existen límites para las listas:
pueden tener muchos elementos, ¡o ninguno!
En un registro vamos a guardar información relacionada a una única cosa (por
ejemplo un monumento o una persona), pero los tipos de los campos pueden cambiar.
Por ejemplo, el nombre y la ubicación de un monumento son strings, pero su año de
construcción es un número.

Ejercicio 3: Accediendo al campo


Cuando consultaste los registros existentes, se veía algo parecido a lo siguiente:

tajMahal
{ nombre: "Taj Mahal", locacion: "Agra, India", anioDeConstruccion: 1653 }

Esa consulta era porque estábamos viendo al registro tajMahal completo, incluyendo todos


sus campos. ¡Pero también se puede consultar por un campo particular! Mirá

tajMahal.locacion
"Agra, India"
tajMahal.anioDeConstruccion
1653

Declaramos los planetas mercurio, marte y saturno como registros con la siguiente


información: nombre, temperaturaPromedio y si tieneAnillos. ¡Probalos en la consola!
CONSOLA
mercurio
=> {nombre:"Mercurio",temperaturaPromedio:67,tieneAnillos:false}
  marte
=> {nombre:"Marte",temperaturaPromedio:-63,tieneAnillos:false}
  saturno
=> {nombre:"Saturno",temperaturaPromedio:-139,tieneAnillos:true}

Ejercicio 4: Temperatura de planeta


Ahora que agregamos registros de planetas, ¡trabajemos un poco con ellos! 
Definí una función temperaturaDePlaneta que reciba como argumento un registro de planeta y
retorne un string que indique su nombre y su temperatura promedio. ¡Tiene que funcionar para
cualquier planeta!  Por ejemplo:

temperaturaDePlaneta(mercurio)
"Mercurio tiene una temperatura promedio de 67 grados"
temperaturaDePlaneta(saturno)
"Saturno tiene una temperatura promedio de -139 grados"
temperaturaDePlaneta(venus)
"Venus tiene una temperatura promedio de 462 grados"

 ¡Dame una pista!


¡Prestá atención a los strings que devuelven los ejemplos! Sólo la parte correspondiente a
cada planeta varía, como el nombre y la temperaturaPromedio. Además, tenés que dejar
espacios entre las palabras que rodean a nombre y temperaturaPromedio. Mirá...
nombre tiene una temperatura promedio de temperaturaPromedio grados
SOLUCION CORRECTA
function temperaturaDePlaneta(planeta){
return (planeta).nombre +' tiene una temperatura promedio de '+
(planeta).temperaturaPromedio +' grados'; }
¡Muy bien! Tu solución pasó todas las pruebas

CONSOLA
 mercurio
=> {nombre:"Mercurio",temperaturaPromedio:67,tieneAnillos:false}
  saturno
=> {nombre:"Saturno",temperaturaPromedio:-139,tieneAnillos:true}

Ejercicio 5: Moviendo archivos


Por el momento estuvimos creando y consultando registros. ¿No sería interesante poder...
modificarlos? 
La sintaxis para modificar campos de registros es muy similar a lo que hacemos para
cambiar los valores de las variables. Por ejemplo, para cambiar la temperatura de un
planeta:

saturno.temperaturaPromedio = -140;
Ahora imaginá que tenemos un registro para representar un archivo, del que sabemos su
ruta (dónde está guardado) y su fecha de creación. Si queremos cambiar su ruta podemos
hacer...

leeme
{ ruta: "C:\leeme.txt", creacion: "23/09/2004" }
moverArchivo(leeme, "C:\documentos\leeme.txt")

Luego el registro leeme tendrá modificada su ruta:


leeme
{ ruta: "C:\documentos\leeme.txt", creacion: "23/09/2004" }

¡Es tu turno! Definí el procedimiento moverArchivo, que recibe un registro y una nueva ruta y
modifica el archivo con la nueva ruta.
SOLUCION CORRECTA
function moverArchivo(registro, nuevaRuta){
registro.ruta = nuevaRuta; }
¡Muy bien! Tu solución pasó todas las pruebas
OTRA SOLUCION CORRECTA
function moverArchivo(registro, nuevaRuta){
let leeme = registro.ruta = nuevaRuta; }
 ¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 6: Registros de dos milenios


En el ejercicio anterior modificamos la ruta del registro, pero no utilizamos su fecha de
creación. ¡Usémosla! Queremos saber si un archivo es del milenio pasado, lo que ocurre
cuando su año es anterior a 2000. 
Definí la función esDelMilenioPasado, que recibe un archivo y retorna un booleano.

esDelMilenioPasado({ ruta: "D:\fotonacimiento.jpg", creacion: "14/09/1989"


}) true
 ¡Dame una pista!
Quizá te pueda servir la función anio:

anio("04/11/1993")
1993

SOLUCION CORRECTA
function esDelMilenioPasado(registro){
return anio(registro.creacion) < 2000;}
Muy bien! Tu solución pasó todas las pruebas
OTRA SOLUCION CORRECTA
function esDelMilenioPasado(archivo){
return(anio(archivo.creacion) < 2000) }
¡Muy bien! Tu solución pasó todas las pruebas

CONSOLA
 "1993"
=> "1993"
  esDelMilenioPasado({ruta: "D:\fotonacimiento.jpg", creacion: "4/11/19
93" })
=> true

Ejercicio 7: Postres complejos


Unos ejercicios atrás te contamos la diferencia entre listas y registros. ¡Pero eso no significa
que no podamos usar ambas estructuras a la vez! 
Por ejemplo, una lista puede ser el campo de un registro. Mirá estos registros de postres, de los
cuales sabemos cuántos minutos de cocción requieren y sus ingredientes:
(estos let se colocan en la solución antes de function)

let flanCasero = { ingredientes: ["huevos", "leche", "azúcar", "vainilla"],


tiempoDeCoccion: 50 }
let cheesecake = { ingredientes: ["queso crema", "frambuesas"], tiempoDeCoccion: 80 }
let lemonPie = { ingredientes: ["jugo de limón", "almidón de maíz", "leche", "huevos"],
tiempoDeCoccion: 65 }

Definí la función masDificilDeCocinar, que recibe dos registros de postres como


argumentos y retorna el que tiene más ingredientes de los dos.

masDificilDeCocinar(flanCasero, cheesecake)
{ ingredientes: ["huevos", "leche", "azúcar", "vainilla"], tiempoDeCoccion: 50 }

 ¡Dame una pista! ¡Recordá que podés usar la función longitud! Y si los dos postres
tienen la misma cantidad de ingredientes, podés devolver cualquiera de los dos. 
SOLUCION CORRECTA
let flanCasero = { ingredientes: ["huevos", "leche", "azúcar", "vainilla"], tiempoDeCoccion:
50 }
let cheesecake = { ingredientes: ["queso crema", "frambuesas"], tiempoDeCoccion: 80 }
let lemonPie = { ingredientes: ["jugo de limón", "almidón de maíz", "leche", "huevos"],
tiempoDeCoccion: 65 }
function masDificilDeCocinar(postre1, postre2) {
if (longitud(postre1.ingredientes) > longitud(postre2.ingredientes))
return postre1;
else {
return postre2; } }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 masDificilDeCocinar(cheesecake, lemonPie)
=> {ingredientes:["jugo de limón","almidón de
maíz","leche","huevos"],tiempoDeCoccion:65}
  masDificilDeCocinar(lemonPie, flanCasero)
=> {ingredientes:
["huevos","leche","azúcar","vainilla"],tiempoDeCoccion:50}
  masDificilDeCocinar(flanCasero, lemonPie)
=> {ingredientes:["jugo de limón","almidón de
maíz","leche","huevos"],tiempoDeCoccion:65}
Toma el registro 2 o ultimo

Ejercicio 8: Listas de registros


En el ejercicio anterior te mostramos que un registro puede tener una lista entre sus
campos. ¿Y al revés? ¿Podemos tener una lista de registros? 
¡Sí! Así como trabajamos con listas de números, booleanos, strings o más listas, también
podemos listar registros. Se puede hacer todo lo que hacías antes, como por
ejemplo remover, saber su longitud o preguntar por el elemento de cierta posición
utilizando los corchetes [ ].
Probá en la consola las listas postresFavoritos y monumentosDeAmerica. Hay un postre
que no mostramos antes, ¿te das cuenta cuál es solamente leyendo sus ingredientes? 
CONSOLA
postresFavoritos
=> [{ingredientes:["galletitas","dulce de
leche","crema"],tiempoDeCoccion:20},{ingredientes:
["huevos","leche","azúcar","vainilla"],tiempoDeCoccion:50},
{ingredientes:["queso crema","frambuesas"],tiempoDeCoccion:80},
{ingredientes:["jugo de limón","almidón de
maíz","leche","huevos"],tiempoDeCoccion:65}]
  monumentosDeAmerica
=> [{nombre:"Monumento Nacional a la Bandera",locacion:"Rosario,
Argentina",anioDeConstruccion:1957},{nombre:"Estatua de la
Libertad",locacion:"Nueva York, Estados Unidos de
América",anioDeConstruccion:1886},{nombre:"Cristo
Redentor",locacion:"Rio de Janeiro,
Brasil",anioDeConstruccion:1931}]

Ejercicio 9: 60 dulces minutos


A veces no sólo queremos comer algo rico, sino que queremos comerlo lo antes posible.  
Definí el procedimiento agregarAPostresRapidos, que toma una lista con postres
rápidos y un postre por parámetro. Si el tiempo de cocción es de una hora o menos, se
agrega el registro a la lista.
 ¡Dame una pista!
¡Recordá que tiempoDeCoccion está expresado en minutos! Por lo tanto, si queremos que
se cocine en una hora o menos, tenés que fijarte que ese tiempoDeCoccion sea menor a 60
minutos. 
Además, como agregarAPostresRapidos es un procedimiento, no t iene que retornar nada.
Sólo tenés que agregar (¿te acordás de este procedimiento?) el postre a la lista si es rápido.
SOLUCION CORRECTA
function agregarAPostresRapidos(postresrapidos, unpostre){
if (unpostre.tiempoDeCoccion <= 60){
agregar(postresrapidos, unpostre) } }
¡Muy bien! Tu solución pasó todas las pruebas

OTRA SOLUCION
function agregarAPostresRapidos(lista, postre){
  if(postre.tiempoDeCoccion <= 60){
    agregar(lista, postre)   } }¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 10: Hay un registro en mi registro


¿Te acordás cuando vimos que una lista podía estar compuesta por otras listas? ¡Con los
registros aplica la misma idea!  Si tenemos alguna estructura de datos compleja, puede
ocurrir que no alcance con representarla únicamente mediante strings, números,
booleanos y listas, sino que necesitemos otro registro dentro.
¡No se puede vivir a base de postres! Bueno, quizás sí, pero mantengamos una
alimentación saludable . Mediante un registro queremos modelar un menú completo:
consiste en un plato principal , los vegetales de la ensalada que acompaña , y un
postre  como lo veníamos trabajando, es decir, sigue siendo un registro.
Por ejemplo, el siguiente es un menú con bife de lomo como plato principal, una ensalada
de papa, zanahoria y arvejas como acompañamiento y un cheesecake de postre. Como el
registro es un poco extenso, y para que sea más legible, lo vamos a escribir de la siguiente
forma:

let menuDelDia = {
platoPrincipal: "bife de lomo",
ensalada: ["papa", "zanahoria", "arvejas"],
postre: { ingredientes: ["queso crema", "frambuesas"], tiempoDeCoccion: 80 } };

Averiguá qué devuelve el campo ingredientes del campo postre del registro menuInfantil.


¡Está un registro adentro del otro! La sintaxis es la siguiente:

menuInfantil.postre.ingredientes

CONSOLA
 menuInfantil.postre.ingredientes
=> ["galletitas","dulce de leche","crema"]
Ejercicio 11: ¡Azúcar!
Para terminar, trabajemos una vez más con los menúes.
Definí un procedimiento endulzarMenu, que recibe un registro menú y le agrega azúcar a
los ingredientes de su postre. Si ya tiene azúcar, no importa... ¡le agrega más! 
 ¡Dame una pista!
Recordá que cada menú tiene un postre y que cada postre tiene ingredientes. 
SOLUCION CORRECTA
function endulzarMenu (menu) {
agregar (menu.postre.ingredientes, "azúcar"); }
¡Muy bien! Tu solución pasó todas las pruebas
¡Terminaste Registros!
Durante la lección aprendiste cuál es la utilidad de esta estructura de datos llamada
registro, cómo acceder a sus campos y modificarlos, y hasta viste que pueden anidarse (es
decir, que haya un registro dentro de otro). ¡Felicitaciones! 

Recorridos
En lecciones anteriores definimos funciones, usamos registros y listas. Ahora que tenemos
todas esas herramientas, llega el momento de combinarlas y aprender a correr las listas y
hacer cosas más complejas. ¡Acompañanos!

Ejercicio 1: Las ganancias semestrales


Ana, contadora de una conocida empresa , tiene registros para representar los balances de
cada mes y una lista para guardarlos. Por ejemplo, para el último semestre del año pasado
registró los siguientes:

En julio ganó $50, en agosto perdió $12, etc


let balancesUltimoSemestre = [
{ mes: "julio", ganancia: 50 },
{ mes: "agosto", ganancia: -12 },
{ mes: "septiembre", ganancia: 1000 },
{ mes: "octubre", ganancia: 300 },
{ mes: "noviembre", ganancia: 200 },
{ mes: "diciembre", ganancia: 0 } ];

Y nos acaba de preguntar: "¿puedo saber la ganancia de todo un semestre?"


"Obvio, solo tenemos que sumar las ganancias de todos los balances", dijimos, y escribimos
el siguiente código:

function gananciaSemestre(balances) {
return balances[0].ganancia + balances[1].ganancia +
balances[2].ganancia + balances[3].ganancia +
balances[4].ganancia + balances[5].ganancia;
}

"Gracias ", nos dijo Ana, y se fue calcular las ganancias usando la función que le pasamos.
Pero un rato más tarde, volvió contándonos que también había registrado los balances del
primer trimestre de este año:

//En enero la empresa ganó $80, en febrero, $453, en marzo $1000


let balancesPrimerTrimestre = [
{ mes: "enero", ganancia: 80 },
{ mes: "febrero", ganancia: 453 },
{ mes: "marzo", ganancia: 1000 } ];
Y nos preguntó: "¿Podría usar esta función que me dieron para calcular las ganancias del
primer trimestre?".
¿Tiene algún problema la función gananciaSemestre que escribimos anteriormente?
¿Funcionará con los balances trimestrales? ¿Y con los cuatrimestrestrales?
¡Probala en la consola!
BIBLIOTECA

let balancesUltimoSemestre = [{ mes: "julio", ganancia: 50 }, { mes: "agosto", ganancia: -


12 }, { mes: "septiembre", ganancia: 1000 }, { mes: "octubre", ganancia: 300 }, { mes:
"noviembre", ganancia: 200 }, { mes: "diciembre", ganancia: 0 }];

let balancesPrimerTrimestre = [{ mes: "enero", ganancia: 80 }, { mes: "febrero", ganancia:


453 }, { mes: "marzo", ganancia: 1000 }];

function gananciaSemestre(balances) {
return balances[0].ganancia + balances[1].ganancia +
balances[2].ganancia + balances[3].ganancia +
balances[4].ganancia + balances[5].ganancia; }

CONSOLA
balancesPrimerTrimestre
=> [{mes:"enero",ganancia:80},{mes:"febrero",ganancia:453},
{mes:"marzo",ganancia:1000}]
  balancesCuartoTrimestre;
^
ReferenceError: balancesCuartoTrimestre is not defined
balancesUltimoSemestre
=> [{mes:"julio",ganancia:50},{mes:"agosto",ganancia:-12},
{mes:"septiembre",ganancia:1000},{mes:"octubre",ganancia:300},
{mes:"noviembre",ganancia:200},{mes:"diciembre",ganancia:0}]

Ejercicio 2: ¿Y el resto de las ganancias?


La función gananciaSemestre anterior tiene dos problemas :
1. Es muy repetitiva y tediosa de escribir. ¡Tenemos que hacer muchas sumas a mano!
2. No es genérica, como bien dice su nombre, sólo sirve para sumar las ganancias de
6 balances:
 si la lista tiene más de seis balances, sólo suma los primeros;
 si tiene menos, no funciona (¿te acordás cuando te dijimos que si te ibas de índice
cosas malas podían ocurrir? )
Lo que nos gustaría es poder sumar las ganancias de todos los balances de una lista, sin
importar cuántos haya realmente; queremos una función gananciaTotal, que
pueda sumar balances de cualquier período de meses: semestres, cuatrimestres, trimestres,
etc. ¡Qué difícil! Ya tenemos nuestra versión; probala con las siguientes consultas:

gananciaTotal([
{ mes: "enero", ganancia: 2 },
{ mes: "febrero", ganancia: 3 } ])
gananciaTotal([
{ mes: "enero", ganancia: 2 },
{ mes: "febrero", ganancia: 3 },
{ mes: "marzo", ganancia: 1 },
{ mes: "abril", ganancia: 8 },
{ mes: "mayo", ganancia: 8 },
{ mes: "junio", ganancia: -1 } ])
gananciaTotal([ ])

Después seguinos para contarte cómo la hicimos.


CONSOLA
gananciaTotal([{mes: "enero", ganancia: 2}, {mes: "febrero", ganan
cia: 3}])
=> 5
  gananciaTotal([ {mes: "enero", ganancia: 2}, {mes: "febrero", gan
ancia: 3}, {mes: "marzo", ganancia: 1}, {mes: "abril", ganancia: 8
}, {mes: "mayo", ganancia: 8}, {mes:"junio", ganancia: -1}])
=> 21
  gananciaTotal([])
=> 0

Ejercicio 3: Todas las ganancias, la ganancia


Ahora que sabemos la función que necesitamos (gananciaTotal), razonemos cómo
hacerla...
Vamos de a poquito : si la lista no tuviera elementos, ¿cuánto debería ser la
sumatoria? ¡0!

function gananciaTotal0(balancesDeUnPeriodo) {
let sumatoria = 0;
return sumatoria; }

¿Y si tuviera exactamente 1 elemento? Sería... 0.... ¿más ese elemento? ¡Exacto!

function gananciaTotal1(balancesDeUnPeriodo) {
let sumatoria = 0;
sumatoria = sumatoria + balancesDeUnPeriodo[0].ganancia;
return sumatoria; }

¿Y si tuviera 2 elementos?

function gananciaTotal2(balancesDeUnPeriodo) {
let sumatoria = 0;
sumatoria = sumatoria + balancesDeUnPeriodo[0].ganancia;
sumatoria = sumatoria + balancesDeUnPeriodo[1].ganancia;
return sumatoria; }

¿Y si tuviera 3 elementos? 

function gananciaTotal3(balancesDeUnPeriodo) {
let sumatoria = 0;
sumatoria = sumatoria + balancesDeUnPeriodo[0].ganancia;
sumatoria = sumatoria + balancesDeUnPeriodo[1].ganancia;
sumatoria = sumatoria + balancesDeUnPeriodo[2].ganancia;
return sumatoria; }

¿Empezás a ver un patrón? Tratá de escribir gananciaTotal4 que funcione para


4 elementos.
SOLUCIONCORRECTA
function gananciaTotal4(balancesDeUnPeriodo) {
let
sumatoria = 0;
sumatoria = sumatoria + balancesDeUnPeriodo[0].ganancia;
sumatoria = sumatoria + balancesDeUnPeriodo[1].ganancia;
sumatoria = sumatoria + balancesDeUnPeriodo[2].ganancia;
sumatoria = sumatoria + balancesDeUnPeriodo[3].ganancia;
return sumatoria; }
 ¡Muy bien! Tu solución pasó todas las pruebas
¿Y si la lista tuviera cualquier cantidad de elementos? Si seguimos repitiendo este patrón,
veremos que una sumatoria de una lista siempre arranca igual, con let sumatoria = 0, y
termina igual, devolviendo la variable local sumatoria (return sumatoria).

function gananciaTotalN(unPeriodo) {
let sumatoria = 0; // esto siempre está
//... etc
return sumatoria; //esto siempre está }

Lo que cambia son las acumulaciones (sumatoria = sumatoria + ...); necesitamos una por
cada elemento de la lista. Dicho de otra forma, tenemos que visitar cada elemento del
mismo, sin importar cuántos tenga. Pero, ¿cómo hacerlo? ¿No te suena conocida esta idea
de repetir algo muchas veces?

 Ejercicio 4: Nos visita un viejo amigo


Lo que tenemos que hacer, entonces, es repetir la operación de acumular varias
veces, una por cada elemento de la lista. ¡Digamos hola  (nuevamente) al for...of!

function gananciaTotal(balancesDeUnPeriodo) {
let sumatoria = 0;
for (let balance of balancesDeUnPeriodo) {
sumatoria = sumatoria + balance.ganancia; }
return sumatoria; }

Como ves, el for...of nos permite visitar y hacer algo con cada elemento de una lista; en
este caso, estaremos visitando cada balance de balancesDeUnPeriodo.
¿Aún no te convenciste? Nuevamente, probá las siguientes expresiones en la consola:

gananciaTotal([])
gananciaTotal([
{ mes: "noviembre", ganancia: 5 }
])
gananciaTotal([
{ mes: "marzo", ganancia: 8 },
{ mes: "agosto", ganancia: 10 }
])
gananciaTotal([
{ mes: "enero", ganancia: 2 },
{ mes: "febrero", ganancia: 10 },
{ mes: "marzo", ganancia: -20 }
])
gananciaTotal([
{ mes: "enero", ganancia: 2 },
{ mes: "febrero", ganancia: 10 },
{ mes: "marzo", ganancia: -20 },
{ mes: "abril", ganancia: 0 },
{ mes: "mayo", ganancia: 10 } ])

BIBLIOTECA
function gananciaTotal(balancesDeUnPeriodo) {
let sumatoria = 0;
for (let balance of balancesDeUnPeriodo) {
sumatoria = sumatoria + balance.ganancia; }
return sumatoria; }

CONSOLA
 gananciaTotal([])
=> 0
  gananciaTotal([ {mes: "noviembre", ganancia: 5} ])
=> 5
  gananciaTotal([ {mes: "marzo", ganancia: 8}, {mes: "agosto", ganancia
: 10} ])
=> 18
  gananciaTotal([ {mes: "enero", ganancia: 2}, {mes: "febrero", gananci
a: 10}, {mes: "marzo", ganancia: -20} ])
=> -8
gananciaTotal([ {mes: "enero", ganancia: 2}, {mes: "febrero", ganancia
: 10}, {mes: "marzo", ganancia: -20}, {mes: “abril”, ganancia: 0},
{mes: “mayo”, ganancia: 10} ])
=> 2

Ejercicio 5: Cuentas claras


¡Ana tiene nuevos requirimientos! Ahora nos pidió lo siguiente: "Quiero saber cuántos
balances fueron positivos, es decir, aquellos en los que la ganancia fue mayor a cero".
. Completá la función cantidadDeBalancesPositivos. Si prestás atención, notarás que tiene
una estructura similar al problema anterior
 ¡Dame una pista!
Lo importante en este ejercicio es pensar cuál es el valor inicial de cantidad y
cuándo incrementa ese valor.
SOLUCION CORRECTA
function cantidadDeBalancesPositivos(balancesDeUnPeriodo){
let cantidad = 0;
for (let balance of balancesDeUnPeriodo) {
if(balance.ganancia > 0){
cantidad= cantidad + 1} }
return cantidad; }
¡Muy bien! Tu solución pasó todas las pruebas
cantidad es lo que en programación se conoce como contador, una variable que se
incrementa cada vez que hacemos algo dentro de un for...of o solo aquellas veces que se
cumpla una condición (como en este caso ).

Ejercicio 6: La ganancia promedio


Pasemos al siguiente requerimiento de Ana. Ya podemos calcular una sumatoria de
ganancias y también crear contadores, ahora vamos a calcular promedios. 
Ana quisiera saber dado un conjunto cualquiera de balances cuál es
su gananciaPromedio.

gananciaPromedio([
{ mes: "marzo", ganancia: 8 },
{ mes: "agosto", ganancia: 10 } ])
9

 ¡Dame una pista!


¿Qué es un promedio?  Veámoslo con un ejemplo:
En un grupo de 3 personas, Felipe de 3, Delfina de 8 y Eli de 10 años, la edad promedio es
de 7 años. Porque 3 + 8 + 10 es igual a 21, dividido la cantidad de gente en el grupo (3
personas) da 7.
Para esto contás con las funciones gananciaTotal y longitud. 
SOLUCION CORRECTA
function gananciaPromedio(balancesDeUnPeriodo) {
let sumatoria=0;
{
sumatoria=gananciaTotal(balancesDeUnPeriodo)/longitud(balancesDeUnPeriodo); }
return sumatoria;}
 ¡Muy bien! Tu solución pasó todas las pruebas
OTRA SOLUCION CORRECTA
function gananciaPromedio(balancesDeUnPeriodo) {
let sumatoria=0;
for (let balance of balancesDeUnPeriodo) {
sumatoria=sumatoria+gananciaTotal(balancesDeUnPeriodo);
sumatoria=gananciaTotal(balancesDeUnPeriodo)/longitud(balancesDeUnPeriodo); }
return sumatoria; }
¡Muy bien! Tu solución pasó todas las pruebas

CONSOLA
 gananciaPromedio
=> <function>
  gananciaPromedio([ {mes: "marzo", ganancia: 8}, {mes: "agosto", ganan
cia: 10}])
=> 9
  gananciaPromedio([ {mes: "marzo", ganancia: 8}, {mes: "agosto", ganan
cia: 10}])
=> 9

Ejercicio 7: Quién gana, quién pierde


Viendo que podemos hacer todo lo que nos pide, Ana quiere saber la ganancia
promedio de los balances positivos. 
Definí las funciones:
 gananciaPositiva, que es la suma de las ganancias de los balances positivos
 promedioGananciasPositivas invocando gananciaPositiva y cantidadDeBalanc
esPositivos
  ¡Dame una pista!
 gananciaPositiva es muy similar a cantidadDeBalancesPositivos, solo que
ahora necesitas una sumatoria en vez de un contador. 

 En la Biblioteca vas a encontrar cantidadDeBalancesPositivos para ayudarte a


resolver este ejercicio. Tené en cuenta que va a estar tal y como vos la
definiste, por lo que si tiene errores, pueden impactar en tu solución actual. 
BIBLIOTECA
function cantidadDeBalancesPositivos(balancesDeUnPeriodo){
let cantidad = 0;
for (let balance of balancesDeUnPeriodo) {
if(balance.ganancia > 0){
cantidad= cantidad + 1}
}
return cantidad; }

SOLUCION CORRECTA
function gananciaPositiva(balancesDeUnPeriodo) {
let sumatoria = 0;
for (let balance of balancesDeUnPeriodo) {
if(balance.ganancia > 0){
sumatoria = sumatoria + (balance.ganancia); } }
return sumatoria }
function promedioGananciasPositivas(balancesDeUnPeriodo) {
return gananciaPositiva(balancesDeUnPeriodo) /
cantidadDeBalancesPositivos(balancesDeUnPeriodo); }
 ¡Muy bien! Tu solución pasó todas las pruebas
Como podés ver todos los promedios se basan en el mismo principio . Sumar una
cantidad determinada elementos y dividir el resultado por esa cantidad. Si
quisiéramos realizar una función promedio genérica sería algo así:

function promedio(listaDeNumeros) {
return sumatoria(listaDeNumeros) / longitud(listaDeNumeros); }
function sumatoria(listaDeNumeros) {
let sumatoria = 0;
for (let numero of listaDeNumeros) {
sumatoria = sumatoria + numero; }
return sumatoria; }

¡Pero nosotros no tenemos una lista de números sino de registros!  ¿Y


entonces? 
CONSOLA
promedioGananciasPositivas([ {mes: "marzo", ganancia: 8}, {mes: "novie
mbre", ganancia: 10}, {mes: "febrero", ganancia: 10}])
=> 9.333333333333334

Ejercicio 8: Soy el mapa, soy el mapa


Lamentablemente no se puede usar la función promedio con nuestra lista de
registros . Lo que necesitamos es una lista que tenga solo las ganancias de cada
balance. Para ello debemos transformar, o mapear, cada elemento de la lista.
Completá la función ganancias que toma una lista de balances y devuelve una lista que
solo posea solo las ganancias de cada uno.
ganancias([
{ mes: "enero", ganancia: 40 },
{ mes: "febrero", ganancia: 12 },
{ mes: "marzo", ganancia: 8}
])
[40, 12, 8]

 ¡Dame una pista!


¿Quizá necesites agregar ganancias a una lista? ¿Te acordas de agregar? 
SOLUCION CORRECTA
function ganancias(balancesDeUnPeriodo) {
let ganancias = [ ];
for (let balance of balancesDeUnPeriodo) {
agregar(ganancias, balance.ganancia) }
return ganancias; }
¡Muy bien! Tu solución pasó todas las pruebas
¡Excelente!  Ahora ya sabemos cómo transformar cada elemento de una lista para obtener
una lista nueva . De esta manera podemos usar promedio con nuestra lista de balances.
Pero, ¿se puede utilizar la función promedio solo para los balances positivos? 
CONSOLA
ganancias([{ mes: "enero", ganancia: 40}, {mes: "febrero", ga
nancia: 12}, {mes: "marzo", ganancia: 8}])
=> [40,12,8]

Ejercicio 9: A filtrar, a filtrar cada cosa en su lugar


Con la programación se puede hacer cualquier cosa, o casi . Ya hicimos una función
para poder saber la cantidad de balances positivos (cantidadDeBalancesPositivos),
ahora vamos a ver cómo podemos hacer para saber cuáles son esos balances. 
Completá la función balancesPositivos que toma los balances de un período y devuelve
una lista con aquellos cuya ganancia fue mayor a cero.
 ¡Dame una pista!
balances es una lista que contiene justamente eso, balances . Pero no todos, tienen
que cumplir una condición.
SOLUCION CORRECTA
function balancesPositivos(balancesDeUnPeriodo) {
let balances = [ ];
for (let balance of balancesDeUnPeriodo) {
if(balance.ganancia>0){
agregar(balances, balance); } }
return balances; }
 ¡Muy bien! Tu solución pasó todas las pruebas
¡Muy bien!  Ahora ya sabemos cómo filtrar una lista. En criollo, aprendimos a
obtener los elementos de una lista que cumplen una condición determinada. En
este caso obtuvimos una nueva lista con los balances que presentaban una
ganancia positiva. 

Ejercicio 10: Un promedio más positivo


Ahora que tenemos la función ganancias y balancesPositivos podemos utilizar la
función promedio genérica para saber cuál es el promedio de ganancia de los
balances positivos.
Definí la función gananciasDeBalancesPositivos y luego usala junto a promedio para
definir promedioDeBalancesPositivos.
 ¡Dame una pista!
Para poder tener la lista que recibe por parámetro promedio vas a tener que
definir gananciasDeBalancesPositivos. Esta función primero filtra los balances
positivos y luego los mapea a ganancias. Recordá que función hace cada una de
estas cosas. 
En la Biblioteca vas a encontrar algunas de las funciones de ejercicios
anteriores para ayudarte a resolver este ejercicio. Tené en cuenta que van a estar
tal y como vos las definiste, por lo que si tienen errores, pueden impactar en tu
solución actual. 
BIBLIOTECA
function ganancias(balancesDeUnPeriodo) {
let ganancias = [ ];
for (let balance of balancesDeUnPeriodo) {
agregar(ganancias, balance.ganancia) }
return ganancias; }
function balancesPositivos(balancesDeUnPeriodo) {
let balances = [ ];
for (let balance of balancesDeUnPeriodo) {
if(balance.ganancia>0){
agregar(balances, balance); } }
return balances; }
function promedio(listaDeNumeros) {
return sumatoria(listaDeNumeros) / longitud(listaDeNumeros); }
function sumatoria(listaDeNumeros) {
let sumatoria = 0;
for (let numero of listaDeNumeros) {
sumatoria = sumatoria + numero;
}
return sumatoria;
}

SOLUCION CORRECTA
function gananciasDeBalancesPositivos(balancesDeUnPeriodo) {
return ganancias(balancesPositivos(balancesDeUnPeriodo)); }
function promedioDeBalancesPositivos(balancesDeUnPeriodo){
return promedio(gananciasDeBalancesPositivos(balancesDeUnPeriodo)); }
¡Muy bien! Tu solución pasó todas las pruebas

CONSOLA
promedio([ {mes: "enero", ganancia: 40}, {mes: "febrero", ganancia: 12
}, {mes: "marzo", ganancia: 8}])
=> null ESTO ESTA MAL
VA ESTO EN LA CONSOLA
 promedioDeBalancesPositivos([ {mes: "enero", ganancia: 40}, {mes: "fe
brero", ganancia: 12}, {mes: "marzo", ganancia: 8}])
=> 20

Ejercicio 11: Esto es lo máximo


Vamos a conocer una nueva función, maximo, que nos permite conocer cuál es el
mayor valor en una lista de números. Por ejemplo:

maximo([5, 8, 10, 42, 87, 776])


776

Usando esta nueva función, definí la función maximaGanancia que nos diga cuál es la


ganancia más alta entre los balances de un período de tiempo.

maximaGanancia([
{ mes: "enero", ganancia: 87 },
{ mes: "febrero", ganancia: 12 },
{ mes: "marzo", ganancia: 8} ])
87

 ¡Dame una pista!


Podés usar la función ganancias que hiciste antes . Tené en cuenta que va a estar tal
y como vos la definiste, por lo que si tiene errores, pueden impactar en tu solución
actual. 
SOLUCION CORRECTA
function maximaGanancia(balancesDeUnPeriodo){
return maximo(ganancias(balancesDeUnPeriodo)) }
¡Muy bien! Tu solución pasó todas las pruebas

CONSOLA
  maximaGanancia([ {mes: "enero", ganancia: 87}, {mes: "febrero", ganan
cia: 12}, {mes: "marzo", ganancia: 95}])
=> 95
Si hay una función para calcular el máximo de una lista también hay una para
calcular el mínimo. ¿Te imaginás como se llama?

Ejercicio 12: Como mínimo


Suponemos que adivinaste el nombre. En caso que no, es minimo. 
Definí la función minimaGananciaPositiva que nos diga cuál es la ganancia más baja de
todos los balances positivos.

minimaGananciaPositiva([
{ mes: "enero", ganancia: -40 },
{ mes: "febrero", ganancia: 42 },
{ mes: "marzo", ganancia: 8},
{ mes: "abril", ganancia: -5} ])
8

 ¡Dame una pista!


Para poder hacer esta nueva función probablemente te sirva
tanto minimo como gananciasDeBalancesPositivos. 
minimo se utiliza como maximo:

minimo([5, 8, 10, 42, 87, 776]) 5

En la Biblioteca vas a encontrar algunas de las funciones de ejercicios anteriores


para ayudarte a resolver este ejercicio. Tené en cuenta que van a estar tal y como
vos las definiste, por lo que si tienen errores, pueden impactar en tu solución
actual. 
BIBLIOTECA
function ganancias(balancesDeUnPeriodo) {
let ganancias = [];
for (let balance of balancesDeUnPeriodo) {
agregar(ganancias, balance.ganancia)
}
return ganancias; }
function balancesPositivos(balancesDeUnPeriodo) {
let balances = [ ];
for (let balance of balancesDeUnPeriodo) {
if(balance.ganancia>0){
agregar(balances, balance); } }
return balances; }
function gananciasDeBalancesPositivos(balancesDeUnPeriodo)
{
return ganancias(balancesPositivos(balancesDeUnPeriodo)); }
function promedioDeBalancesPositivos(balancesDeUnPeriodo){
return promedio(gananciasDeBalancesPositivos(balancesDeUnPeriodo)); }
function minimo(numeros) /* ... */
// Retorna el menor valor dentro de una lista de números:
//
// minimo([5, 7, 8, 9, 3])
// => 3

SOLUCION CORRECTA
function minimaGananciaPositiva(balancesDeUnPeriodo){
return minimo(gananciasDeBalancesPositivos(balancesDeUnPeriodo)); }
¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
 minimaGananciaPositiva([ {mes: "enero", ganancia: -40}, {mes: "febrer
o", ganancia: 42}, {mes: "marzo", ganancia: 8}, {mes: "abril", gananci
a: 3}])
=> 3
Muy bien!  Solo queda un ejercicio por delante.

Ejercicio 13: Los mejores meses del año


¡Vamos a terminar esta lección con todo! 
Para eso vamos a hacer las siguientes funciones:
 meses, la cual dada una lista con registros devuelve una lista de meses ;
 afortunados, que filtra aquellos registros que tuvieron una ganancia mayor a
$1000 ;
 mesesAfortunados, devuelve aquellos meses que fueron afortunados.

meses([
{ mes: "enero", ganancia: 870 },
{ mes: "febrero", ganancia: 1000 },
{ mes: "marzo", ganancia: 1020 },
{ mes: "abril", ganancia: 2300 },
{ mes: "mayo", ganancia: -10 } ])
["enero", "febrero", "marzo", "abril", "mayo"]
afortunados([
{ mes: "enero", ganancia: 870 },
{ mes: "febrero", ganancia: 1000 },
{ mes: "marzo", ganancia: 1020 },
{ mes: "abril", ganancia: 2300 },
{ mes: "mayo", ganancia: -10 } ])
[ { mes: "marzo", ganancia: 1020 }, { mes: "abril", ganancia: 2300 }]

mesesAfortunados([
{ mes: "enero", ganancia: 870 },
{ mes: "febrero", ganancia: 1000 },
{ mes: "marzo", ganancia: 1020 },
{ mes: "abril", ganancia: 2300 },
{ mes: "mayo", ganancia: -10 } ])
["marzo", "abril"]

Definí las funciones meses, afortunados, mesesAfortunados.


 ¡Dame una pista!
En la función meses es necesario mapear, en afortunados filtrar y
en mesesAfortunados tenés que usar las dos anteriores. 
SOLUCION CORRECTA
function meses(balancesDeUnPeriodo){
let meses= [];
for (let balance of balancesDeUnPeriodo){
agregar (meses,balance.mes); }
return meses; }
function afortunados(balancesDeUnPeriodo){
let afortunados = [];
for (let balance of balancesDeUnPeriodo){
if (balance.ganancia>1000){
agregar (afortunados,balance); } }
return afortunados; }
function mesesAfortunados(balancesDeUnPeriodo){
return meses(afortunados(balancesDeUnPeriodo)); }
 ¡Muy bien! Tu solución pasó todas las pruebas
CONSOLA
meses([
    { mes: "enero", ganancia: 870 }, 
    { mes: "febrero", ganancia: 1000 }, 
    { mes: "marzo", ganancia: 1020 }, 
    { mes: "abril", ganancia: 2300 }, 
    { mes: "mayo", ganancia: -10 }
  ])
=> ["enero","febrero","marzo","abril","mayo"]
  afortunados([
      { mes: "enero", ganancia: 870 }, 
      { mes: "febrero", ganancia: 1000 }, 
      { mes: "marzo", ganancia: 1020 }, 
      { mes: "abril", ganancia: 2300 }, 
      { mes: "mayo", ganancia: -10 }
    ])
=> [{mes:"marzo",ganancia:1020},{mes:"abril",ganancia:2300}]
  mesesAfortunados([
    { mes: "enero", ganancia: 870 }, 
    { mes: "febrero", ganancia: 1000 }, 
    { mes: "marzo", ganancia: 1020 }, 
    { mes: "abril", ganancia: 2300 }, 
    { mes: "mayo", ganancia: -10 }
  ])
=> ["marzo","abril"]
¡Terminaste Recorridos!
En esta última lección te reencontraste con el for...of y resolviste problemas más
complejos con él . Más especificamente creaste funciones para filtrar y/o
transformar los elementos de una lista.

Apéndice
Referencia rápida del lenguaje JavaScript
El lenguaje JavaScript es utilizado ampliamente para construir software en todo el mundo,
siendo una de las principales tecnologías de la Web. En Mumuki sólo usamos una muy
pequeña parte del mismo, que listamos a continuación:
Declaración de Funciones
A partir de la Lección 1: Funciones y tipos de datos
Las funciones en JavaScript se declaran mediante la palabra clave function, y su
cuerpo va entre llaves { y }:
function nombreDeLaFuncion(parametro1, parametro2, parametro3) {
return ...; }

Toda función debe tener al menos un retorno, que se expresa mediante return.


Operadores matemáticos
A partir de la Lección 1: Funciones y tipos de datos
4+5
10 - 5
8*9
10 / 5

Operadores lógicos
A partir de la Lección 1: Funciones y tipos de datos
true && false
true || false
! false

Comparaciones
A partir de la Lección 1: Funciones y tipos de datos
// para cualquier tipo de dato
"hola" === "hola"
"hola" !== "chau"
// para números
4 >= 5
4>5
4 <= 5
4<5

Alternativa Condicional
A partir de la Lección 1: Funciones y tipos de datos
Los ifs en JavaScript encierran la condición entre paréntesis y su cuerpo entre
llaves:
if (hayPersonasEnEspera( )) {
llamarSiguientePersona( ); }

Además, los ifs pueden opcionalmente tener un else:


if (hayPersonasEnEspera()) {
llamarSiguientePersona();
} else {
esperarSiguientePersona(); }

Por último, podemos combinar varios ifs para tomar decisiones ante múltiples
condiciones:
if (hayPersonasEnEspera()) {
llamarSiguientePersona();
} else if (elPuestoDebeSeguirAbierto()) {
esperarSiguientePersona();
} else {
cerrarPuesto(); }

Variables
A partir de la Lección 3: Variables y procedimientos
Las variables nos permiten recordar valores y se declaran mediante la palabra
reservada let y se les da un valor inicial usando =:
let pesosEnMiBilletera = 100;
let diasQueFaltanParaElVerano = 10;

La mismas se asignan mediante =:


pesosEnMiBilletera = 65;
diasQueFaltanParaElVerano = 7;
En ocasiones las asignaremos usando el valor anterior:

pesosEnMiBilletera = pesosEnMiBilletera * 2;
diasQueFaltanParaElVerano = diasQueFaltanParaElVerano - 1;

La asignación anterior se puede compactar combinando el signo = y la operación:

pesosEnMiBilletera *= 2;
diasQueFaltanParaElVerano -= 1;

Repetición indexada
A partir de la Lección 7: Recorridos
Las listas pueden ser recorridas, visitando y haciendo algo con cada uno de sus
elementos. Para ello contamos con la estructura de control for..of, que encierra su
generador entre paréntesis (( y )) y su cuerpo entre llaves ({ y }):

let patrimoniosDeLaHumanidad = [
{declarado: 1979, nombre: "Parque nacional Tikal", pais: "Guatemala"},
{declarado: 1983, nombre: "Santuario histórico de Machu Picchu", pais: "Perú"}
{declarado: 1986, nombre: "Parque nacional do Iguaçu", pais: "Brasil"},
{declarado: 1995, nombre: "Parque nacional de Rapa Nui", pais: "Chile"},
{declarado: 2003, nombre: "Quebrada de Humahuaca", pais: "Argentina"} ]

let cantidadPatrimoniosDeclaradosEnEsteSiglo = 0;
for (let patrimonio of patrimoniosDeLaHumanidad) {
if (patrimonio.declarado >= 2000) {
cantidadPatrimoniosDeclaradosEnEsteSiglo += 1; } }

Biblioteca simplificada
Dentro de Mumuki usamos una biblioteca de funciones inspirada en la que ya viene con
JavaScript, pero simplifiacada para que sea más sencilla y segura de usar. A continuación
listamos las principales funciones que se pueden usar, indicando el equivalente real en
JavaScript cuando corresponda.
longitud(unString)
A partir de la Lección 1: Funciones y tipos de datos
Versión simplificada de length
Uso:

longitud("hola")
4

convertirEnMayuscula(unString)
A partir de la Lección 1: Funciones y tipos de datos
Versión simplificada de toUpperCase
Convierte un unString en mayúsculas:
convertirEnMayuscula("hola")
"HOLA"

comienzaCon(unString, otroString)
A partir de la Lección 1: Funciones y tipos de datos
Versión simplificada de startsWith
Dice si unString empieza con otroString:
comienzaCon("aprendiendo a programar", "aprendiendo")
true
comienzaCon("aprendiendo a programar", "aprend")
true
comienzaCon("aprendiendo a programar", "programar")
false
comienzaCon("aprendiendo a programar", "tomar el té")
false

imprimir(unString)
A partir de la Lección 3: Variables y procedimientos
Versión simplificada de console.log
Imprime por pantalla unString:

imprimir("¡estoy imprimiendo!")
¡estoy imprimiendo!

tirarDado( )
A partir de la Lección 3: Variables y procedimientos
Devuelve al azar un número entre 1 y 6:

tirarDado()
5
tirarDado()
1
tirarDado()
2

listasIguales(unaLista, otraLista)
A partir de la Lección 5: Listas

listasIguales([1,4,7], [1,4,7])
true
listasIguales([1,4,7], [1,4,8])
false

longitud(unaLista)
A partir de la Lección 5: Listas
 length de listas
Nos dice cuan largo es unaLista:

longitud([true, false, false, true])


4
longitud([5, 6, 3])
3

agregar(unaLista, unElemento)
A partir de la Lección 5: Listas
Versión simplificada de push
Inserta unElemento al final de unaLista. Este es un procedimiento que no devuelve
nada pero modifica a unaLista:

let cancionesFavoritas = ["La colina de la vida", "Zamba por vos"]


// agrega el elemento "Seminare" a la lista cancionesFavoritas
agregar(cancionesFavoritas, "Seminare")
// ahora la lista tiene un elemento más:
cancionesFavoritas
["La colina de la vida", "Zamba por vos", "Seminare"]

remover(unaLista)
A partir de la Lección 5: Listas Versión simplificada de pop
Quita el último elemento de unaLista. Este es un procedimiento que no devuelve nada
pero modifica a unaLista:

let listaDeCompras = ["leche", "pan", "arroz", "aceite", "yerba"]


// removemos el último elemento
remove(listaDeCompras)
// la "yerba" ya no está en lista de compras
listaDeCompras
["leche", "pan", "arroz", "aceite"]

posicion(unaLista, unElemento)
A partir de la Lección 5: Listas
Versión simplificada de indexOf
Nos dice en qué posición se encuentra unElemento dentro de unaLista. Si el elemento no
está en la lista, devuelve -1

let premios = ["dani", "agus", "juli", "fran"]


posicion(premios, "dani")
0
posicion(premios, "juli")
2
posicion(premios, "feli")
-1

Bibliografía complementaria
 https://developer.mozilla.org/es/docs/Web/JavaScript
 https://es.javascript.info/

PROGRAMACION CON OBJETOS


Ejercicio 1: Fijando nuestro objetivo
Anteriormente hablamos de los paradigmas de programación. En este capítulo
vamos a ver otra forma de pensar el mundo de la programación.
El paradigma de programación con objetos o programación orientada a objetos nos
propone tratar con…¡Adiviná! Sí, nos permite trabajar con objetos. 
En este video te contamos qué son esos famosos objetos y cómo interactúan entre sí.

Ejercicio 2: ¡Hola Pepita!


Para empezar en este mundo, conozcamos a Pepita, una golondrina.
Pepita, además de ser un ave que come y vuela (como todo pájaro), es un objeto,
que vive en el mundo de los objetos, al cual conocemos como ambiente.
¿No nos creés que Pepita está viva y es un objeto? Escribí en la consola Pepita y fijate qué
sucede. Cuando te convenzas, pasá al siguiente ejercicio.
 ¡Dame una pista!
La consola es el recuadro que tenés a la derecha del texto, y sirve para hacer
pruebas rápidas. 
Usarla es muy sencillo: escribí lo que querés probar y luego presioná la
tecla Enter para ver su resultado. Si alguna vez usaste alguna terminal, como la
de GNU/Linux, la mecánica es la misma.
Pepita
=> Pepita

Ejercicio 3: Un mundo de objetos


Como vimos, Pepita es un objeto. Pero Pepita no está sola en este mundo. ¡Hay
muchos más!
Por ejemplo, existe otra golondrina, llamada Norita, que también vive en
este ambiente.
Como ya te vendrás dando cuenta, en este paradigma bajo el cual estamos trabajando
absolutamente todo es un objeto: también los números, las cadenas de texto (o strings) y
los booleanos.
Probalo! Hacé las siguientes consultas en la consola:

Pepita
Norita
87
'hola mundo'
true

De todas formas tené cuidado. A Pepita y Norita las creamos por vos. Y los números y
booleanos vienen de regalo. Si probás con otra cosa, como por ejemplo Felix, te va a tirar
un error.
 Pepita
=> Pepita
  Norita
=> Norita
  87
=> 87
'hola mundo'
=> "hola mundo"
  true
=> true

Ejercicio 4: El derecho a la Identidad


Un aspecto muy importante de los objetos es que tienen identidad: cada objeto
sabe quién es y gracias a esto sabe también que es diferente de los demás. Por
ejemplo, Pepita sabe que ella es diferente de Norita, y viceversa.
En Ruby, podemos comparar por identidad a dos objetos utilizando el
operador == de la siguiente forma:

Pepita == Norita

¡Intentalo por tu propia cuenta! Ejecutá las siguientes pruebas en la consola:

Pepita == Norita
Norita == Pepita
Norita == Norita
"hola" == "chau"

 ¡Dame una pista!


¿Te cansaste de escribir? Te dejamos un truquito para usar la consola más rápido: si
apretás la flecha para arriba , se repite lo último que escribiste. 
CONSOLA
Pepita == Norita
=> false
  Norita == Pepita
=> false
  Norita == Norita
=> true
  "hola" == "chau"
=> false
Ejercicio 5: Mensajes, primera parte
Ya entendimos que en un ambiente hay objetos, y que cada uno de ellos
tiene identidad: sabe que es diferente de otro.
Pero esto no parece ser muy útil. ¿Qué cosas sabrá hacer una golondrina
como Pepita? ¿Sabrá, por ejemplo, cantar!? 
Averigualo: enviale un mensaje cantar! y fijate qué pasa...

Pepita.cantar!

 ¡Dame una pista!


El signo de exclamación ! es parte del nombre del mensaje, no te olvides de
ponerlo. 
CONSOLA
Pepita.cantar!
=> "pri pri pri"

Ejercicio 6: Mensajes, segunda parte


Ehhhh, ¿qué acaba de pasar acá?  Para comunicarnos con los objetos, debemos
enviarles mensajes. Cuando un objeto recibe un mensaje, este responde haciendo
algo. En este caso, Pepita produjo el sonido de una golondrina: pri pri
pri ...imaginate acá que escuchamos este sonido... .
¿Qué mas sabrá hacer Pepita? ¿Sabrá, por ejemplo, bailar!? 
¡Descubrámoslo! Enviale el mensaje bailar!

Pepita.bailar!

Ejercicio 5: Mensajes, segunda parte


Wow, ¿que acaba de suceder acá? 
Para comunicarte con los objetos, le podés enviar mensajes. Cuando un objeto recibe un
mensaje, responde de alguna manera, en este caso con su canto típico.
¿Qué más sabrá le querés pedir a pepita? ¿Sabrá, por ejemplo, saludar?
¡Descubrámoslo! Enviale el mensaje saludar()

pepita.saludar()

pepita.saludar( )

pepita does not understand saludar()

Ejercicio 6: ¡No entiendo!


¡Buu, pepita no sabía saludar! 
En el mundo de los objetos, no podés pretender que un objeto sepa hacer de
todo... sólo tiene sentido enviarle ciertos mensajes a un objeto, aquellos que el
objeto entiende. Si el objeto sabe hacer algo como reacción a ese mensaje, lo hará.
De lo contrario se lanzará un error:

objeto pepita does not understand saludar()

Descubramos qué otras cosas sabe responder pepita. Probá enviarle los siguientes
mensajes y fijate cuáles entiende, cuales no y qué es lo que responde. ¡Y anotalos!, este
conocimiento nos servirá en breve.
  pepita.pasear()
  pepita.energia()
  pepita.estasFeliz()
  pepita.cantidadDeOjivasNucleares()
 pepita.pasear()
 pepita does not understand pasear()
   pepita.energia()
 => 100
   pepita.estasFeliz()
 => true
   pepita.cantidadeOjivasNucleares
 ERROR: Bad message: cantidadeOjivasNucleares)

Ejercicio 7: No entendí...
¡Buu, Pepita no sabía bailar! 
En el mundo de los objetos, sólo tiene sentido enviarle un mensaje a un objeto si lo
entiende, es decir, si sabe hacer algo como reacción a ese mensaje. De lo contrario,
se lanzará un error un poco feo (y en inglés ) como el siguiente:

undefined method `bailar!' for Pepita:Module

Descubramos qué otras cosas sabe hacer Pepita. Probá enviarle los siguientes mensajes y
fijate cuáles entiende y cuales no ¡y anotalos!  Este conocimiento nos servirá en breve.
Pepita.comer_lombriz!
=> nil
  Pepita.volar_en_circulos!
=> nil
  Pepita.se_la_banca?
undefined method `se_la_banca?' for Pepita:Module (NoMethodError)

Ejercicio 8: Un poco de sintaxis


¡Pausa! Analicemos la sintaxis del envío de mensajes:
1. Pepita.energia es un envío de mensaje, también llamado colaboración;
2. energia es el mensaje;
3. energia es el nombre del mensaje (en este caso es igual, pero ya
veremos otros en los que no);
4. Pepita es el objeto receptor del mensaje.
 Es importante respetar la sintaxis del envío de mensajes. Por ejemplo, las siguientes NO
son colaboraciones validas, porque no funcionan o no hacen lo que deben:
CONSOLA
energia
Pepita energia
Pepita..energia

¿Eh, no nos creés?  ¡Probalas!


CONSOLA DEL EJERCICIO
energia
undefined local variable or method `energia' for main:Object
(NameError)
  Pepita energia
undefined local variable or method `energia' for main:Object
(NameError)
  Pepita..energia
undefined local variable or method `energia' for main:Object
(NameError)

Ejercicio 7(8): Un poco de sintaxis


¡Pausa! Analicemos la forma correcta de escribir el envío de mensajes, es
decir su sintaxis:
1. pepita.volar( ) es un envío de mensaje, también llamado colaboración
2. volar( ) es el mensaje
3. pepita es el objeto receptor del mensaje
 Es importante respetar la sintaxis del envío de mensajes. Por ejemplo, las
siguientes NO son colaboraciones validas, porque no funcionan o no hacen lo que
deben:
  volar( )
  volar.pepita( )
  pepita volar( )
  pepita.volar
¿Eh, no nos creés?  ¡Probalo!

volar()
ERROR: extraneous input '(' expecting ')'
ERROR: Couldn't resolve reference to Referenciable 'volar'.
  volar.pepita()
ERROR: Couldn't resolve reference to Referenciable 'volar'.
  pepita volar()
ERROR: missing ')' at 'volar'
ERROR: no viable alternative at input ')'
ERROR: extraneous input ')' expecting '}'
ERROR: Couldn't resolve reference to Referenciable 'volar'.
  pepita.volar
ERROR: Bad message: volar)

Ejercicio 9: Interfaz
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:
 energia: nos dice cuanta energía tiene (un número);
 cantar!: hace que cante;
 comer_lombriz!: hace que coma una lombriz;
 volar_en_circulos!: hace que vuele en circulos.
Lo cual también se puede graficar de la siguiente forma:

¡Un momento! ¿Por qué algunos mensajes terminan en ! y otros no? Enviá nuevamente
esos mensajes. Fijate qué devuelve cada uno (lo que está a la derecha del = >) y tratá de
descubrir el patrón.
 ¡Dame una pista!
nil es la forma que tenemos en Ruby de representar a "la nada" (que, casualmente,
¡también es un objeto!).
CONSOLA
Pepita.energia
=> 100
  Pepita.cantar!
=> "pri pri pri"
  Pepita.comer_lombriz!
=> nil
  Pepita.volar_en_circulos!
=> nil
   

Ejercicio 10: Hacer versus Devolver


Cuando se envía un mensaje a un objeto, y este lo entiende, puede reaccionar de
dos formas diferentes:
 Podría producir un efecto, es decir hacer algo. Por ejemplo, el
mensaje cantar! reproduce el sonido del canto de Pepita.
 O también podría devolver otro objeto. Por ejemplo el
mensaje energia devuelve siempre un número.
 En realidad, un mensaje podría reaccionar con una combinación de las formas
anteriores: tener un efecto y devolver algo. Pero esto es una muy mala idea.
¿Y qué hay de los mensajes como comer_lombriz! y volar_en_circulos!? ¿Hicieron algo? ¿Qué
clase de efecto produjeron? ¿Devuelve energia siempre lo mismo? 
Descubrilo: enviale a Pepita esos tres mensajes varias veces en distinto orden y fijate si
cambia algo.
CONSOLA
 Pepita.comer_lombriz!
=> nil
  Pepita.volar_en_circulos!
=> nil
Pepita.energia
=> 110
  Pepita.volar_en_circulos!
=> nil
  Pepita.comer_lombriz!
=> nil

Ejercicio 11: Tu primer programa con objetos


¡Exacto! El efecto que producen los mensajes comer_lombriz! y volar_en_circulos! es
el de alterar la energía de Pepita. En concreto:
 comer_lombriz! hace que la energia de Pepita aumente en 20 unidades;
 volar_en_circulos! hace que la energia de Pepita disminuya en 10 unidades.
Como convención, a los mensajes con efecto (es decir, que hacen algo) les
pondremos un signo de exclamación ! al final.
Veamos si se entiende: escribí un primer programa que consista en hacer que Pepita coma y vuele
hasta quedarse con 150 unidades de energía. Acordate que Pepita arranca con la energía
en 100.
 ¡Dame una pista!
No te olvides de que siempre tenés que poner el objeto receptor del mensaje. Por
ejemplo esto es válido:

Pepita.energia

pero esto no:

energia

SOLUCION CORRECTA
1. Pepita.comer_lombriz!
2. Pepita.volar_en_circulos!
3. Pepita.comer_lombriz!
4. Pepita.volar_en_circulos!
5. Pepita.comer_lombriz!
6. Pepita.volar_en_circulos!
7. Pepita.comer_lombriz!
¡Muy bien! Tu solución pasó todas las pruebas
Podemos sacar dos conclusiones:
1. Los objetos no reaccionan necesariamente siempre igual a los mismos
mensajes. Podrían hacer cosas diferentes, o en este caso, devolver objetos
distintos.
2. ¡Un programa es simplemente una secuencia de envío de mensajes!
Ejercicio 11: ¿Quién te entiende?
Ya vimos que un objeto puede entender múltiples mensajes, y esos mensajes
conforman su interfaz.
¿Pero podría haber más de un objeto que entienda los mismos mensajes?
A pepita ya la conocemos bien: canta, come, etc. Su amiga anastasia, por otro lado, no
aprendió nunca a decirnos su energía. Y mercedes es una reconocida cantora.
Usando la consola, averiguá cuál es la interfaz de cada una de ellas.
 ¡Dame una pista!
Probá con los mismos mensajes que entiende pepita
 anastasia.energia()
anastasia does not understand energia()
  mercedes.cantar()
=> ♪ una voz antigua de viento y de sal ♫
   

Ejercicio 12: ¿Quién te entiende?


Ya vimos que un objeto puede entender múltiples mensajes, y esos mensajes conforman su
interfaz.
¿Pero podría haber más de un objeto que entienda los mismos mensajes?
A Pepita ya la conocemos bien: canta, come, etc. Su amiga Norita, por otro lado, no
aprendió nunca a decirnos su energía. Y Mercedes es una reconocida cantora.
Usando la consola, averiguá cuál es la interfaz de cada una de ellas, y completá el listado de
mensajes que cada una entiende en el editor.
¡Dame una pista!
Buscá la Consola en la pestaña de al lado de tu Solución y probá de enviarle a cada objeto
los mismos mensajes que Pepita entiende.
Cuando descubras cuáles son, completá la solución con el mismo formato que viene la de
Pepita.
Solución correcta
interfaz_pepita = %w(
energia
cantar!
comer_lombriz!
volar_en_circulos! )
interfaz_norita = %w(
cantar!
comer_lombriz!
volar_en_circulos! )

interfaz_mercedes = %w(
cantar! )

¡Muy bien! Tu solución pasó todas las pruebas


¡Así es! Puede haber más de un objeto que entienda el mismo mensaje. Notá que sin
embargo no todos los objetos están obligados a reaccionar de igual forma ante el mismo
mensaje:
Pepita.cantar!
=> "pri pri pri"
Norita.cantar!
=> "priiiip priiiip"
Mercedes.cantar!
=> "♪ una voz antigua de viento y de sal ♫"
Esto significa que dos o más objetos pueden entender un mismo mensaje,
pero pueden comportarse de formas diferentes. Ya hablaremos más de esto
en próximas lecciones.
CONSOLA
Pepita.cantar!
=> "pri pri pri"
  Norita.cantar!
=> "priiiip priiiip"
  Mercedes.cantar!
=> "♪ una voz antigua de viento y de sal ♫"

Ejercicio 13: Interfaces compartidas


Veamos si queda claro, siendo que las interfaces de Norita, Pepita y Mercedesson las
siguientes:

Esto significa que comparten algunos mensajes y otros no. ¿Qué interfaces comparten entre
ellas?
Completá el código en el editor.
¡Dame una pista!
Recordá que la interfaz es el conjunto de mensajes que un objeto entiende. Por lo tanto, si
queremos ver cual interfaz comparten dos objetos, tenemos que pensar en la intersección
entre los conjuntos de mensajes de cada uno (es decir, aquellos que son iguales).
SOLUCION CORRECTA
# ¿Qué interfaz comparten Mercedes y Norita?
interfaz_compartida_entre_mercedes_y_norita = %w(
cantar! )
# ¿Qué interfaz comparten Pepita y Norita?
interfaz_compartida_entre_pepita_y_norita = %w(
cantar!
comer_lombriz!
volar_en_circulos! )
# ¿Qué interfaz comparten Mercedes, Norita y Pepita?
interfaz_compartida_entre_todas = %w(
cantar! )

¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 12: Interfaces compartidas


Veamos si queda claro: las interfaces de los objetos son las siguientes:
pepita:
 energia()
 cantar()
 comerLombriz()
 volarEnCirculos()
 estasFeliz()

anastasia:
 cantar()
 comerLombriz()
 volarEnCirculos()

mercedes:
 cantar()
Esto significa que comparten algunos mensajes y otros no, que en algunos casos le podes
hacer las mismas preguntas a diferentes objetos y cada uno responde a su manera. Pero
tampoco le podes pedir cualquier cosa a todo objeto. Depende de lo que cada objeto sepa
hacer.
Comprobá que todos los objetos canten y que mercedes no sabe nada de vuelo.
 pepita.cantar()
=> pri pri pri
  anastasia.cantar (este error me lo dio por no poner los parentesis
ERROR: Bad message: cantar)
  anastasia.cantar()
=> priiiip priiiip
  mercedes.volarEnCirculos()
mercedes does not understand volarEnCirculos()

Ejercicio 13: Argumentos


Para hacer cosas más interesantes, vamos a necesitar mensajes más complejos .
Por ejemplo, queremos que pepita coma una cierta cantidad de alpiste, y que esa cantidad
no sea fija o predeterminada sino que nos gustaría indicarla a nosotros al momento de
hacer que coma. Esto podemos escribirlo de la siguiente forma:

pepita.comerAlpiste(40)

Allí, 40 es un argumento del mensaje, representa en este caso que vamos a alimentar a


pepita con 40 (gramos) de alpiste.
También, un mensaje podría tener varios argumentos, y en este caso se separan con
comas.
Probá enviar los siguientes envíos de mensajes:
pepita.volarHacia(rosario)
pepita.comerAlpiste(39)
pepita.comerAlpisteYVolarHacia(10, santaFe)
pepita.comerAlpiste(6, anastasia)
pepita.comerAlpiste()
 pepita.volarHacia(rosario),pepita.comerAlpiste(39)
mumukiPrettyPrinter[] does not understand prettyPrint(param1, param2).
However other methods exist with different argument count:
prettyPrint(anObject)
  pepita.volarHacia(rosario)
ERROR: mismatched input ',' expecting '}'
ERROR: Bad structure: catch definition must be before object.
  pepita.comerAlpisteYVolarHacia(10, santaFe)
ERROR: mismatched input ',' expecting '}'
ERROR: Bad structure: catch definition must be before object.
  pepita.comerAlpiste(6, anastasia)
ERROR: mismatched input ',' expecting '}'
ERROR: Bad structure: catch definition must be before object.
  pepita.comerAlpiste()
ERROR: mismatched input ',' expecting '}'
ERROR: Bad structure: catch definition must be before object.
   

Ejercicio 14: Más argumentos


Como ves, si envías un mensaje con una cantidad incorrecta de argumentos...

pepita.comerAlpiste(6, anastasia)
objeto pepita does not understand comerAlpiste(p0,p1)

pepita.comerAlpiste()
objeto pepita does not understand comerAlpiste()

...el envío del mensaje fallará en ambos casos.


Dicho de otra forma, un mensaje queda identificado no sólo por su nombre sino
también por su la cantidad de argumentos: no es lo
mismo comerAlpiste() que comerAlpiste(56) que comerAlpiste(5, 6), son todos mensajes
distintos. Y en este caso, pepita sólo entiende el de un solo argumento.
Veamos si va quedando claro: escribí un programa que haga que pepita coma 500
gramos de alpiste, vuele a buenosAires, y finalmente vuelva a rosario.
Dame una pista!
Tené en cuenta que nuestra golondrina entiende también los siguientes mensajes:
 volarHacia, que toma una ciudad
 comerAlpiste, que toma una cantidad de alpiste
Y que además, existen los objetos rosario y buenosAires
CONSOLA
  pepita.comerAlpiste(500)

=>
  pepita.volarHacia(buenosAires)
=>
  pepita.volarHacia(rosario)
=>
   
SOLUCION
pepita.comerAlpiste(500)
pepita.volarHacia(buenosAires)
pepita.volarHacia(rosario)
¡Muy bien! Tu solución pasó todas las pruebas
Ejercicio 15: Más argumentos
Como ves, si enviás un mensaje con una cantidad incorrecta de argumentos...
Pepita.comer_alpiste! 6, Norita
# wrong number of arguments (2 for 1) (ArgumentError)
...el envío del mensaje también fallará.
Dicho de otra forma, un mensaje queda identificado no sólo por su nombre sino también
por la cantidad de parámetros que tiene: no es lo mismo comer_alpiste! que
comer_alpiste! 67 que comer_alpiste! 5, 6, son todos mensajes distintos. Y en este caso,
Pepita sólo entiende el segundo.
Veamos si va quedando claro: escribí un programa que haga que Pepita coma 500 gramos
de alpiste, vuele a Iruya, y finalmente vuelva a Obera.
¡Dame una pista!
Tené en cuenta que nuestra golondrina entiende también los siguientes mensajes:
 volar_hacia!, que espera como argumento una ciudad;
 comer_alpiste!, que espera como argumento una cantidad de gramos de alpiste;
Y que además, existen los objetos Iruya y Obera.
SOLUCION CORRECTA
Pepita.comer_alpiste! (500)
Pepita.volar_hacia!(Iruya)
Pepita.volar_hacia!(Obera)
¡Muy bien! Tu solución pasó todas las pruebas
¡Perfecto!
Un detalle: en Ruby, a veces, los paréntesis son opcionales. Por eso, cuando no sean
imprescindibles los omitiremos.
Ejercicio 15: Sólo objetos y mensajes
Cuando trabajamos en un ambiente de objetos, no sólo todas las "cosas" son
objetos, sino que además todas las comunicaciones que se den entre nosotros y
los objetos serán a través de mensajes.
Pero momento, ¿no dijimos antes que los números, los strings, los booleanos son
objetos?  Si sólo podemos comunicarnos con los objetos mediante mensajes,
¿significa entonces que las operaciones matemáticas, booleanas, etc, son mensajes
también?
¡Exacto!  Aunque se vean un poco diferentes, todos los siguientes son envíos de
mensajes:

5+6
4>6
true || false
! true
"El cisne " + "negro"
"quémese después de leerse".capitalize()
8.max(10)
5.abs()

Probá estos mensajes en la consola


5 + 6
=> 11
  4 > 6
=> false
  ! true

=> false
  "El cisne" + "negro"
=> El cisnenegro
  "quemese despues de leerse".capitalize()
=> Quemese Despues De Leerse
  .max(10)
8.max(10)
ERROR: missing ')' at '.'
ERROR: extraneous input ')' expecting '}'
  .abs()
ERROR: missing ')' at '.'
ERROR: extraneous input ')' expecting '}'
  true || false
=> true

Ejercicio 16: Mensajes por todas partes


Es fácil ver que en Pepita.volar_hacia! Barreal el objeto receptor es Pepita, el
mensaje volar_hacia! y el argumento Barreal; pero ¿dónde queda eso de objeto y
mensaje cuando hacemos, por ejemplo, 2 + 3?
Como ya dijimos, todas nuestras interacciones en un ambiente de objetos ocurren enviando
mensajes y las operaciones aritméticas no son la excepción a esta regla.
En el caso de 2 + 3 podemos hacer el mismo análisis:
 el objeto receptor es 2;
 el mensaje es +;
 el argumento es 3.
Y de hecho, ¡también podemos escribirlo como un envío de mensajes convencional!
Probá en la consola los siguientes envíos de mensajes:
5.+ 6
3.< 27
Pepita.== Norita
CONSOLA
5.+ 6
=> 11
  3.< 27
=> true
  pEPITA.== Norita
undefined local variable or method `pEPITA' for main:Object (NameError)
  Pepita. == Norita
=> false

Ejercicio 16: Mensajes por todas partes


Es fácil ver que en Pepita.volar_hacia! Barreal el objeto receptor es Pepita, el
mensaje volar_hacia! y el argumento Barreal; pero ¿dónde queda eso de objeto y
mensaje cuando hacemos, por ejemplo, 2 + 3?
Como ya dijimos, todas nuestras interacciones en un ambiente de objetos ocurren enviando
mensajes y las operaciones aritméticas no son la excepción a esta regla.
En el caso de 2 + 3 podemos hacer el mismo análisis:
 el objeto receptor es 2;
 el mensaje es +;
 el argumento es 3.
Y de hecho, ¡también podemos escribirlo como un envío de mensajes convencional!
Ejercicio 17: Recapitulando
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,
 pueden no tomar argumentos, como Rayuela.anio_de_edicion;
 o pueden tomar uno o más argumentos, separados por coma: SanMartin.cruzar!
LosAndes, Mula.
Operadores. Son todos aquellos cuyo "nombre" se compone de uno o más símbolos, y se
envían simplemente escribiendo dichos símbolos. En cuanto a los argumentos,
 pueden no tomar ninguno, como la negación !true;
 o pueden tomar uno (y solo uno), como Orson == Garfield o energia + 80.
Como vimos, también se pueden escribir como mensajes de palabra clave (aunque no
parece buena idea escribir 1.== 2 en vez de 1 == 2 ).
Vamos a enviar algunos mensajes para terminar de cerrar la idea. Te toca escribir un
programa que haga que Pepita:
1. Coma 90 gramos de alpiste.
2. Vuele a Iruya.
3. Finalmente, coma tanto alpiste como el 10% de la energía que le haya
quedado.
Este programa tiene que andar sin importar con cuanta energía arranque Pepita.
¡Dame una pista!
Cualquier envío de mensajes que devuelva algo es una expresión válida, y puede ser usada
en cualquier lugar en que se espera un objeto. Por ejemplo, las siguientes colaboraciones
son válidas:
Fitito.cargar_nafta!(120 * 4)
Fitito.cargar_nafta!(Fitito.capacidad_tanque_nafta -
Fitito.nafta_disponible) #Carga al Fitito lo necesario para
completar su tanque. Para ello le pregunta al Fitito su
capacidad y la nafta que tiene en este momento.
SOLUCION CORRECTA
Pepita.comer_alpiste! (90)
Pepita.volar_hacia! (Iruya)
Pepita.comer_alpiste! (Pepita.energia * 0.10)
¡Muy bien! Tu solución pasó todas las pruebas
¡Terminaste Objetos y mensajes!
¡Felicitaciones! Diste tus primeros pasos en el mundo de los objetos. Ya sabés:
 Que un objeto es la representación de algún aspecto del problema que queremos
resolver.
 Que los objetos tienen identidad y saben diferenciarse de otros objetos.
 Que interactuamos con los objetos mediante el envío de mensajes.
 Que al conjunto de mensajes que un objeto entiende lo llamamos interfaz.
 Que si le enviamos un mensaje que no entiende, se produce un error.

Definiendo objetos: métodos y estado


Ejercicio 1: Creando a Pepita
Inicialmente en el ambiente solo existen objetos simples como números, strings y
booleanos.
Pero como es imposible que quienes diseñan un lenguaje puedan precargar objetos para
solucionar todos nuestros problemas, también nos dan la posibilidad de crear los
nuestros.
En Ruby, si quisiéramos definir a Norita, escribiríamos el siguiente código:
module Norita
end
Sí, así de simple.
¿Te animás a modificar nuestro código para crear a Pepita?
SOLUCION
module Pepita
end
¡Muy bien! Tu solución pasó todas las pruebas
¡Muy bien, Pepita vive!
Como dedujiste, la definición de un objeto se inicia con la palabra reservada module,
luego el nombre del objeto (con la primera letra en mayúscula) y su fin se indica con un
end.
CONSOLA
Pepita
=> Pepita
Ejercicio 2: Pepita, ¿me entendés?
En la lección anterior Pepita entendía los mensajes comer_lombriz!, cantar!,
volar_en_circulos! y energia.
Con la definición que construimos recién, ¿podrá responderlos?
Intentá enviarle a Pepita los mensajes habituales y fijate qué sucede.
¡Dame una pista!
Para que no tengas que volver a escribirla, pusimos por vos la definición de Pepita en la
Biblioteca.
Siempre que te lo indiquemos, podrás usar los objetos de la Biblioteca como si los
hubieras creado vos.
BIBLIOTECA
module Pepita
end

CONSOLA
=> Pepita
  Pepita.energia
undefined method `energia' for Pepita:Module (NoMethodError)
  Pepita.energia!
undefined method `energia!' for Pepita:Module (NoMethodError)
Ejercicio 3: Los mejores, los únicos, los métodos en
objetos
¿Otra vez undefined method? ¿Y ahora qué falta?
Para que un objeto entienda un mensaje debemos "enseñarle" cómo hacerlo, y para ello
es necesario definir un método dentro de ese objeto:
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.
Dos cosas muy importantes a tener en cuenta :
 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.
Agregale a la definición de Pepita los métodos necesarios para que pueda responder a los
mensajes cantar!, comer_lombriz! y volar_en_circulos!.
¡Dame una pista!
No te olvides de que el ! forma parte del nombre del mensaje y por lo tanto tenés que
escribirlo.
Recordá también que podés usar la consola para ir probando tu solución.

Ejercicio 3: Los mejores, los únicos, los


métodos en objetos
¿Otra vez undefined method? ¿Y ahora qué falta?
Para que un objeto entienda un mensaje debemos "enseñarle" cómo hacerlo, y para ello
es necesario definir un método dentro de ese objeto:
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.
Dos cosas muy importantes a tener en cuenta :
 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.
Agregale a la definición de Pepita los métodos necesarios para que pueda responder a los
mensajes cantar!, comer_lombriz! y volar_en_circulos!.
¡Dame una pista!
No te olvides de que el ! forma parte del nombre del mensaje y por lo tanto tenés que
escribirlo.
Recordá también que podés usar la consola para ir probando tu solución.
SOLUCION CORRECTA
module Pepita
def self.cantar!
end

def self.volar_en_circulos!
end

def self.comer_lombriz!
end
end
¡Muy bien! Tu solución pasó todas las pruebas
Perfecto, ahora Pepita entiende casi todos los mismos mensajes que en la lección anterior.
Pero, ¿hacen lo mismo?
Antes de seguir, enviá algunos de los mensajes en la Consola y fijate qué efecto producen
sobre nuestra golondrina.
Ejercicio 4: Perdiendo energía
Acabamos de aprender una de las reglas fundamentales del envío de mensajes: si a un
objeto no le decímos cómo reaccionar ante un mensaje, y se lo envíamos, no lo entenderá
y nuestro programa se romperá. Y la forma de hacer esto es definiendo un método.
Ahora bien, los métodos que definiste recién no eran muy interesantes: se trataba de
métodos vacíos que evitaban que el programa se rompiera, pero no hacían nada. En
realidad, Pepita tiene energía y los diferentes mensajes que entiende deberían
modificarla.
¿Cómo podríamos decir que cuando Pepita vuela, pierde 10 unidades de energía? ¿Y que
inicialmente esta energía es 100? Así:
module Pepita
@energia = 100

def self.volar_en_circulos!
@energia = @energia - 10
end
end
Una vez más, ya definimos a Pepita por vos. Probá, en orden, las siguientes consultas:
Pepita.volar_en_circulos!
Pepita.volar_en_circulos!
Pepita.energia
Puede que los resultados te sorprendan, en breve hablaremos de esto.
CONSOLA
Pepita.volar_en_circulos!
=> 90
  Pepita.volar_en_circulos!
=> 80
  Pepita.energia
undefined method `energia' for Pepita:Module (NoMethodError)
Ejercicio 5: Atributos
Analicemos el código que acabamos de escribir:
module Pepita
@energia = 100
def self.volar_en_circulos!
@energia = @energia - 10
end
end
Decimos que Pepita conoce o tiene un nivel de energía, que es variable, e inicialmente
toma el valor 100. La energía es un atributo de nuestro objeto, y la forma de asignarle un
valor es escribiendo @energia = 100.
Por otro lado, cuando Pepita recibe el mensaje volar_en_circulos!, su energía disminuye:
se realiza una nueva asignación del atributo y pasa a valer lo que valía antes (o sea,
@energia), menos 10.
Como la operación @energia = @energia - 10 es tan común, se puede escribir @energia -=
10. Como te imaginarás, también se puede hacer con la suma.
Sabiendo esto:
 cambiá la definición del método volar_en_circulos! para que utilice la
expresión simplificada;
 definí la versión correcta del método comer_lombriz!, que provoca que
Pepita gane 20 puntos de energía;
SOLUCION PARA MODIFICAR
module Pepita
@energia = 100

def self.volar_en_circulos!
@energia = @energia - 10
end
reemplazar ………..
end
end
SOLUCION CORRECTA
module Pepita
@energia = 100

def self.volar_en_circulos!
@energia = @energia - 10
end
def self.volar_en_circulos!
@energia -= 10
end
end
module Pepita
def self.comer_lombriz!
@energia += 20
end
end
¡Muy bien! Tu solución pasó todas las pruebas
Acabamos de aprender un nuevo elemento del paradigma de objetos: los atributos (los
cuales escribiremos anteponiendo @), son objetos que nos permiten representar una
característica de otro objeto. Un objeto conoce a todos sus atributos por lo que puede
enviarles mensajes, tal como hicimos con @energia.
Entonces, si le pude enviar mensajes a @energia, ¿eso significa que los números también
son objetos?
¡Claro que sí! ¡Todo-todo-todo es un objeto!
Ejercicio 6: Conociendo el país
Hasta ahora los métodos que vimos solo producían un efecto. Si bien solo pueden
devolver una cosa, ¡pueden producir varios efectos!
Solo tenés que poner uno debajo del otro de la siguiente forma:
def self.comprar_libro!
@plata -= 300
@libros += 1
end
Como te dijimos, Pepita podía volar a diferentes ciudades. Y cuando lo hace, cambia su
ciudad actual, además de perder 100 unidades de energía. Las distintas ciudades vas a
poder verlas en la Biblioteca.
Con esto en mente:
 Creá un atributo ciudad en Pepita: la ciudad donde actualmente está nuestra
golondrina.
 Hacé que la ciudad inicial de pepita sea Iruya.
 Definí un método volar_hacia! en Pepita, que tome como argumento otra ciudad y
haga lo necesario.
¡Dame una pista!
Al parámetro de volar_hacia! tenés que darle un nombre. Podrías llamarlo ciudad, pero eso
colisionaría con el nombre del atributo ciudad. Así que te proponemos otros nombres:
una_ciudad o, mejor, destino;
SOLUCION INICIAL A MODIFICAR
module Pepita
@energia = 100
def self.volar_en_circulos!
@energia = @energia - 10
end
def self.volar_en_circulos!
@energia -= 10
end
end
module Pepita
def self.comer_lombriz!
@energia += 20
end
end
BIBLIOTECA
module Iruya
end
module Obera
end
module GralLasHeras
end
module Calamuchita
end
module Ushuaia
end
SOLUCION CORRECTA
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!(destino)
@energia -= 100
@ciudad = destino
end
end
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 7: Leyendo el estado


Antes te mostramos que si enviamos el mensaje energia, fallará:
Pepita.energia
undefined method `energia' for Pepita:Module (NoMethodError)
El motivo es simple: los atributos NO son mensajes.
Entonces, ¿cómo podríamos consultar la energía de Pepita? Definiendo un método, ¡por
supuesto!
module Pepita
#...atributos y métodos anteriores...

def energia
@energia
End end
Ya agregamos el método energia por vos. Probá en la consola ahora las siguientes consultas :
Pepita.energia
Pepita.energia = 120
energia
¿Todas las consultas funcionan? ¿Por qué?
 Pepita.energia
=> 100
  Pepita.energia = 120
undefined method `energia=' for Pepita:Module (NoMethodError)
Did you mean? energia
  energia
undefined local variable or method `energia' for main:Object (NameError)

Ejercicio 8: Cuestión de estado


Los objetos pueden tener múltiples atributos y al conjunto de estos atributos se lo
denomina estado. Por ejemplo, si miramos a Pepita:
module Pepita
@energia = 100
@ciudad = Obera

#...etc...
end
Lo que podemos observar es que su estado está conformado por ciudad y energia, dado
que son sus atributos.
El estado es siempre privado, es decir, solo el objeto puede utilizar sus atributos, lo que
explica por qué las siguiente consultas que hicimos antes fallaban:
Pepita.energia = 100
energia
Veamos si se entiende: mirá los objetos en la solapa Biblioteca y escribí el estado de cada
uno.
BIBLIOTECA
module Obera
#...más cosas que ahora no interesan...
end
module Pepita
@energia = 100
@ciudad = Obera

#...más cosas que ahora no interesan...


end
module Kiano1100
#...más cosas que ahora no interesan...
end
module RolamotoC115
#...más cosas que ahora no interesan...
end
module Enrique
@celular = Kiano1100
@dinero_en_billetera = 13
@frase_favorita = 'la juventud está perdida'
end
SOLUCION CORRECTA
estado_pepita = %w(
energia
ciudad
)
estado_kiano1100 = %w(
)
estado_rolamotoC115 = %w(
)
estado_enrique = %w(
celular
dinero_en_billetera
frase_favorita )
¡Muy bien! Tu solución pasó todas las pruebas

Ejercicio 9: ¿Dónde estás?


Queremos saber dónde se encuentra Pepita, para lo cual necesitamos agregarle un
mensaje ciudad que nos permita acceder al atributo del mismo nombre.
Inspirándote en la definición de energia, definí el método ciudad que retorne la ubicación
de nuestra golondrina.
SOLUCION
module Pepita
@energia = 100
@ciudad = Obera
def self.energia
@energia
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 -= 100
@ciudad = destino
end
end
SOLUCION CORRECTA
module Pepita
@energia = 100
@ciudad = Obera

def self.ciudad
@ciudad
end

def self.energia
@energia
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 -= 100
@ciudad = destino
end
end
¡Muy bien! Tu solución pasó todas las pruebas
A estos métodos que sirven para conocer el valor de un atributo los llamamos métodos de
acceso o simplemente accessors, por su nombre en inglés.

Ejercicio 10: Volando alto


Volar hacia un cierto punto no es tarea tán fácil: en realidad, Pepita pierde tanta energía
como la mitad de kilómetros que tenga que recorrer.
Aunque en el mapa real no sea así, imaginaremos que las ciudades están ubicadas en línea
recta, para facilitar los cálculos:

Por ejemplo, si Pepita está en Obera y quiere volar a Iruya debe recorrer 670 kilómetros,
por lo que perderá 335 unidades de energía.
¿Y si Pepita está en Iruya y quiere volar a Obera? ¡También! La distancia entre dos
ciudades siempre es un valor positivo . Para resolver esto contamos con el mensaje abs
que entienden los números y nos retorna su valor absoluto:
17.abs
=> 17
(-17).abs
=> 17
(1710 - 1040).abs
=> 670
(1040 - 1710).abs
=> 670
(1040 - 1710).abs / 2
=> 335
Sabiendo esto:
 Definí el objeto que representa a BuenosAires.
 Definí en Obera, Iruya y BuenosAires un método kilometro que retorne la
altura a la que se encuentran, según el esquema. ¡Cuidado! No tenés que
guardar el valor en un atributo @kilometro sino simplemente retornar el
número que corresponde.
 Modificá el método volar_hacia! de Pepita para hacer el cálculo de la
distancia y alterar su energía. Para acceder al kilometro inicial de Pepita
tenes que hacer @ciudad.kilometro.
Para que el ejemplo tenga sentido, vamos a hacer que Pepita arranque con la energía en
1000. ¡Dame una pista!
Tené en cuenta que el ejemplo que vimos nos muestra el cálculo de la distancia entre
Obera e Iruya. Sin embargo, Pepita tiene que poder volar desde su ciudad hasta cualquier
destino.
module Obera
def self.kilometro
# completar acá...
end
end

module Iruya
def self.kilometro
# completar acá...
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 -= # completar acá...
@ciudad = destino

end
end
SOLUCION CORRECTA
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 -= (@ciudad.kilometro-destino.kilometro).abs/2
@ciudad = destino
end end
¡Muy bien! Tu solución pasó todas las pruebas
¡Buen trabajo!
Cuando programamos en este paradigma solemos tener a disposición un montón de objetos
que interactúan entre sí, y por lo tanto aprender cuándo usarlos y definirlos es una habilidad
fundamental, que irás adquiriendo con la práctica.
CONSOLA

1040.abs
=> 1040
  (1710 - 1040).abs
=> 670
  (1040 - 1710).abs
=> 670
  (1040 - 1710).abs/2
=> 335

Ejercicio 11: Delegar es bueno


En el ejercicio anterior vimos que un objeto (en ese caso, Pepita) le puede enviar mensajes
a otro que conozca (en ese caso, ciudades como Obera o BuenosAires):

module Pepita
# ...etc...

def self.volar_hacia!(destino)
@energia -= (@ciudad.kilometro - destino.kilometro).abs / 2
@ciudad = destino
end
end
Esto se conoce como delegar una responsabilidad, o simplemente, delegar: la
responsabilidad de saber en qué kilómetro se encuentra es de la ciudad, y no de Pepita.
A veces nos va a pasar que un objeto tiene un método muy complejo, y nos gustaría
subdividirlo en problemas más chicos que el mismo objeto puede resolver. Pero, ¿cómo
se envía un objeto mensajes a sí mismo?
Un objeto puede enviarse un mensaje a sí mismo fácilmente usando self como receptor
del mensaje.
module Pepita
# ...etc...
def self.volar_hacia!(destino)
self.gastar_energia! destino #¡Ojo! No hicimos Pepita.gastar_energia!(destino)
@ciudad = destino
end

def self.gastar_energia!(destino)
@energia -= (@ciudad.kilometro - destino.kilometro).abs / 2
end
end
Pero esto se puede mejorar un poco más. Delegá el cálculo de la distancia en un método
distancia_a, que tome un destino y devuelva la distancia desde la ciudad actual hasta el
destino.
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.kilometro - destino.kilometro).abs / 2
end
end
BIBLIOTECA
module Obera
def self.kilometro
1040
end
end
module Iruya
def self.kilometro
1710
end
end
module BuenosAires
def self.kilometro
0
end
end
SOLUCION CORRECTA
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(destino)

(@ciudad.kilometro - destino.kilometro).abs
end
end

¡Muy bien! Tu solución pasó todas las pruebas


La delegación es la forma que tenemos en objetos de dividir en subtareas: separar un
problema grande en problemas más chicos para que nos resulte más sencillo resolverlo.
A diferencia de lenguajes sin objetos, aquí debemos pensar dos cosas:
1. cómo dividir la subtarea, lo cual nos llevará a delegar ese comportamiento en
varios métodos;
2. qué objeto tendrá la responsabilidad de resolver esa tarea.

Ejercicio 12: ¿Es mi responsabilidad?


Hay un pequeño problema conceptual con la solución anterior: ¿por qué Pepita, una
golondrina, es responsable de calcular la distancia entre dos ciudades?
Dicho de otra manera, ¿es necesario contar con una golondrina para poder calcular la
distancia entre dos lugares? ¿Cual es el objeto más pequeño que podría saber hacer esto?
¿Lo pensaste? La respuesta es simple: ¡la misma ciudad! Por ejemplo, BuenosAires podría
entender un mensaje distancia_a, que tome otra ciudad y devuelva la distancia entre ésta y
sí misma.
Modificá la solución del ejercicio anterior para que sean las ciudades las que calculan las
distancias. Pensá que no solo Obera debe tener este método, sino también BuenosAires e
Iruya, para cuando tenga que volver.
¡Dame una pista!
Con las herramientas que vimos hasta ahora, no queda más opción que repetir el mismo
código en las tres ciudades.
¡Muy pronto lo solucionaremos!
SOLUCION CORRECTA
module Obera
def self.kilometro
1040
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs
end
end
module Iruya
def self.kilometro
1710
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs
end
end
module BuenosAires
def self.kilometro
0
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs

end
end
module Pepita
@energia = 1000
@ciudad = Obera
@distancia=0

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.ciudad.distancia_a(destino) / 2
end
end
¡Muy bien! Tu solución pasó todas las pruebas
¡Terminaste Definiendo objetos: métodos y estado!
Con lo que aprendiste en esta lección, ya podés crear un programa completo, creando los
objetos que te sean necesarios.
Vimos además dos de los conceptos más poderosos del paradigma: la delegación y la
distribución de responsabilidades. Estas herramientas son el principio básico para la
división en subtareas y seguirán acompañándonos en todas las lecciones.

Polimorfismo y encapsulamiento
Ya que te vas familiarizando con este paradigma, aprovecharemos para hacer algunas
reflexiones sobre cómo los objetos interactúan entre sí.
A partir de los conceptos que ya conocés, veremos algunas herramientas conceptuales que
nos ayudarán a contestar algunas preguntas:
 ¿Puede un objeto interactuar con otros sin saber quiénes son?
 Si quiero poder hacer algo con dos objetos distintos ¿necesito que entiendan exactamente
los mismos mensajes?
 ¿Se puede modificar el estado de un objeto "desde afuera"?
Esto, y algunas cosas más sobre aves, entrenamientos y gauchos, en esta lección.
¡Empecemos!

Ejercicio 1: ¿Pepita está feliz?


¿Te acordás de Pepita? Bueno, aunque no lo creas, también cambia de estados de ánimo.
En nuestro modelo de Pepita, vamos a representar simplemente dos estados posibles:
cuando está débil y cuando está feliz.
¿Y cuándo ocurre eso?
 Pepita está débil si su energía es menor que 100.
 Pepita está feliz si su energía es mayor que 1000.
Completá los métodos debil? y feliz? de Pepita.
Como en esta lección no vamos a interactuar con las ciudades, hemos quitado todo lo
relacionado a ellas de Pepita. Esto solo lo hacemos para que te sea más fácil escribir el
código, no lo intentes en casa.
¡Dame una pista!
Recordá que existen los operadores de comparación < y >, que sirven para verificar si una
expresión númerica es menor o mayor a otra, respectivamente.
Por ejemplo:
130 < 20 * 10
=> true
Semana.cantidad_de_dias > 5
=> true
3 > PerroBoby.cantidad_de_patas
=> false
SOLUCION CORRECTA
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
end
¡Muy bien! Tu solución pasó todas las pruebas
En Ruby, es una convención que los mensajes que devuelven booleanos (o sea, verdadero
o falso) terminen con un ?.
Intentá respetarla cuando crees tus propios mensajes, acordate que uno de los objetivos
del código es comunicar nuestras ideas a otras personas... y las convenciones, muchas
veces, nos ayudan con esto.
CONSOLA
100 > 500
=> false
  100 < 1000
=> true

Ejercicio 2: Reencuentro alternativo


Si llegaste hasta acá, ya deberías saber que en programación existe una herramienta
llamada alternativa condicional.
En Ruby, como en muchos otros lenguajes, esto se escribe con la palabra reservada if. Por
ejemplo:
module Jose
def self.acomodar_habitacion!
self.ordenar!
if self.tiene_sabanas_sucias?
self.cambiar_sabanas!
end
self.tender_la_cama!
end
end
Sabiendo cómo se escribe la alternativa condicional en Ruby queremos que Pepita,
además de recibir órdenes, tenga sus momentos para poder hacer lo que quiera.
Obviamente, qué quiere hacer en un momento dado depende de su estado de ánimo:
 Si está débil, come diez gramos de alpiste, para recuperarse.
 Si no lo está, no hace nada.
Hacé que Pepita entienda el mensaje hacer_lo_que_quiera! que se comporte como
explicamos.
EJERCICIO DADO A MODIFICAR
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
end
ESTO ES LO QUE SE LE DEBIA AGREGAR
def self.hacer_lo_que_quiera!
if self.debil?
self.comer_alpiste!10
end
end
end

SOLUCION CORRECTA
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
end
end
end
¡Muy bien! Tu solución pasó todas las pruebas
Como acabamos de ver, la alternativa condicional es como en otros lenguajes. La diferencia
radica en su sintaxis, es decir, cómo la escribimos.

Ejercicio 3: Repitamos qué pasa si no


Hay veces que con un if alcanza, pero otras queremos hacer algo si no se cumple una
condición. Como ya te podrás imaginar, donde hay un if ¡cerca anda un else!
module Jardinero
def self.cuidar!(planta)
if planta.necesita_agua?
3.times { self.regar! planta }
else
self.sacar_bichos! planta
end
end
end
¿Y ese times qué es?
Es un mensaje que entienden los números que sirve para ejecutar una porción de código
varias veces. En este caso regaríamos 3 veces la planta recibida como argumento.
Ahora que conocimos la existencia de times y vimos cómo hacer else...
Modificá la solución para que si Pepita no está débil vuele en círculos 3 veces.
SOLUCION DADA EN LA PAGINA
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
end
end
end
SOLUCION CORRECTA
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
¡Muy bien! Tu solución pasó todas las pruebas
Ejercicio 4: Voy a hacer, pero como yo quiero
Algunas veces vamos a tener condiciones anidadas. En otras palabras, un if dentro de un if
o un else. Como en este ejemplo:
module Docente
def self.nota_conceptual(nota)
if nota > 8
"Sobresaliente"
else
if nota > 6
"Satisfactoria"
else
"No satisfactoria"
end
end
end
end
Ahora que vimos estas condiciones anidadas que poco tienen que ver con el nido de
Pepita , vamos a conocer el comportamiento definitivo de Pepita cuando hace lo que
quiere:
 Si está débil, come diez gramos de alpiste, para recuperarse.
 Si no está debil pero sí feliz, vuela en círculos cinco veces.
 Si no está feliz ni débil, vuela en círculos 3 veces.
Modificá a Pepita para que el método hacer_lo_que_quiera! se comporte como
mencionamos más arriba.
SOLUCION CORRECTA
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
¡Muy bien! Tu solución pasó todas las pruebas
En Ruby, podemos simplicar la manera de escribir un if dentro un else con elsif. Por
ejemplo este código:
def self.nota_conceptual(nota)
if nota > 8
"Sobresaliente"
else
if nota > 6
"Satisfactoria"
else
"No satisfactoria"
end
end
end
Lo podemos escribir:
def self.nota_conceptual(nota)
if nota > 8
"Sobresaliente"
elsif nota > 6
"Satisfactoria"
else
"No satisfactoria"
end
end
Antes de seguir, ¿te animás a editar tu solución para que use elsif?
SOLUCION CORRECTA MIA
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
elsif
self.feliz?
5.times { self.volar_en_circulos!}
else
3.times { self.volar_en_circulos!}

end
end
end cuando lo hice con cuatro end me dio error de sintaxis porque van de acuerdo a los
if por eso solo lleva 3 end

Ejercicio 5: Llegó Pepo


Pepo es un gorrión que también sabe comer, volar y hacer lo que quiera, pero lo hace de
manera diferente a Pepita.
 comer alpiste: el aparato digestivo de Pepo no anda muy bien, por eso solo puede
aprovechar la mitad del alpiste que come. Por ejemplo, si come 20 gramos de
alpiste, su energía solo aumenta en 10.
 volar en círculos: gasta 15 unidades de energía si está pesado y 5 si no lo está.
Decimos que está pesado si su energía es mayor a 1100.
 hacer lo que quiera: como siempre tiene hambre, aprovecha y come 120 gramos
de alpiste.
Ah, y al igual que Pepita, su energía comienza en 1000.
Definí a Pepo según las reglas anteriores. Te dejamos el código de Pepita para usar como
base, modificá y borrá las partes que no correspondan.
¡Dame una pista!
En la Biblioteca te dejamos a Pepita por si no recordás cómo era la sintaxis de la
alternativa condicional.
BIBLIOTECA
module Obera
def self.kilometro
1040
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs
end
end
module Iruya
def self.kilometro
1710
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs
end
end
module BuenosAires
def self.kilometro
0
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs
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
elsif
self.feliz?
5.times { self.volar_en_circulos!}
else
3.times { self.volar_en_circulos!}
end
end
end
SOLUCION CORRECTA
module Pepo
@energia = 1000

def self.energia
@energia
end

def self.volar_en_circulos!
if @energia > 1100
@energia -= 15
else
@energia -= 5
end
end

def self.comer_alpiste!(gramos)
@energia += gramos / 2
end

def self.hacer_lo_que_quiera!
comer_alpiste!(120)
end
end
¡Muy bien! Tu solución pasó todas las pruebas
Genial, ya tenemos dos aves con las cuales trabajar y que además comparten una
interfaz: ambas entienden los mensajes comer_alpiste!(gramos), volar_en_circulos! y
hacer_lo_que_quiera!.
Veamos qué podemos hacer con ellas...

Ejercicio 6: ¡A entrenar!
Nuestras aves quieren presentarse a las próximas Olimpíadas, y para eso necesitan
ejercitar un poco.
Para ayudarnos en esta tarea conseguimos a Pachorra, un ex entrenador de fútbol que
ahora se dedica a trabajar con aves. Él diseñó una rutina especial que consiste en lo
siguiente:
 Volar en círculos 10 veces.
 Comer un puñado de 30 gramos de alpiste.
 Volar en círculos 5 veces.
 Como premio, que el ave haga lo que quiera.
Creá a Pachorra, el entrenador de aves, y hacé que cuando reciba el mensaje
entrenar_ave! haga que Pepita realice su rutina (si, solo puede entrar a Pepita , pero lo
solucionaremos pronto).
Para que no moleste, movimos el código de Pepita a la Biblioteca.
¡Dame una pista!
Recordá que la que entrena es Pepita, su entrenador solo le dice lo que tiene que hacer.
¡Ah! Y para que un objeto pueda mandarle mensajes a otro debe conocerlo. Por ejemplo,
llamándolo por su nombre como ya hemos hecho:
Pepita.energia
SOLUCION CORRECTA
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
¡Muy bien! Tu solución pasó todas las pruebas
Aunque lo que hiciste funciona, es bastante rígido: para que Pachorra pueda entrenar a
otro pájaro hay que modificar el método entrenar_ave! y cambiar el objeto al que le envía
los mensajes.
¡Mejoremos eso entonces!

Ejercicio 7: Pachorra todoterreno


Como imaginabas, Pachorra puede entrenar cualquier tipo de aves, aunque para que no
haya problemas, solo entrena de a una a la vez.
Antes de empezar a entrenar, debe firmar un contrato con el ave. Esto, por ejemplo, lo
haríamos de la siguiente manera:
Pachorra.firmar_contrato! Pepita # ahora el ave de Pachorra es Pepita
Cada vez que firmamos un contrato cambiamos el ave que entrenará Pachorra, por lo
cual es necesario recordar cuál es ya que a ella le enviaremos mensajes:
Pachorra.entrenar_ave! # acá entrena a Pepita
Pachorra.firmar_contrato! Pepo # ahora el ave de Pachorra es Pepo
Pachorra.entrenar_ave! # ahora entrena a Pepo
Agregale a Pachorra el método firmar_contrato!(ave), de forma tal que cuando le
enviemos el mensaje entrenar_ave! haga entrenar al último ave con el que haya firmado
contrato. ¡Dame una pista!
El método firmar_contrato! solo cambia el ave de Pachorra, no hay que hacer nada más en ese
método.
SOLUCION DADA POR MUMUKI A MODIFICAR
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
SOLUCION CORRECTA
module Pachorra
def self.firmar_contrato! (ave)
@ave = ave
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
¡Muy bien! Tu solución pasó todas las pruebas
Una forma posible de cambiar el objeto al que le enviamos mensajes es modificando el
valor de un atributo, como estamos haciendo en este ejemplo.

Ejercicio 8: Una golondrina diferente


¿Te acordás de Norita, la amiga de Pepita? Resulta que ella también quiere empezar a
entrenar, y su código es el siguiente:
module Norita
@energia = 500

def self.volar_en_circulos!
@energia -= 30
end
def self.comer_alpiste!(gramos)
@energia -= gramos
end
end
Pero, ¿podrá entrenar con Pachorra?
Probalo en la consola, enviando los siguientes mensajes:
Pachorra.firmar_contrato! Norita
Pachorra.entrenar_ave!
CONSOLA
Pachorra.firmar_contrato! Norita
=> Norita
  Pachorra.entrenar_ave!
undefined method `hacer_lo_que_quiera!' for Norita:Module (NoMethodError)

Ejercicio 9: Un entrenamiento más duro


Analicemos el error:
Pachorra.entrenar_ave!
undefined method `hacer_lo_que_quiera!' for Norita:Module (NoMethodError)
En criollo, lo que dice ahí es que Norita no entiende el mensaje hacer_lo_que_quiera!, y
por eso Pachorra no la puede entrenar; este mensaje forma parte de su rutina.
Miremos ahora el método entrenar_ave! de Emilce, una entrenadora un poco más estricta:
module Emilce
def self.entrenar_ave!
53.times { @ave.volar_en_circulos! }
@ave.comer_alpiste! 8
end
end
¿Podrá Norita entrenar con Emilce? ¿Y Pepita? ¿Y Pepo?
Probalo en la consola y completá el código con true (verdadero) o false (falso) según
corresponda para cada ave. ¡Dame una pista!
Para que Emilce pueda entrenar a un ave debe firmar un contrato tal como hace Pachorra.
norita_puede_entrenar_con_pachorra = # true / false
norita_puede_entrenar_con_emilce = # true / false
pepita_puede_entrenar_con_pachorra = # true / false
pepita_puede_entrenar_con_emilce = # true / false
pepo_puede_entrenar_con_pachorra = # true / false
pepo_puede_entrenar_con_emilce = # true / false
CONSOLA CORRECTA DEL EJERCICIO
Pachorra.firmar_contrato! (Pepita)
=> Pepita
  Pachorra.entrenar_ave!
=> 5
  Pepita_puede_entrenar_con_Pachorra = true
=> true
  Emilce.firmar_contrato! (Pepita)
=> Pepita
  Emilce.entrenar_ave!
=> 840
  Pepita_puede_entrenar_con_Emilce = true
=> true
  Pachorra.firmar_contrato! (Pepo)
=> Pepo
  Pachorra.entrenar_ave!
=> 1000
  Pepo_puede_entrenar_con_Pachorra = true
=> true
  Emilce.firmar_contrato! (Pepo)
=> Pepo
  Emilce.entrenar_ave!
=> 739
  Pepo_puede_entrenar_con_Emilce = true
=> true
SOLUCION CORRECTA
norita_puede_entrenar_con_pachorra = false
norita_puede_entrenar_con_emilce = true

pepita_puede_entrenar_con_pachorra = true
pepita_puede_entrenar_con_emilce = true

pepo_puede_entrenar_con_pachorra = true
pepo_puede_entrenar_con_emilce = true
¡Muy bien! Tu solución pasó todas las pruebas
Según las rutinas que definen, cada entrenador/a solo puede trabajar con ciertas aves:

 Pachorra puede entrenar a cualquier ave que entienda volar_en_circulos!,


comer_alpiste!(gramos) y hacer_lo_que_quiera!.
 Emilce puede entrenar a cualquier ave que entienda volar_en_circulos! y
comer_alpiste!(gramos).
Dicho de otra manera, la rutina nos define cuál debe ser la interfaz que debe respetar un
objeto para poder ser utilizado.

Ejercicio 10: ¿¿Polimor-qué??


¿Qué pasa si dos objetos, como Pepita, Norita o Pepo son capaces de responder a un
mismo mensaje? Podemos intercambiar un objeto por otro sin notar la diferencia, como
experimentaste recién.
Este concepto es fundamental en objetos, y lo conocemos como polimorfismo. Decimos
entonces que dos objetos son polimórficos cuando pueden responder a un mismo conjunto
de mensajes y hay un tercer objeto que los usa indistintamente.
¡Comprobemos si entendiste! Elegí las opciones correctas:
Pepita, Norita y Pepo son polimórficas para Emilce.
Pepita, Norita y Pepo son polimórficas para Pachorra.
Pepita, Norita y Pepo no son polimórficas para Pachorra.
Pepita y Pepo son polimórficas para Pachorra.

¡La respuesta es correcta!


Para que quede clarísimo, una definición para leer todas las mañanas antes de desayunar:
Dos objetos son polimórficos para un tercer objeto cuando este puede enviarles los
mismos mensajes, sin importar cómo respondan o qué otros mensajes entiendan.

Ejercicio 11: Forzando el polimorfismo


Bueno, ya entendimos que para el caso de Pachorra, Norita no es polimórfica con las otras
aves, pero... ¿podremos hacer algo al respecto?
¡Claro que sí! Podemos agregarle los mensajes que le faltan, en este caso
hacer_lo_que_quiera!.
¿Y qué hace Norita cuando le decimos que haga lo que quiera? Nada.
Modificá a Norita para que pueda entrenar con Pachorra.
SOLUCION CORRECTA
module Norita
@energia = 500
def self.hacer_lo_que_quiera!
end
def self.energia
@energia
end

def self.volar_en_circulos!
@energia -= 30
end
def self.comer_alpiste!(gramos)
@energia -= gramos
end
end
¡Muy bien! Tu solución pasó todas las pruebas
Aunque parezca que no tiene mucho sentido, es común que trabajando con objetos
necesitemos forzar el polimorfismo y hagamos cosas como estas.
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.

Ejercicio 12: Empieza el set


En los ejercicios anteriores, le habíamos incluido a Pachorra y Emilce un mensaje
firmar_contrato!(ave) que modificaba su estado, es decir, alguno de sus atributos. A estos
mensajes que solo modifican un atributo los conocemos con el nombre de setters, porque
vienen del inglés set que significa establecer, ajustar, fijar.
Para estos casos, solemos utilizar una convención que se asemeja a la forma que se
modifican los atributos desde el propio objeto, pudiendo ejecutar el siguiente código
desde una consola:
Emilce.ave = Pepita
Esto se logra definiendo el método ave=, todo junto, como se ve a continuación:
module Emilce
def self.ave=(ave_nueva)
@ave = ave_nueva
end

def self.entrenar_ave!
53.times { @ave.volar_en_circulos! }
@ave.comer_alpiste!(8)
end
end
¿Te animás a cambiar el código de Pachorra para que siga esta convención?
module Pachorra
def self.firmar_contrato!(un_ave)
@ave = un_ave
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
SOLUCION CORRECTA
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
¡Muy bien! Tu solución pasó todas las pruebas
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.
La convención en Ruby para estos métodos es:

Ejercicio 13: El encapsulamiento


Ya aprendiste cómo crear getters y setters para un atributo, pero ¿siempre vamos a
querer ambos?
La respuesta es que no, y a medida que desarrolles más programas y dominios diferentes
tendrás que construir tu propio criterio para decidir cuándo sí y cuándo no.
Por ejemplo, ¿qué pasaría si a Pepita le agregaramos un setter para la ciudad? Podríamos
cambiarla en cualquier momento de nuestro programa ¡y no perdería energía! Eso va
claramente en contra de las reglas de nuestro dominio, y no queremos que nuestro
programa lo permita.
Te dejamos en la Biblioteca el código que modela a Manuelita, una tortuga viajera.
Algunos de sus atributos pueden ser leidos, otros modificados y otros ambas cosas.
Completá las listas de atributos_con_getter y atributos_con_setter mirando en la definicion de
Manuelita qué tiene programado como setter y que como getter.
¡Dame una pista!
Recordá la convención para nombrar los métodos de acceso que mencionamos antes:
 Para los getters, que sirven para obtener el valor de un atributo, usamos el mismo
nombre que este.
 Para los setters, que sirven para fijar el valor de un atributo, usamos el mismo
nombre que este pero con un = al final.
SOLUCION A COMPLETAR DADA POR MUMUKI
atributos = %w(
energia
ciudad
mineral_preferido
donde_va )

atributos_con_getter = %w(
# completá acá... )

atributos_con_setter = %w(
# completá acá... )
BIBLIOTECA
module Obera
def self.kilometro
1040
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs
end
end
module Iruya
def self.kilometro
1710
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs
end
end
module BuenosAires
def self.kilometro
0
end
def self.distancia_a(destino)
(destino.kilometro - self.kilometro).abs
end
end
module Pehuajo
end
module Malaquita
end
module Paris
end
module Manuelita
@energia = 100
@ciudad = Pehuajo
@mineral_preferido = Malaquita
@donde_va = Paris
def self.energia
@energia
end
def self.ciudad
@ciudad
end
def self.mineral_preferido=(mineral)
@mineral_preferido = mineral
end
def self.mineral_preferido
@mineral_preferido
end
def self.donde_va=(ciudad)
@donde_va = ciudad
end
end
El nombre que le ponemos es una simple convención, digamos que nos pusimos de
acuerdo para que sea así y por eso lo hacemos de esa forma. Esto nos sirve para
diferencias más fácil cuál es el setter y cuál el getter de un atributo, ya que sabemos que
tanto los setter como getter llevan el mismo nombre que el atributo. Si tenemos:
def self.mineral_preferido(mineral) #setter
@mineral_preferido = mineral
end

def self.minera_preferido #getter


@mineral_preferido
end
Por más que no sean el mismo método, como tienen el mismo nombre quizás puede ser
algo confuso. Si nosotros agregamos el =, nos da el pie para ver que dentro del método
hacemos una asignación, lo que significa que estamos trabajando con el setter y no con el
getter.
A nivel lógico, no tienen diferencia, ambos deberían funcionar por igual, es más que nada
una forma de organizarnos dentro del lenguaje para facilitarnos las cosas.
SOLUCION CORRECTA
atributos = %w(
energia
ciudad
mineral_preferido
donde_va
)

atributos_con_getter = %w(
energia
ciudad
mineral_preferido
)

atributos_con_setter = %w(
mineral_preferido
donde_va

)
¡Muy bien! Tu solución pasó todas las pruebas
Si hacemos bien las cosas, quien use nuestros objetos sólo verá lo que necesite para poder
interactuar con ellos. A esta idea la conocemos como encapsulamiento, y es esencial para
la separación de responsabilidades de la que veníamos hablando.
Será tarea tuya (y de tu equipo de trabajo, claro) decidir qué atributos exponer en cada
objeto.
CONSOLA
@energia
=> nil
  @ciudad
=> nil
  @mineral_preferido = preferido
undefined local variable or method `preferido' for main:Object (NameError)
  @mineral_preferido = Malaquita
=> Malaquita
  @mineral_preferido
=> Malaquita
  @adonde_va = ciudad
undefined local variable or method `ciudad' for main:Object (NameError)
  @donde_va = ciudad
undefined local variable or method `ciudad' for main:Object (NameError)

Ejercicio 14: Vamos terminando


Vamos a empezar a repasar todo lo que aprendiste en esta lección, te vamos a pedir que
modeles a nuestro amigo Inodoro, un gaucho solitario de la pampa argentina. Fiel al
estereotipo, Inodoro se la pasa tomando mate, y siempre lo hace con algún compinche; ya
sea Eulogia, su compañera o Mendieta, su perro parlante.
Tu tarea será completar el código que te ofrecemos, definiendo los métodos incompletos
y agregando los getters y setters necesarios para que sea posible:
 Consultar cuánta cafeína en sangre tiene Inodoro.
 Consultar al compinche de Inodoro.
 Modificar al compinche de Inodoro.
 Consultar si Eulogia está enojada.
 Consultar cuántas ganas de hablar tiene Mendieta.
 Modificar las ganas de hablar de Mendieta.
¡Dame una pista!
Pará, pará, pará, ¿y cómo sé qué nombre le tengo que poner a los métodos?
Como ya te explicamos, desde ahora nos vamos a manejar siempre con la misma
convención para nombrar getters y setters. Podés buscarla en los ejercicios anteriores si
aún no la recordás.
module Inodoro
@cafeina_en_sangre = 90
# completá acá...
end
module Eulogia
@enojada = false
# completá acá...
end

module Mendieta
@ganas_de_hablar = 5
# completá acá...
end
Además de eso, en Inodoro queremos saber quién es el compinche, por lo que
necesitamos un getter, recordemos que a estos les colocamos el mismo nombre que el
atributo que queremos consultar, pero en la solución el nombre es: tomar_mate!.
Por la parte de Eulogia también queremos consultar si está enojada, por lo que
necesitamos un getter en lugar de un setter.
Por último Mendieta necesita tanto un getter como un setter. El getter en este caso se
llama: hablar y el atributo es ganas_de_hablar, ocurre lo mismo que con tomar_mate!.
En cuando al setter, queremos que podamos por parámetro cambiar las ganas de hablar,
de la misma forma que por ejemplo podemos cambiar al compinche de Inodoro.
 El getter es el método que nos sirve para consultar un atributo. Por ejemplo si
tenemos un atributo:
module Persona
@edad
end
Si queremos saber el valor de ese atributo, es decir queremos saber la edad de la
persona, creamos el método getter con el mismo nombre:
def self.edad
@edad
end
Si fuese un booleano, por ejemplo la persona es mayor de edad:
module Persona
@mayor_de_edad
end
También podemos crear un getter pero como es un booleano al nombre le agregamos
un ?. Esto puede servir para recordar, que los booleanos son de alguna forma preguntas,
por ejemplo: ¿es mayor de edad?, y lo contesto con sí o no. El getter de este atributo
sería:
def self.mayor_de_edad?
@mayor_de_edad
end
El setter por otra parte es un método mediante el cual cambiamos el valor del atributo, le
asignamos un nuevo valor. Por ejemplo supongamos que una persona tiene un atributo
para saber el color de la remera que está usando, pero cada tanto le gusta cambiarsela:
module Persona
@color_remera
end
Queremos tener un método setter que nos permita cambiar el valor de color_remera
mediante un parámetro (que sería el nuevo color). Creamos también los setters con el
mismo nombre, pero les agregamos un = para diferenciarlos, ya que por dentro estamos
usando el = para asignar el nuevo valor:
def self.color_remera=(nuevo_color)
@color_remera = nuevo_color
end
Espero que sirva, cualquier cosa no dudes en volver a consultar 😄
Julian Gonzalo A. hace 11 meses
Ya estoy mas cerca... Solo estoy teniendo problemas para definir compinche. Puse esto:
@compinche def self.compinche @compinche = Eulogia @compinche = Mendieta end
def self.compinche=(nuevo_compinche) @compinche = nuevo_compinche end
SOLUCION CORRECTA
module Inodoro
@cafeina_en_sangre = 90
def self.cafeina_en_sangre
@cafeina_en_sangre

end

def self.compinche
@compinche
end
def self.compinche= (compinche_nuevo)
@compinche = (compinche_nuevo)
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= (valor)
@ganas_de_hablar = (valor)
end
end
¡Muy bien! Tu solución pasó todas las pruebas
¡Excelente! Parece que los getters y setters quedaron claros.
Para finalizar esta lección vamos a repasar lo que aprendimos de polimorfismo.

Ejercicio 15: ¡Se va la que falta!


Para finalizar el repaso vamos a modelar el comportamiento necesario para que Inodoro
pueda tomar mate con cualquiera de sus compinches... ¡Polimórficamente!
 Cuando Inodoro toma mate aumenta en 10 su cafeína en sangre y su compinche
recibe un mate.
 Al recibir un mate, Eulogia se enoja porque Inodoro siempre le da mates fríos.
 Por su parte, Mendieta se descompone cuando recibe un mate, porque bueno... es
un perro. Esto provoca que no tenga nada de ganas de hablar (o en otras palabras,
que sus ganas_de_hablar se vuelvan 0).
Definí los métodos tomar_mate!, en Inodoro, y recibir_mate! en Eulogia y Mendieta.
¡Dame una pista! Cuando Inodoro toma mate, su compinche recibe un mate:
Inodoro.compinche= Mendieta
Mendieta.ganas_de_hablar
=> 5
Inodoro.tomar_mate!
Mendieta.ganas_de_hablar
=> 0
Esto no funciona al revés, cuando sus compinches reciben un mate no lo hacen tomar a
Inodoro:
Inodoro.compinche= Eulogia
Inodoro.cafeina_en_sangre
=> 90
Eulogia.recibir_mate!
Inodoro.cafeina_en_sangre
=> 90
SOLUCION CORRECTA
module Inodoro
@cafeina_en_sangre = 90
@compinche
def self.cafeina_en_sangre
@cafeina_en_sangre

end
def self.tomar_mate!
@cafeina_en_sangre= +10
@compinche.recibir_mate!

end
def self.compinche= compinche_nuevo
@compinche = compinche_nuevo
end
end

module Eulogia
def self.enojada?
@enojada

end
def self.recibir_mate!
@enojada=true
end
end
module Mendieta
@ganas_de_hablar = 5
def self.ganas_de_hablar
@ganas_de_hablar

end
def self.ganas_de_hablar=(gan)
@ganas_de_hablar=gan

end
def self.recibir_mate!
@ganas_de_hablar= 0
end
end
¡Muy bien! Tu solución pasó todas las pruebas
¡Terminaste Polimorfismo y encapsulamiento!
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.
Relacionado a esto, hablamos del encapsulamiento que nos permite el paradigma,
haciendo que cada objeto solo exponga lo necesario para interactuar con él y se reserve
para su ámbito privado lo que no sea necesario compartir.
En el caso de los atributos, esta exposición se logra implementando un getter (método que
nos permite ver su valor) o un setter (método que nos permite modificar su valor). Y que
nuestro código sea entendido fácilmente por otras personas, elegimos utilizar una
convención para darle nombre a estos métodos.

You might also like