Professional Documents
Culture Documents
Los Ejercicios Resueltos
Los Ejercicios Resueltos
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 {
}
program {
Mover(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) }
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.
program { Poner(Rojo) }
¡Felicitaciones! Acabás de escribir un programa que pone una bolita roja en la celda actual.
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) }
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ó.
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)}
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:
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é.
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.
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) }
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?
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!
procedure PonerVerdeYAzul() {
Poner(Verde)
Poner(Azul) }
program {
PonerVerdeYAzul() }
procedure Mover4AlNorte() {
Mover(Norte)
Mover(Norte)
Mover(Norte)
Mover(Norte) }
program {
Mover4AlNorte( ) }
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.
procedure Poner3Rojas() {
Poner(Rojo)
Poner(Rojo)
Poner(Rojo) }
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.
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!
procedure DibujarLineaColorida() {
PonerCeldaColorida()
Mover(Este)
PonerCeldaColorida()
Mover(Este)
PonerCeldaColorida()
Mover(Este)
PonerCeldaColorida()
IrAlBorde(Oeste) }
procedure PonerCeldaColorida() {
Poner(Verde)
Poner(Rojo)
Poner(Negro)
Poner(Azul) }
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) } }
procedure MoverOeste10() {
repeat(10) {
Mover(Oeste) } }
Solución
procedure MoverOeste5() {
repeat(5) {
Mover(Oeste) } }
program {
Mover(Sur)
repeat(4) {
Poner(Rojo) }
Mover(Norte) }
procedure Poner3AlNoreste() {
Mover(Norte)
repeat(3) {
Mover(Este)
Poner(Negro) } }
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)
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) } }
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) }
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!
procedure PonerN(cantidad,color){
repeat(cantidad){
Poner(color) } }
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).
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.
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
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.
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
procedure PonerMisterioso() {
Mover(siguiente(Este))
Poner(Negro)
Mover(opuesto(Oeste))
Poner(Negro)
Mover(previo(Oeste))
Poner(Negro) }
ESTA ES
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
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) }
[2:3]: No se puede sacar una bolita de color Azul: no hay bolitas de ese color.
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
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
Original Negada
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.
procedure MoverComoSea() {
if (puedeMover(Oeste)) {
Mover(Oeste)
} else { Mover(Norte) } }
if(condicion) {
//código si se cumple condicion
}
else {
//código si no se cumple condición
}
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) } }
function nroBolitasTotal() {
return (nroBolitas(Azul) + nroBolitas(Negro) + nroBolitas(Rojo) + nroBolitas(Verde)) }
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
444 33
0 0
9 1
0
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:
[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.
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.
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) }
program {
PonerUnaBolitaDeCadaColor( ) }
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:
program{
repeat(3){
Poner(Azul)
Mover(Este)
}
Poner(Azul) }
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) }
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(numero)
Retorna un número: el original, negado. Ejemplo:
siguiente(direccion)
Retorna una dirección: la siguiente a la recibida como argumento, es decir, la
próxima en sentido horario. Ejemplo:
previo(direccion)
Retorna una dirección: la anterior a la recibida como argumento, es decir, la
próxima en sentido anti horario. Ejemplo:
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:
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!
function doble(numero) {
return 2 * numero; }
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.
doble(3)
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)); }
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
function esDiaFavorito(diaDelMes) {
return diaDelMes = = = 1 ; }
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
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:
esFinDeSemana("sábado")
true
esFinDeSemana("martes")
false
longitud("biblioteca")
10
longitud("babel") 5
"aa" + "bb"
"aabb"
"sus anaqueles " + "registran todas las combinaciones"
"sus anaqueles registran todas las combinaciones"
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
gritar("miguel")
"¡MIGUEL!"
gritar("benito")
"¡BENITO!"
"todo" + "terreno"
"todoterreno"
"¿" + "Aló" + "?"
"¿Aló?"
BIBLIOTECA
//Equivalente a Math.abs
function valorAbsoluto(unNumero) {
if (unNumero >= 0) {
return unNumero; } else { return -unNumero; } }
/**/
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
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
function esMayorDeEdad(edad) {
if (edad >= 18) {
return true;
} else {
return false; } }
function esMayorDeEdad(edad) {
return edad >= 18; }
medallaSegunPuesto(1)
"oro"
medallaSegunPuesto(5)
"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"
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
meConviene(25, 8)
false // porque el monitor es demasiado chico
meConviene(42, 12)
true // cumple las tres condiciones
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
estaAfinado(440) true
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"
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"
valorEnvido(12)
0
valorEnvido(3) 3
truco 2
retruco 3
vale
4
cuatro
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!
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
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:
Esta function debe retornar 0
¡Dame una pista!
Ah, ¿y cómo se hace para imprimir varias líneas? ¡Llamando a imprimir varias veces !
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
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"); }
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());
Ejercicio 6: Coerciones
Volvamos un momento al código anterior. ¿Notás algo extraño en esta expresión?
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)
function sumaSinSentido() {
return numero + 8; }
function cuentaExtravagante(unNumero) {
let elDoble = unNumero * 2;
if (elDoble > 10) {
return elDoble;
} else {
return 0; } }
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
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;
"numero"
8
16
20
true
¡La respuesta es correcta!
function esImpar(numero) {
return !esPar(numero); }
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
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.
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
V V F
V F V
F V V
F F F
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:
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
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:
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!
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"]
let unaListaVacia = [ ]
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:
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
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"
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.
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}
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}
tajMahal
{ nombre: "Taj Mahal", locacion: "Agra, India", anioDeConstruccion: 1653 }
tajMahal.locacion
"Agra, India"
tajMahal.anioDeConstruccion
1653
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"
CONSOLA
mercurio
=> {nombre:"Mercurio",temperaturaPromedio:67,tieneAnillos:false}
saturno
=> {nombre:"Saturno",temperaturaPromedio:-139,tieneAnillos:true}
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")
¡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
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
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
OTRA SOLUCION
function agregarAPostresRapidos(lista, postre){
if(postre.tiempoDeCoccion <= 60){
agregar(lista, postre) } }¡Muy bien! Tu solución pasó todas las pruebas
let menuDelDia = {
platoPrincipal: "bife de lomo",
ensalada: ["papa", "zanahoria", "arvejas"],
postre: { ingredientes: ["queso crema", "frambuesas"], tiempoDeCoccion: 80 } };
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!
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:
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}]
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([ ])
function gananciaTotal0(balancesDeUnPeriodo) {
let sumatoria = 0;
return sumatoria; }
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; }
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?
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
gananciaPromedio([
{ mes: "marzo", ganancia: 8 },
{ mes: "agosto", ganancia: 10 } ])
9
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
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; }
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
maximaGanancia([
{ mes: "enero", ganancia: 87 },
{ mes: "febrero", ganancia: 12 },
{ mes: "marzo", ganancia: 8} ])
87
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?
minimaGananciaPositiva([
{ mes: "enero", ganancia: -40 },
{ mes: "febrero", ganancia: 42 },
{ mes: "marzo", ganancia: 8},
{ mes: "abril", ganancia: -5} ])
8
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.
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"]
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 ...; }
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( ); }
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;
pesosEnMiBilletera = pesosEnMiBilletera * 2;
diasQueFaltanParaElVerano = diasQueFaltanParaElVerano - 1;
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:
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:
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:
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
Bibliografía complementaria
https://developer.mozilla.org/es/docs/Web/JavaScript
https://es.javascript.info/
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
Pepita == Norita
Pepita == Norita
Norita == Pepita
Norita == Norita
"hola" == "chau"
Pepita.cantar!
Pepita.bailar!
pepita.saludar()
pepita.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:
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)
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
Pepita.energia
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 ♫
interfaz_mercedes = %w(
cantar! )
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! )
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()
pepita.comerAlpiste(40)
pepita.comerAlpiste(6, anastasia)
objeto pepita does not understand comerAlpiste(p0,p1)
pepita.comerAlpiste()
objeto pepita does not understand comerAlpiste()
=>
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()
=> 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
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.
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
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)
#...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
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.
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
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!
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
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!
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
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.
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
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!
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)
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:
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.
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:
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
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)
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.
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.