Errores en Programación y Parámetros
Errores en Programación y Parámetros
Reutilización de herramientas
Programar se trata de constantemente construir herramientas de cada vez más alto nivel que
nos ayuden a resolver nuestro problema. A medida que avancemos en las clases y en la
dificultad, el Poner y Mover van a quedar cada vez más enterrados, porque sino es
imposible construir cosas más interesantes que una línea.
Muchos teclearon en el e y el f de la tarea; cuando se les pidió el DibujarFila, que era
de más alto nivel, se olvidaron del Rojo y Azul salteado y volvieron a Poner y Mover:
¡NO! Siempre construimos sobre lo anterior, los ejercicios están incluso planteados así. En
programación copiar y pegar es un indicio de que algo estamos haciendo mal.
Confusión entre definición y uso
Cuando lo defino, no sé qué valor va a tener, justamente ese es el chiste. Por eso,
nunca nunca nunca un parámetro se va a poder llamar Norte ni Rojo, porque eso
espero que me lo pase quien lo va a usar.
Cuando lo uso, tengo que darle un valor, que por ahora se puede hacer de 2 formas:
con un literal (Azul, Negro, Este, Sur) o “pasándole” otro parámetro (sólo vale
cuando un procedimiento llama a otro, no se puede hacer esto en el program, porque
no puede tener parámetros).
Parametritis
Es un mal que aqueja tanto a aprendices como a profesionales. Cada vez que aprendemos
una herramienta nueva, tendemos a querer usarla por todos lados, aún cuando esto
complejiza nuestro código.
En programación, la mejor solución es siempre la más sencilla, y aprender a hacer eso es
toda una habilidad. Nosotros tenemos una ventaja que no tienen otras disciplinas: cambiar
no nos cuesta casi nada, y tenemos que aprender a aprovecharlo. Moraleja: parametricen si
y sólo si eso les hace escribir menos código, y no más.
Ejercitación
Banderas. Armamos pseudo-banderas de Argentina, Italia, Francia y Perú, con la
condición de que para construir una bandera tenía que haber 3 llamados al mismo
procedimiento. El código, con alguna que otra variante, terminó quedando así:
procedure Argentina() {
FranjaHorizontal(Azul)
Mover(Norte)
FranjaHorizontal(Negro)
Mover(Norte)
FranjaHorizontal(Azul) }
procedure Italia() {
FranjaVertical(Verde)
Mover(Este)
FranjaVertical(Negro)
Mover(Este)
FranjaVertical(Rojo) }
Una vez construidas las 4 banderas, nos dimos cuenta que podíamos generalizar todavía un
poco más: para armar una bandera horizontal o vertical de 3 franjas basta simplemente con
elegir los 3 colores. La segunda versión quedó entonces así:
procedure Argentina() {
BanderaHorizontal(Azul, Negro, Azul)
}
procedure Italia() {
BanderaVertical(Verde, Negro, Rojo)
}
Como cierre, concluimos en que esto simplemente era un ejercicio para usar parámetros y
más parámetros, y por eso seguimos abstrayendo hasta llegar a generalizar la construcción
de las banderas. De nuevo, la solución más simple siempre es la mejor, y sólo vamos a
intentar hacer algo “más genérico” si la situación lo amerita.
Cuadrado relleno. La siguiente tarea fue armar un procedimiento que permitiera construir
un cuadrado de 4x4 con un color para el contorno y otro para el relleno. Surgieron de nuevo
algunas dudas, varixs confundieron el ejemplo que yo di (borde rojo y contorno verde) e
hicieron un procedimiento que sólo construía cuadrados de esos colores.
La estrategia sugerida fue pensar el contorno por un lado y el relleno por otro, quedando
como solución propuesta la siguiente:
procedure Cuadrado(colorRelleno, colorContorno) {
Contorno(colorContorno)
Mover(Este)
Mover(Norte)
Relleno(colorRelleno)
}
Varios dibujos con líneas negras. Lo último, que además quedó como tarea, fue resolver
los ejercicios que están desde la página 9 de la guía en adelante, con dos pequeños cambios:
en vez de implementar los LineaNorte4, LineaEste4, LineaSur4 y
LineaOeste4 que se pedían, la idea es hacerlo todo con un Linea4(direccion);
lo mismo para los LineaNorte4V, LineaEste4V, LineaSur4V y LineaOeste4V,
pero esta vez van a necesitar parametrizar la dirección de la ida y la de la vuelta
Linea4V(direccionIda, direccionVuelta. Obviamente, la de la vuelta
siempre tiene que ser la opuesta a la de la ida (todavía no conocemos una mejor forma
de manejar esto, paciencia…).
Para quienes terminen todos los que están ahí, les queda como desafío hacer este hermoso
dibujo de un numeral o hashtag, alusivo a los tiempos digitales que corren:
Pista: sale pensándolo como dos pasillos (ver ejercicio c).
aprender a usar una nueva herramienta: la repetición simple. En gobstones, esto lo
conocemos como repeat.
Motivación
Hicimos un procedimiento Poner37Verdes, que hace justamente eso,
poner 37 bolitas verdes en el casillero actual. Para ello, no escribimos 37
veces el Poner , sino que usamos el repeat, que es la nueva
herramienta que tenemos .
program {
Poner37Verdes()
}
procedure Poner37Verdes(){
repeat(37){
Poner(Verde)
}
}
Dijimos también que la repetición simple cambia el flujo de ejecución. Antes era recto,
lineal (una línea atrás de la otra), y ahora, cuando aparece un repetir, aparece lo que se
llama un ciclo.
Jugando un poco
A. Hacer un procedimiento Poner10AlLado, que pone 10 bolitas Negras, todas en el
casillero que está al Este del casillero actual.
B. Hacer un procedimiento PonerLejos que ponga 1 bolita Negra 7 casilleros al Este, y
vuelva a la posición original.
Estos dos ejercicios A y B nos sirvieron para entender que se pueden poner instrucciones
antes y después del repeat.
C. Teniendo en cuenta este tablero inicial:
En este ejercicio vimos, sobretodo con los últimos dos programas, que sigue
importando el orden de las instrucciones dentro del repeat así como ya importaba
fuera.
La primera, hacer un zigzag hacia arriba. Como si fuera una impresora, imprimo
hacia el Este, subo, e imprimo hacia el Oeste, y así.
La segunda estrategia es la que elegimos, porque aprovecha que algo se repita. Es
así: Dibujo una línea, vuelvo sin dibujar, y subo. Y así.
procedure Cuadrado5x5Negro() {
repeat(5){
HacerLinea5()
MoverOeste5()
Mover(Norte)
}
}
procedure HacerLinea5(){
repeat(5){
Poner(Negro)
Mover(Este)
}
}
procedure MoverOeste5(){
repeat(5){
Mover(Oeste)
}
}
Más ejercitación
E. Hacer el procedimiento Diagonal4x23(), que haga lo siguiente con bolitas azules:
F. Hacer el procedimiento MoverN(cant,direccion) que mueva el cabezal tantas veces
como se indique en la dirección que se indique
G. Hacer el procedimiento PonerN(cant,color) que ponga en la celda actual tantas
bolitas como indique cant, del color indicado.
H. Hacer de nuevo el ejercicio E, pero ahora haciendo que Diagonal4x23() use el
procedimiento PonerN(...).
I. Hacer el procedimiento LineaVa(long, dir, color) para que dibuje una “línea” de
bolitas en la dirección dir, del color indicado y de la longitud indicada.
J. Hacer nuevamente el ejercicio que hicieron con Fede de ContornoYRelleno(), para
que use los procedimientos anteriores donde convenga.
Nombres de las variables
Hicimos un pequeño apartado en el que hablamos sobre los nombres. Por ejemplo, si quiero
Si queremos Le ponemos Pero no le ponemos y tampoco
Un
moverHaciaArriba
procedimiento Moverhaciaarriba
MoverHaciaArrib (porque un procedimiento
que se llame (porque debemos separar
a debe empezar con
“Mover hacia visualmente las palabras)
mayúscula)
arriba”
Un parámetro CantidadDePasos
cantidaddepasos
que se llame (porque los parámetros
cantidadDePasos (porque debemos separar
“cantidad de comienzan con
visualmente las palabras)
pasos” minúscula)
1. Volver a definir el procedimiento que hicieron con Fede de la guía 2
(PonerConDobleYTriple(cantRojas)), que pone en la celda actual, la
cantidad indicada de celdas rojas, el doble de negras, y el triple de verdes. Esta vez,
sin usar repeat, usando PonerN (que ya la tienen definida) y usando expresiones.
2. Hacer el procedimiento ReplicarAzulesConRojas(), que pone tantas bolitas
rojas en el casillero actual como cantidad de bolitas azules. Para ilustrar lo que hace,
tres ejemplos:
3. Hacer el procedimiento SacarTodas(color) que saca todas las bolitas del
casillero actual que son del color indicado.
4. Ejecutar a mano, a partir del tablero inicial propuesto, este programa, y obtener el
tablero final: ¡NO VALE EJECUTARLO en Gobstones!
formalización de expresiones
La última vez vieron una especie de procedimientos que devuelven o denotan valores:
nroBolitas(color), opuesto(direccion) y otros tantos que ya están en el machete;
el nombre correcto para estos “procedimientos especiales” es expresiones. Entonces a las
que ya conocían, que se llaman expresiones literales (7, Sur, Rojo) se le agregan estas
otras más inteligentes, que hacen alguna operación, computan, pueden depender de la
entrada (argumentos o estado del tablero).
Con esta noción, empezaron a hacer algunos programas y procedimientos que dependen
del tablero. Por ejemplo, ejecutar PasarTodoANegro() tiene un efecto si hay bolitas y
otro efecto (en particular, ninguno) si no las hay. Hoy vamos a seguir en esa línea de “lo
desconocido”, vamos a seguir aprendiendo a preguntarle cosas a Gobstones.
Alternativa condicional
Dentro de todo el universo de preguntas que podemos hacer, las que nos van a interesar son
aquellas que puedan responderse por sí o por no, o siendo más formales por Verdadero y
Falso (hola Lucrecia).
Entre todos, surgieron estas preguntas:
procedure TocoYMeVoy(dir,color){
MoverN(nroBolitas(Negro),dir)
Poner(color)
MoverN(nroBolitas(Negro),opuesto(dir))
}
Acá el paso a paso que explica que cuando ya estoy lejos, no sé cuánto volver, porque el
nroBolitas es una expresión que sí o sí tengo que usar en el casillero inicial. ¡Allá lejos
no hay bolitas negras, están acá!
Solución con truquito (lee el tablero sólo una vez):
procedure TocoYMeVoy(dir,color) {
TocoYMeVoyConCant(nroBolitas(Negro),dir,color)
}
procedure TocoYMeVoyConCant(cant,dir,color) {
MoverN(cant,dir)
Poner(color)
MoverN(cant,opuesto(dir))
}
procedure VolverYMoverTipito(dir){
Mover(Oeste)
Sacar(Negro)
Mover(dir)
Poner(Negro)
}
Dominios: El bosque
Hicimos también el ejercicio del Bosque, que está al final de la Guía 3.
Aquí vuelve a ser importante que aparezcan las abstracciones de dominio en nuestro
programa. En otras palabras, si el enunciado habla de semillas, habla de nutrientes y de
árboles, entonces tiene que haber procedimientos acordes. No vale que nuestro programa
hable sólo de bolitas.
Se ve que los conceptos de “hay semilla” y “hay suficientes nutrientes” que estaban en la
definición del problema se perdieron en la representación: nuestro procedimiento sólo
habla de bolitas de colores. Pero también se ve que con lo aprendido hasta ahora no
podemos mejorarlo, ya que lo que necesitamos es poder crear nuestras propias
expresiones. ¿Y entonces qué hacemos?
Funciones, ¡al poder!
Vamos a sumar a unas nuevas invitadas a nuestra fiesta de la programación: las funciones.
En cierta forma, podemos decir que son primas de los procedimientos, pero tienen una
diferencia fundamental, siempre nos dan algo. Pensemos en las funciones que conocemos
hasta ahora:
Nombre ¿Qué nos da?
nroBolitas(color
Un número
)
puedeMover(dir) Un booleano (V/F)
hayBolitas(color
Un booleano (V/F)
)
A este “algo” que “nos dan” las funciones lo solemos llamar, más formalmente, valor de
retorno, y decimos que una función denota, devuelve o retorna un valor. Si miramos la
tablita también podemos reconocer que estos valores en el universo Gobstones pueden ser
de distintos tipos: números, booleanos y colores (ok, no está en la tabla, pero creanmé que
también se puede).
¡Basta de charla! Empecemos a mejorar el código de Germinar(). Lo primero que vamos
a querer hacer es una función colorSemillas(), para poder poner ahí el color que
elegimos para representarlas.
function colorSemillas() {
return (Rojo)}
Veamos un poco la sintaxis de esto:
la primera línea es similar a la definición de un procedure, sólo que acá la palabra
clave es function y el nombre empieza con una minúscula (como ya habíamos
visto que pasaba con las expresiones primitivas y los parámetros). Podemos tener
también parámetros en las funciones, de hecho todas las que habíamos visto los
tenían, aunque en este caso no hay ninguno;
la última línea (y única en este caso) tiene otra palabrita nueva: el return.
Básicamente, eso se lee como “devolvé Rojo” y siempre nuestras funciones tienen
que tener exactamente un return, el cual debe estar en la última línea de la
definición. Entre los paréntesis va aquello que querramos que nuestra función
retorne, que en este caso es simplemente Rojo.
Para ejercitarlo, releimos el enunciado de “El bosque” y propusimos funciones que nos
ayudaran a abstraer aún más el problema. Más o menos entre todos salieron las siguientes:
cantidadNutrientes()
cantidadArboles()
hayNutrientesNecesarios()
De ahora en adelante, ya no vale usar hayBolitas o nroBolitas cuando podamos en su
lugar escribir una función que abstraiga nuestro problema.
Las invitadas ideales
Volviendo a nuestra metáfora de la fiesta de la programación, las funciones tienen otro
aspecto importantísimo en el cual se diferencian de los procedimientos: antes de irse,
limpian todo y lo dejan igual que como lo encontraron (quién pudiera tener invitados
así…).
Pero qué mejor que comprobarlo uno mismo, volvamos por un rato al mundo de las bolitas
y escribamos la función hayBolitasAl(dir, color), que nos indica si hay alguna
bolita del color dado en la celda vecina correspondiente. Importante: el cabezal tiene que
quedar donde empezó.
Antes de largarnos a implementarla, vamos a ver un truquito para poder probarlas, ya que
ahora lo que nos interesa no es mirar el tablero sino el retorno. Como hacíamos antes, se
puede escribir un program, pero esta vez agregándole un return al final.
program {
return (miFuncionLoca()) }
Pero, si el return tiene que ser la última línea entonces ¿cómo hago para volver? El punto
es que no hace falta, porque como dijimos recién las funciones son limpias o puras, en el
sentido de que al terminar deshacen los efectos producidos sobre el tablero.
Repaso de Funciones
Rehicimos el ejercicio de escapando del incendio:
procedure AvanzarUnPaso(){
if(hayIncendioAlEste()){
MoverTipito(Norte)
} else {
MoverTipito(Este)
} }
procedure MoverTipito(dir){
Sacar(Negro)
Mover(dir)
Poner(Negro)
}
function hayIncendioAlEste(){
Mover(Este)
return (hayIncendio())
}
function hayIncendio(){
return (hayBolitas(colorFuego()))
}
function colorFuego(){
return (Rojo) }
Entonces, queda:
program {
while(puedoMover(Este)){
AvanzarUnPaso()
}
}
¡Y listo! Eso repite mientras se cumpla que se puede mover al Este. En cuanto deja de
cumplirse… ¡para!.
La estructura del while (que es nuestra repetición condicional), entonces, es así:
(Debe funcionar
para cualquier montaña, por alta que sea)
2. Definir el procedimiento CavarTunel(dir), que cava un túnel (saca bolitas de
montaña) desde donde está parado el escalador, en línea recta y hasta que se acabe
la montaña, que puede ser:
o bien porque salí del otro lado de la montaña
o o bien porque llegué al fin del tablero.
3. Estos van de yapa para que practiquen: Definir la función hayOroAl(dir), que
busca oro (representado por bolitas rojas) en esa dirección, y denota si en esa
“línea” hay oro ó no. No hace falta mover al escalador, se puede sólo mover el
cabezal. Por ejemplo, en el siguiente tablero hayOroAl(Este) denota True, pero
Distribución de mercadería
1. Fácil
2. Búsqueda - fácil, usar las funciones de 1
3. Búsqueda, hay que usar opuesto, usar las funciones de 1
4. Usar 2 y 3 + un if y una función para ver si hay suficiente
5. Idem 4
6. Combinación de 4 y 5
7. No lo hacemos - necesita recorrer el tablero
8. Función de búsqueda con retorno
9. Combinación del anterior
Unitarios y federales
Vamos a usar el tablero para representar un campo de batalla, donde intervienen dos
ejércitos: los unitarios (representados con bolitas azules) y los federales (representados con
bolitas rojas).
En cada celda puede haber o no combatientes, y de cada uno de ellos nos va a interesar
conocer su rango. Los rangos están representados por la cantidad de bolitas de colores,
respetando la siguiente codificación:
Cant.
Rango
bolitas
1 Soldado raso
2 Teniente
3 Capitán
4 Comandante
Parte 1: inteligencia
Lo primero que vamos a necesitar es una serie de funciones que nos sirvan para “hacer
inteligencia” sobre el campo de batalla.
1. haySoldado() que indica si hay algún soldado en la celda actual.
2. rangoSoldado(colorEjercito) que indica el rango del soldado perteneciente
al ejército dado que hay en la celda actual.
3. haySoldadoHacia(dir) que indica si hay algún soldado en dirección dir.
4. gradoSoldadoMasCercano(dir, colorEjercito) que devuelve el grado del
soldado más cercano en dirección dir, perteneciente al ejército correspondiente.
5. haySoldadoALaVista(dir, colorEjercito) que indica si hay algún soldado
de ese ejército a la vista, o sea: si miramos en línea recta hacia dir lo primero que
vemos es un soldado de ese color.
6. haySuperiorALaVista(dir, colorEjercito) que indica si hay algún
superior a la vista en esa dirección. Para saber si hay un superior, hay que comparar
con el rango del soldado que está en la celda actual (se puede asumir que hay uno).
Si en el trayecto hacia el superior hay algún enemigo, la función debería devolver
False. Ayudín: lo primero que hay que mirar es si hay un soldado de mi bando a
la vista en la dirección requerida.
7. puedeAvanzarHacia(colorEjercito, dirAvance) que indica si un soldado
ubicado en la celda actual podría avanzar en una dirección dada. Para poder
avanzar, es necesario que haya aliados a mis costados: por ejemplo para poder
moverme al Norte tiene que haber un soldado de mi mismo bando hacia el Este y
hacia el Oeste. Ayudín: revisar las funciones del machete que trabajan con
direcciones.
Parte 2: estrategia
En esta segunda parte nos vamos a encargar de mover distintos soldados por el campo de
batalla.
1. MoverSoldado(colorEjercito, dir) que mueva al soldado de la celda actual
en la dirección dada. Ayudín: como no sabemos el rango del soldado, una vez que
nos movamos no sabremos cuántas bolitas había en la posición original. El truco acá
es construir primero otro procedimiento MoverBolitas(color, cantidad,
direccion) y con ese definir el que se pide.
2. AvanzarLoQuePuedaHacia(colorEjercito, dirAvance) que avance al
soldado de la celda actual todo lo que pueda en la dirección dada, teniendo en
cuenta lo ya mencionado sobre los movimientos de los soldados.
3. ReportarseConSuperiorHacia(colorEjercito, dir) asumiendo que hay
un superior en la dirección dada, avanzar hasta quedar al lado de él.
4. ReportarseConSuperior(colorEjercito) asumiendo que hay un superior en
alguna de las cuatro direcciones, avanzar hasta quedar al lado de él.
Cómo encarar un ejercicio con while y funciones
Buenas! El jueves intentamos continuar con el ejercicio que dejó Fede de tarea. Después de
intentar por un rato largo, hicimos un cambio completo, para atacar algo diferente, y no
quedarnos trabados con eso. ¡Es importante eso! Nos ayuda a despejar la cabeza para luego
poder atacar nuevamente el problema. Además… ejem… debíamos seguir con el tema de la
clase…
Pero sé que varios se quedaron con las ganas de saber cómo se hacía. Así que aquí les
traigo una pequeña explicación que espero les ayude:
¿Hay soldado a la vista?
haySoldadoALaVista(dir, colorEjercito) que indica si hay algún soldado de ese
ejército a la vista, o sea: si miramos en línea recta hacia dir lo primero que vemos esun
soldado de ese color.
¿Nos piden un programa, una función ó un procedimiento? Una función. Entonces,
lo que tenemos que hacer es denotar algo. (devolver algo). Si hubiera sido un
procedimiento, deberíamos concentrarnos en cambiar el tablero, pero nos pidieron
una función, así que lo que importa es lo que denotamos, porque después de lo que
hagamos, el tablero al final quedará igual.
¿Qué tipo denota? Y, dice indica si hay algún soldado… O sea… Verdadero o
Falso! entonces, el tipo de lo que devuelvo es booleano.
Lo más importante es entender qué es lo que me denota. Ya sabemos el tipo, pero
¿Qué significa haySoldadoALaVista? … Significa imaginarse que estoy parado
mirando hacia adelante, y si “veo” un soldado, entonces es cierto.
Una vez que sabemos que queremos una función, que tiene de denotar booleano, y
lo que significa, debemos pensar la estrategia. Si quiero “ver si hay lejos”, mi
estrategia puede tener dos pasos: 1- ir. 2- fijarme y devolver.
Ahora sí un poco de código:
function haySoldadoALaVista(dir, colorEjercito){
# paso 1: intentar ir hasta el soldado
# paso 2: devolver si en la celda a la que llegué hay soldado del
colorEjercito.
}
¡Cuidado! ¡Hay que pensar antes de largarse a escribir! Vale usar lápiz y papel para
plantear borradores.
Y recién ahora podemos desglosar esos pasos: ¿Hay algún procedimiento previo que me
sirva para alguno de los dos pasos?
Para el paso 1: no. Lamentablemente voy a tener que hacerlo.
Para el paso 2: SSssss casi. Tenemos una función haySoldado(), pero se fija si
hay soldado de cualquier ejército, no del color indicado. También vamos a tener
que hacerlo.
Entonces, una vez que ya sabemos que estamos obligados a codificar porque no tenemos
cosas hechas…
function haySoldadoALaVista(dir, colorEjercito){
while( # no llegué a donde quiero... ){
Mover(dir)
}
return (#si estoy parado en el soldado ó no)
}
Y para saber si estoy ó no parado en el soldado que quiero, puedo inventarme una función
haySoldadoDe(color), que me diga si hay un soldado de ese color, de manera que me
queda todo así:
function haySoldadoALaVista(dir, colorEjercito){
while( puedeMover(dir) && not haySoldadoDe(color) ){
Mover(dir)
}
return (haySoldadoDe(color))
}
function haySoldadoDe(color){
return (hayBolitas(color))
}
Ejemplo:
En ese caso, haySoldadoALaVista(Este, Rojo) ¡Denota True! ¡Pero si no hay al
Este…! Ah, sí que hay: es el soldado sobre el que está parado el cabezal. ¡Nunca entró al
while! Es importante notar este caso borde.
Búsquedas
Éste recorrido se lo conoce normalmente como Búsqueda. Las búsquedas tienen la
siguiente estructura:
while( puedo dar un paso && no encontré ){
dar un paso
}
Se llama búsqueda, porque da pasos hasta encontrar, y cuando encuentra para. Esto me
puede servir después tanto para hacer procedimientos como funciones muy útiles.
¿Hay superior a la vista?
haySuperiorALaVista(dir, colorEjercito) que indica si hay algún superior a la
vista en esa dirección. Para saber si hay un superior, hay que comparar con el
rango del soldado que está en la celda actual (se puede asumir que hay uno). Si en
el trayecto hacia el superior hay algún enemigo, la función debería devolver False.
Bueno, empecemos como con el otro ejercicio:
¿Nos piden un programa, una función ó un procedimiento? Una función. Hay que
devolver algo.
¿De qué tipo es? Y, el nombre es hay…., así que tengo que denotar True si hay, y
False si no.
¿Qué significa lo que me piden? Ah, en este hay que pensar más: Al decir “a la
vista” parece similar al anterior… Pero ¡ojo! que las apariencias engañan. Lo que
quiero saber es, no sólo si hay a la vista un soldado, sino que además su rango debe
ser superior al mío.
Entonces, ahora podemos plantear una estrategia que, puede describirse igual que
recién hicimos: 1- Fijarme si hay soldado a la vista. 2. Fijarme si su rango es mayor
al mío. ¡y el resultado es un Y lógico entre ambos!
Borrador:
function haySuperiorALaVista(dir, colorEjercito){
return (hay soldado a la vista && su rango es superior al mío)
}
¿Se ve cómo está pensada la función? Acá, si hay que recorrer, se hará ese recorrido en una
función auxiliar. La primera parte (hay soldado a la vista) es del punto anterior, y podemos
reusarlo:
function haySuperiorALaVista(dir, colorEjercito){
return (haySoldadoALaVista(dir,colorEjercito) && su rango es superior
al mío)
}
y la segunda parte , lo podemos resolver haciendo rango de él > rango mío, ¡y para
eso se puede usar otra función que hicimos antes! Estoy hablando de
gradoSoldadoMasCercano (para el rango de él) e inventarnos una función
gradoActual(color) que diga el rango de la celda actual…
Entonces quedaría:
function haySuperiorALaVista(dir, colorEjercito){
return (haySoldadoALaVista(dir,colorEjercito) &&
gradoSoldadoMasCercano(dir,colorEjercito) > gradoActual(colorEjercito))
}
function gradoActual(color){
return (nroBolitas(color))
}
Peeeeeero hay un problema: ¿Recuerdan el caso borde del que hablamos antes? ¡Tanto
haySoldadoALaVista como gradoSoldadoMasCercano tienen el mismo caso inicial!
Esto significa que si ya hay un soldado del color indicado en la celda actual, no van a
buscar otro lejos, se quedan acá.
Ejemplo:
Apuestas
Ahora, pensemos el ejercicio de Apuestas, de la Guía 4 Parte 2.
El procedimiento AgregarApuesta(nroJugador,nroApostado,monto) que agrega
la apuesta indicada en alguna celda vacía. Precondición: hay al menos una celda vacía.
¿Programa, función ó procedimiento? Procedimiento. O sea, me piden cambiar
algo.
No tiene sentido pensar un tipo de retorno, así que pasamos directamente a qué me
piden: El tablero tiene bocha de apuestas, y casillas que están vacías. El objetivo es
poner una nueva apuesta en una de esas vacías. ¡Guarda! El cabezal puede estar en
cualquier lado…
Y ahora, podemos pensar estrategia: 1- Ir hasta una celda vacía. 2- Poner la
información en la celda encontrada.
procedure AgregarApuesta(nroJugador,nroApostado,monto){
# Paso 1: ir hasta celda vacía
# Paso 2: poner la info
}
Y ahora que tenemos una idea de cómo realizar esa búsqueda, podemos plantearla así:
Les dejamos acá escrito cómo serían los procedimientos y funciones IrAlOrigen(),
haySiguiente() y IrAlSiguiente(), que les sirven para recorrer el TODO el
tablero de abajo hacia arriba, con la estrategia planteada arriba.
¡A la biblioteca directo!
procedure IrAlOrigen(){
IrAlBorde(Oeste)
IrAlBorde(Sur)
}
procedure IrAlSiguiente(){
if(puedeMover(Este)){
Mover(Este)
} else {
IrAlBorde(Oeste)
Mover(Norte)
} }
function haySiguiente(){
return (puedeMover(Este) || puedeMover(Norte))
}
Tarea
Ejercicio 7 del dominio de apuestas (estaElJugador(nroJugador))
Ejercicio 8 del dominio de apuestas
( alguienJugoMasDe(nroApostado,cantMinima))
Metodología Alf para la resolución de problemas
complejos
Nos hacemos las cuatro preguntas:
¿Nos piden un programa (raro a esta altura), una función ó un procedimiento?
En caso de que sea una función, ¿qué tipo denota? Recordemos que los tipos
posibles en Gobstones son: número, color, dirección y booleano.
¿Qué tiene que hacer? Ojo acá con los casos borde.
¿Qué estrategia vamos a seguir? Vale pensar: ¿se parece a alguno de los esquemas
(búsqueda, recorrido de tablero completo, etc) con los que ya trabajamos?
Seguimos con las apuestas
Ejercicio 2. Respondamos las preguntas:
1. Es un procedimiento.
2. No aplica.
3. Recorrer toda la mesa, haciendo lo que corresponda con cada apuesta.
4. Como el tablero puede estar lleno de apuestas, hay que recorrerlo por completo: eso
ya vimos cómo hacerlo. Pensemos ahora lo que hay que hacer en cada celda:
Lo primero, ver si efectivamente hay alguna apuesta.
Si la hay, decidir si ganó o perdió, esto se logra mirando el número apostado.
Si corresponde, Pagar(): paga 5 veces el monto de la apuesta.
Si no, Cobrar(): se queda con todo lo apostado.
procedure PagarYCobrar(nroQueSalio) {
IrAlInicio()
while (haySiguiente()) {
if (hayApuesta()) {
ProcesarApuesta(nroQueSalio)
}
IrAlSiguiente()
} }
procedure ProcesarApuesta(nroQueSalio) {
if (esApuestaGanadora(nroQueSalio)) {
Pagar()
} else {
Cobrar()
} }
La necesidad de recordar
Teniendo en cuenta la restricción de el return tiene que estar en la última línea, ¿cómo
hacemos para que una función retorne cosas diferentes? Ejemplos de esto son: ¿de qué
bando hay más soldados? ¿hacía qué dirección hay un superior? ¿qué número es más
grande entre a y b?
Surge así la necesidad de recordar valores en algún lado, para poder usarlos más tarde o
incluso para poder modificarlos (por ejemplo, para contar algo o hacer una suma de muchas
cosas). Básicamente, lo que hacemos es darle un nombre a un valor, para poder utilizarlo
más tarde en otro punto del programa.
Como ejemplo, pensemos SIN EJECUTARLO EN GOBSTONES cuántas bolitas rojas
pone cada uno de estos programas:
program {
numerito := 2
PonerN(numerito, Rojo)
}
program {
numerito := 2
numerito := 5
PonerN(numerito, Rojo)
}
program {
numerito := 2
numerito := numerito + 2
PonerN(numerito, Rojo)
}
A estos nombres, que se escriben con minúscula inicial y son también expresiones, los
llamaremos variables. Algunos tips para tener en cuenta:
a la acción de darle un valor la conocemos como asignación: asigno el valor 5 a la
variable numerito;
podemos hacer tantas asignaciones como querramos, cada una sobreescribe (pisa) a
la anterior;
si intento usar una variable a la cual nunca le asigné un valor: ¡BOOM!
Repaso de variables
Buenas!
Antes que nada, recuerden que les subí el nuevo machete oficial (v1.1) en la sección de
material.
Hoy continuamos con los ejercicios que dejó Fede de la Guía 5:
Ejercicio 6: nroBolitasAlSiHay(dir,col)
Ejercicio 7: nroBolitasEnVecinas(col)
Ejercicio 8: nroBolitasYoYMisVecinos(col)
Varios se equivocaron e intentaron poner && en vez de +. Vimos que esto era porque no
están pensando en qué me piden. Recuerden siempre tenerlo en cuenta. En la bitácora del
jueves pasado, les puse que lo que necesito saber para resolver un ejercicio es:
Saber si me piden un programa, un procedimiento ó una función.
Entender qué parámetros entran y, si es una función, el tipo de que retorna.
Por supuesto, lo importante, entender qué me piden, ó sea, qué significa el
procedimiento ó la función. ¡Hay que tenerlo en la cabeza todo el tiempo!
Recién después pensar una estrategia, se puede bocetar por arriba con código “de
mentirita”
Y recién después, tirarse a codificar bien.
Ejercicio adicional de variables:
Además, hicimos este ejercicio adicional:
En azul se ve la solución. La idea era hacer el seguimiento de cómo iban quedando las
variables a medida que el programa avanza.
Más Apuestas
Después, continuamos con la guía 5, que tiene la segunda parte de el ejercicio de Apuestas
de la guía 4. Hicimos hasta el ejercicio 5 inclusive, y para esos últimos dos ejercicios
hicimos una puesta en común.
Ejercicio 1: cuantosApostaronA(nroApostado)
Ejercicio 2: cuantaPlataSeApostoA(nroApostado)
Ejercicio 3: cuantaPlataAposto(nroJugador)
Ejercicio 4: algunJugadorApostoMasDe(nroApostado,minimo)
Ejercicio 5: algunJugadorApostoEnTotalMasDe(minimo)
function cuantaPlataSeApostoA(nroApostado){
plata := 0
IrAlOrigen()
while(haySiguiente){
if(hayApuestaA(nro)){
plata := plata + montoApostado()
}
IrAlSiguiente
}
# ¡Caso borde! En la última celda debemos volver a chequear si hay que
sumar el monto...
if(hayApuestaA(nro)){
plata := plata + montoApostado()
}
return (plata)
}
function hayApuestaA(nro){
return (nroApostado() == nro)
}
function nroApostado(){
return (nroBolitas(colorNumero()))
}
function montoApostado(){
return (nroBolitas(colorMonto())) }
Esta es una forma muy válida de pensar una estrategia. No siempre van a ser búsquedas,
pero este pseudocódigo me puede ayudar a pensar lo importante sin concentrarme en
detalles.
Enunciados que guiaron la clase
1. Hacer la función doble(numero), que recibe un número y denota el doble del
mismo.
2. Hacer la función max(nro1,nro2), que denota el máximo entre ambos números.
3. Hacer la función dobleDelMaximo(nro1,nro2), que denota el doble del
máximo entre ambos números.
4. Hacer la función quienGano, que denota el color del equipo ganador de un partido.
En un partido juegan dos equipos (hay dos colores) y cada uno mete una cantidad de
goles. Para pensar: ¿Cuántos parámetros debe recibir la función?, pero también,
¿Cuántas cosas necesito?.
5. Hacer la función diferenciaDeGoles, que recibe un partido y denota la
diferencia de goles.
6. Hacer la función jugoEn(colorEquipo,partido), que dice si el equipo
indicado jugó en ese partido.
7. Hacer la función puntosDe(colorEquipo,partido), que dice cuántos puntos
sacó. Recordemos que en un campeonato de fútbol, los puntos en un partido son: si
el equipo ganó, tiene 3 puntos. Si empató tiene 1 punto, y si perdió tiene 0 puntos.
8. Ahora juguemos con un tipo Fecha, que tiene día, mes y año: hacer la función
esPrimerCuatri(fecha), que dice si esa fecha es del primer cuatrimestre.
9. Hacer la función esNavidad(fecha) que dice si esa fecha es el 25 de diciembre.
Repaso de variables y funciones
Bueno, en principio arrancamos con los primeros tres ejercicios.
function doble(numero){
return (2*numero)
}
function max(nro1,nro2){
maximo := nro1
if(nro2 > nro1){
maximo := nro2
}
return (maximo)
}
Porque hay que reutilizar las funciones de arriba. Recordemos que si primero quiero hacer
el max, y luego quiero hacer el doble, puedo usar la función max y mandarle el
resultado a la función doble.
Eso se hace así:
Y si yo quisiera saber “el cuadrado del doble del máximo”, eso se hace así:
cuadrado(doble(max(nro1,nro2))). ¿Se entiende cómo se van pasando los valores a
cada función?
Intro a Registros
Ahora nos topamos con el punto 4:
Hacer la función quienGano, que denota el color del equipo ganador de un partido.
En un partido juegan dos equipos (hay dos colores) y cada uno mete una cantidad
de goles. Para pensar: ¿Cuántos parámetros debe recibir la función?, pero también,
¿Cuántas cosas necesito?.
Con lo que sabemos hasta ahora, para poder hacer quienGano necesitamos 4 parámetros:
el color y los goles que metió el equipo 1, y el color y los goles del equipo 2.
Entonces, podríamos por ejemplo hacer estas pruebas en el program:
Y para eso la función queda definida así:
Se llaman estructuras de datos a los datos que están compuestos por otros datos. En
nuestro caso, los partidos están compuestos por colores y números. Pero podríamos tener
también fechas, que van a estar compuestas por día, mes y año, ó también podríamos tener
calles, que pueden tener altura y sentido.
Para crear nuestro propio tipo Partido (lo vamos a escribir con mayúscula), vamos a
necesitar poner arriba de todo en nuestro archivo lo siguiente:
type Partido is record {
field colorEq1
field colorEq2
field cantGolesEq1
field cantGolesEq2
}
Fíjense que se llaman campos a cada una de las componentes de un registro. Cada
componente tiene un nombre.
Ahora que tenemos eso en nuestro archivo, en nuestro program debo crear el partido. Para
crear el partido, se escribe Partido(....) y ahí en el medio se pone lo que tiene el
partido adentro, separado por comas, y con una notación medio extraña, que es así:
nombreDelCampo <- valor. Eso significa que en ese campo, el registro tendrá ese
valor.
Rápidamente un ejemplo para no confundirnos:
program {
boliviaArgentina := Partido(colorEq1 <- Verde, colorEq2 <- Azul,
cantGolesEq1 <- 6, cantGolesEq2 <- 1)
riverBoca := Partido(colorEq1 <- Verde, colorEq2 <- Azul,
cantGolesEq1 <- 6, cantGolesEq2 <- 1)
}
Ahí hemos creado dos partidos, y los metimos cada uno en una variable distinta. ¡Todo el
partido entra dentro de una variable!
Ahora podemos usarlo para pasarle a quienGano sólo una cosa:
program {
boliviaArgentina := Partido(colorEq1 <- Verde, colorEq2 <- Azul,
cantGolesEq1 <- 6, cantGolesEq2 <- 1)
riverBoca := Partido(colorEq1 <- Verde, colorEq2 <- Azul, cantGolesEq1
<- 6, cantGolesEq2 <- 1)
return(quienGano(boliviaArgentina))
}
Notar que la estrategia y el algoritmo son exactamente los mismos que en nuestra primer
versión de quienGano. Pero hay algo diferente:
Antes colorEq1 llegaba por parámetro. Pero ahora ¡Por parámetro llega elPartido!
¿Dónde está colorEq1? -> Está dentro del partido.
Lo que es interesante de saber, es que cuando escribimos type Partido is record
{... se generan automáticamente cuatro funciones que me permiten acceder al
registro:
La función colorEq1(partido), que saca el color del equipo 1 de adentro del
partido, y me devuelve ese color.
La función colorEq2(partido), que saca el color del equipo 2 de adentro del
partido, y me devuelve ese color.
La función cantGolesEq1(partido), que saca el la cantidad de goles del equipo
1 de adentro del partido, y me devuelve ese número.
La función cantGolesEq2(partido), que saca la cantidad de goles del equipo 2
de adentro del partido, y me devuelve ese número.
¡No hace falta codificarlas, ya me las dan!. Estas funciones se denominan accessors ó
proyectores de un registro. Fíjense que esas funciones tienen el mismo nombre que el
nombre de las componentes.
Entonces, así se explica por qué ahora tengo que poner:
ganador := colorEq1(elPartido)
en lugar de:
ganador := colorEq1.
Eso es porque ¡Tengo que sacar el colorEq1 de adentro del partido! Y usando las funciones
que me dan, puedo hacerlo. Volvé a leer el código de arriba, de la función
quienGano(partido), que vas a ver cómo estamos usando esas cuatro funciones para
acceder a las cosas que están dentro del partido.
Más práctica
Hacer la función diferenciaDeGoles, que recibe un partido y denota la diferencia
de goles.
function diferenciaDeGoles(unPartido){
return (cantGolesEq1(unPartido) - cantGolesEq2(unPartido))
}
registros
Parte 1: más sobre registros
Ahora juguemos con un tipo Fecha, que tiene día, mes y año:
type Fecha is record {
field dia
field mes
field anio
}
1. Asumiendo que existe una función diaDeHoy() que devuelve qué día es hoy,
escribí edad(estudiante), usando cuantosAniosPasaron. Para poder
probarlo, escribí efectivamente la función diaDeHoy() con cualquier fecha que te
quede cómoda para hacer las cuentas (como por ejemplo, el día en el que estés
haciendo estos ejercicios).
2. elMasViejo(estudiante1, estudiante2), que devuelva al estudiante más
viejo.
3. Se lanzó una beca para estimular las vocaciones TIC, destinada a mujeres de entre
18 y 35 años. Codificá puedeRecibirBeca(estudiante) que indique si un
estudiante podría pedir la beca.
4. esIngresante(estudiante), que nos indique si es ingresante: no tiene que
tener materias aprobadas.
5. estaAvanzado(estudiante) que nos indique si un estudiante está avanzado en
la carrera. Esto es así si ya aprobó más de la mitad de las materias, que son 30 en
total.
6. En la búsqueda de auxiliares académicos, nos pidieron encontrar una pareja
pedagógica que cumpla ciertos requisitos: ambos estudiantes avanzados, de la
misma edad y distinto sexo. Programá la función
puedenSerParejaPedagogica(estudiante1, estudiante2) que indique
eso.
7. podriaSerElPadre(estudiante1, estudiante2) que indique si la primera
persona podría ser el padre de la segunda. Digamos que para que esto sea cierto
debe ser al menos 18 años más grande.
8. nacioEnArgentina(estudiante) que nos indique si un estudiante nació en
Argentina. ¿Cómo darse cuenta? Fácil, los ciudadanos extranjeros tienen DNIs con
números mayores a 90.000.000.
9. Desde la Secretaría de Inclusión de la Universidad nos pidieron desarrollar una
función que determine si un estudiante se encuentra dentro del grupo vulnerable,
para poder realizar un seguimiento especial sobre ellos. Escribí la funcion
estaEnGrupoVulnerable(estudiante) teniendo en cuenta las siguientes
reglas (te conviene mucho dividir en subtareas y prestar especial atención a todo lo
definido hasta ahora):
o los migrantes menores de 21 años lo son, por el riesgo de no poder
sustentarse económicamente y tener que volver a sus países;
o las mujeres entre 25 y 35 años también están incluidas, por la posibilidad de
quedar embarazadas y tener que dejar la Universidad;
o todos los ingresantes lo son, porque pueden sentirse abrumados por la
exigencia y verse tentados a abandonar.
Punto 2:
function cuantosAniosPasaron(fecha1, fecha2){
cuantosAnios := anio(fecha2) - anio(fecha1)
if(esDiaMasAntiguo(fecha2,fecha1)){
cuantosAnios := cuantosAnios - 1
}
return (cuantosAnios)
}
Punto 3:
function ultimoDiaInvierno(elAnio){
return (Fecha(dia <- 20, mes <- 09, anio <- elAnio))
}
Acá es interesante detenerse y notar que estoy armando y devolviendo
una fecha, como si fuera un número.
Teniendo en cuenta que ésto:
cuenta := 5 + 3
return (cuenta)
entonces decir:
laFechaADevolver := Fecha(dia <- 20, mes <- 09, anio <- elAnio)
return (laFechaADevolver)
es lo mismo que:
return (Fecha(dia <- 20, mes <- 09, anio <- elAnio))
Punto 4:
function yaPasoMiCumple(){
return (esDiaMasAntiguo(miCumple(),diaDeHoy()))
}
function diaDeHoy(){
return (Fecha (dia <- 26, mes <- 5, anio <- 2016))
}
function miCumple(){
return (Fecha (dia <- 13, mes <- 11, anio <- 1986))
}
Punto 5 (lo hace cada uno, pero puede tener esta forma:)
function yo(){
return (Estudiante(fechaNacimiento <- miCumple(), dni <- 33333333,
nroMateriasAprobadas <- 43, esMujer <- False))
}
Punto 6:
Acá es importante entender que recibo por parámetro partecitas de un estudiante, y debo
devolver un estudiante entero.
function nuevoIngresante(dni, fechaNac, sexo){
return Estudiante(dni <- dni, fechaNacimiento <- fechaNac, esMujer <-
sexo, nroMateriasAprobadas <- 0)
}
Acá es importante notar que no se puede escribir dia(unEstudiante)
¡Porque el estudiante no tiene dias! Su fechaNacimiento tiene dia.
Acá es importante mirar esta parte:
function nuevoIngresante(dni, fechaNac, sexo){
return Estudiante(dni <- dni, ….
¿Cuál es la diferencia entre esos dos dni? Fácil: recordemos que la sintaxis es:
nombreDelCampo <- valor
Entonces, el que está a la izquierda es el nombre del campo donde estoy queriendo poner
el valor, y el que está a la derecha es el nuevo valor que me viene por parámetro.
Bonus
Una nueva versión de esDiaMasAntiguo, usando la siguiente estrategia: Para saber si el
dia es más antiguo que el otro, puedo cambiar los años de las fechas para que sean iguales,
y luego usar la función esMasAntigua, que recibe dos fechas y me dice cuál es la más
antigua. Ella es la encargada de revisar el día y el mes. ¡Ya lo habíamos hecho la clase
pasada!
/* Versión 2, reeee fumeta BONUS considerando temas que vienen después:
*/
function esDiaMasAntiguo(fechaA,fechaB){
return esMasAntigua(Fecha( dia <- dia(fechaA), mes <- mes(fechaA) ,
anio <- anio(fechaB)) , fechaB),
}
Objetivos de la clase
Introducir la noción de listas, como secuencia ordenada de elementos del mismo
tipo.
Contar que en Gobstones las listas también son inmutables.
Mostrar cómo construirlas, con el literal [] y la concatenación ++.
Mostrar cómo proyectarlas, usando head, tail, last e init.
Ver cómo recorrer una lista usando while.
Constructores y proyectores
Las tres diferencias entre listas y registros:
Un tipo de registro tiene una cantidad fija y definida de componentes. [Link]. el tipo
de registro Partido tiene 5 componentes, Fecha tiene 3. Una lista puede tener la
cantidad de elementos que uno quiera: puede haber una lista con 3 números, una
con 5 números y otra con 8 números, y son todas listas de números.
Cada componente de un registro tiene un nombre, que es el mismo nombre del
proyector correspondiente. Los elementos de una lista no tienen cada uno un
nombre, y por lo tanto no es posible acceder directamente al que yo quiera (ya
veremos que sí podemos programarlo nosotros).
En un mismo registro puede haber componentes de distinto tipo. [Link]. en un Partido
tengo cuatro números y una Fecha. Todos los elementos de una misma lista tienen
que ser del mismo tipo. Si una lista es de números, tienen que ser todos números.
Puede haber listas de distinto tipo, [Link]. una lista de números, una de direcciones y
otra de registros Partido, lo que no vale es que en la misma lista haya [Link]. un
número y una dirección.
Vimos cómo construirla con [] y cómo proyectarla con head, tail, last e init.
Algunos ejercicios sobre esto:
segundo(lista) - el head del tail de la lista
tercero(lista) - el segundo del tail de la lista
sumaDeLosPrimerosTres(lista) - usando head, segundo y tercero
losPrimerosTres(lista) - usando head, segundo y tercero
losExtremosCoinciden(lista) - usando head y last
extremos(lista) - usando head y last
anteultimo(lista) - el last del init de la lista
antepenultimo(lista) - el anteultimo del last de la lista
losUltimosTres(lista) - usando head, anteultimo y antepenultimo
Otra forma de construir una lista es “pegando” otras dos: [1, 2, 3] ++ [4, 5]
equivale a [1, 2, 3, 4, 5]; cuando hacemos esto decimos que estamos concatenando
dos listas. Ojo que ambas cosas deben ser listas, [1, 4] ++ 5 produce BOOM, lo
correcto es hacer [1, 4] ++ [5].
¡A practicar!
duplicar(lista) - usando ++
triplicar(lista) - usando ++
primeroPalFondo(lista) - mezcla de head, tail y ++
elUltimoSeraElPrimero(lista) - mezcla de last, init y ++
multiplicar(lista, cuantasVeces) - usando ++ y repeat
También hablamos de la lista vacía, de la que no se puede proyectar nada, y de cómo saber
si una lista es vacía o no, con isEmpty. Una cosa importante: el tail de una lista de un
elemento es la lista vacía, lo mismo el init.
Recorridos sobre listas
El recorrido es bastante similar al que hacíamos para un tablero, veamos dos ejemplos de
funciones que computan el tamaño:
function tamanioTablero() {
IrAlOrigen()
total := 0
while (haySiguiente()) {
total := total + 1
IrAlSiguiente()
}
return (total + 1)
}
function tamanioLista(lista) {
porRecorrer := lista
total := 0
while (not isEmpty(porRecorrer)) {
total := total + 1
porRecorrer := tail(porRecorrer)
}
return (total)
}
Diferencias importantes:
necesitamos que la lista venga por parámetro, porque ya no es global como lo era
el tablero;
como no tenemos un cabezal, hay que recordar por qué parte del recorrido vamos:
para eso usamos una variable que vamos actualizando en cada iteración;
cuando la lista se termina ya no hay nada más que procesar, no hay caso borde.
En definitiva, la gran diferencia gran es que en este caso el recorrido lo manejamos
nosotros y por eso hace falta usar una variable para saber por dónde vamos (antes esa tarea
la resolvía Gobstones con el cabezal).
Más ejercicios:
sumatoria(lista) - devuelve la suma de todos los números de la lista;
elDobleDeCada(lista) - devuelve una lista donde cada elemento es el doble del
elemento de la lista original;
siguientes(lista) - devuelve una lista con el siguiente de cada número (o sea,
+1);
opuestos(lista) - devuelve una lista con los opuestos de cada número;
reverso(lista) - da vuelta la lista, sale recorriendo con tail e init.
Tarea:
enesimo(lista, posicion) - devuelve el elemeto de la lista que está en la
posición que llegó por parámetro. Por ejemplo: enesimo([3, 8, 13], 2)
devuelve 8, porque es el elemento que está en la posición 2.
repetidos(lista, cuanto) - devuelve una lista que tiene cada elemento
repetido la cantidad de veces que vino por parámetro. Por ejemplo
repetidos([6, 9, 3], 4) devuelve [6, 6, 6, 6, 9, 9, 9, 9, 3, 3,
3, 3], o sea cada elemento de la lista original repetido 4 veces. Pista: usar
multiplicar.
Recorridos
Repaso de recolección (ó “mapeo”).
Filtro.
Recolección con filtro.
Búsqueda.
Enunciados de clase:
1. siguientes(direcciones) - devuelve una lista donde cada elemento es el
siguiente del elemento de la lista original. Si le paso una lista [Norte, Este,
Este, Sur], denota la lista [Este, Sur, Sur, Oeste].
2. Pregunta teórica: ¿Puedo pasarle una lista de números al punto anterior?
3. Pregunta teórica: ¿Puedo pasarle una lista de direcciones al punto opuestos de la
clase pasada?
4. fuerzas(colores) - devuelve una lista de números donde cada elemento
representa la fuerza del color de la lista original. La fuerza del Rojo es 17, igual que
la del Negro. La fuerza del Azul es 15, igual que la del Verde. En una lista
[Negro, Rojo, Verde] el resultado es [17, 17, 15].
5. decapitar(listaDeListas) - devuelve una lista con las cabezas de cada
elemento de la lista original. Por ejemplo, decapitar([[1,2,3,4],
[66,77,88],[9,0,9]]) denota la lista [1,66,9].
6. mayoresA(unNumero, numeros) - recibe un número y una lista, y denota una
lista con aquellos números que son mayores al indicado. Por ejemplo, si quiero
saber los mayoresA(5,[16,3,4,56,7,1,8]) denota la lista [16,56,7,8].
7. losLindos(colores) - recibe una lista de colores y denota una lista con los
colores lindos de la lista original. El Azul es un color feo, los demás son lindos. Por
ejemplo, losLindos([Rojo, Rojo, Azul, Verde, Azul]) denota la lista
[Rojo,Rojo,Verde].
8. losPares(nros) - recibe una lista de números, y denota una lista conteniendo
sólo los números pares de la lista original.
9. tripleDeLosMenoresA(numero,nros) - recibe un número y una lista de
números y retorna una lista, cuyos elementos son el triple de cada elemento menor
al indicado. Por ejemplo tripleDeLosMenoresA(6,[3,8,4,7]) denota la lista
[9,12].
10. fuerzaDeLosLindos(colores) - recibe una lista de colores, y denota la fuerza
de cada uno de los colores lindos de la lista original.
11. ¡No todo son recorridos! Hacer la función multiplosDe3Hasta(tope) -
devuelve la lista de los múltiplos de 3 empezando de 0 hasta llegar al tope. Por
ejemplo, multiplosDe3Hasta(14) da la lista [0,3,6,9,12].
Introdujimos el foreach como una herramienta útil para realizar recorridos
completos sobre listas. Básicamente resuelve por nosotros el problema de recorrer
una lista de izquierda a derecha y en cada iteración podemos “mirar por dónde va”
accediendo a la variable.
Vimos que los registros pueden tener campos que guardan listas y las listas
pueden estar compuestas por registros. Para ejercitar esto, continuamos con la
práctica de los estudiantes universitarios.
Deben terminar de resolver los primeros cuatro ejercicios de la segunda parte y
enviarlos por mail - incluyan también las funciones que utilicen en su resolución.
Punto 2 quemarPrimerPixel
Enunciado y program
Acá el enunciado
Los puntos que se recomienda hacer en clase son: (Igual en casa practiquen con
todos)
Punto 2. diasQueAlquilo(videoclub, nroCliente)
Punto 3. seTeFueLaMano(videoclub, nroCliente)
Punto 5. cargarActor(videoclub, nroPelicula,nroActor)
Punto 6. esDocumental(videoclub,nroPelicula)
Punto 14. cuantoDebe(videoclub,nroCliente)
Punto 8. pelisPedorras(videoclub)
Punto 13. puedoAlquilar(videoclub,nroPelicula)
El program:
type Videoclub is record {
field pelis -- [Pelicula]
field alquileres -- [Alquiler]
}
type Pelicula is record {
field nroPelicula -- numero que identifica a la pelicula
field actores -- lista de numeros (cada numero identifica a
un actor)
field duracion -- numero, indica cantidad de minutos
field cantCopias -- numero, indica de cuantas copias es dueño
el videoclub
}
type Alquiler is record {
field nroPelicula -- numero que identifica a la pelicula.
field nroCliente -- numero que identifica al cliente.
field diasAlquiler -- numero que indica cuantos dias se llevo
la peli
}
program {
lasPelis := [
Pelicula( nroPelicula <- 101, actores <- [20,45,57], duracion <- 126,
cantCopias <- 4),
Pelicula( nroPelicula <- 102, actores <- [20], duracion <- 140,
cantCopias <- 2),
Pelicula( nroPelicula <- 103, actores <- [33,57], duracion <- 50,
cantCopias <- 1)
]
losAlquileres := [
Alquiler(nroPelicula <- 101, nroCliente <- 3001, diasAlquiler <- 7),
Alquiler(nroPelicula <- 101, nroCliente <- 3005, diasAlquiler <- 4),
Alquiler(nroPelicula <- 102, nroCliente <- 3001, diasAlquiler <- 8),
Alquiler(nroPelicula <- 102, nroCliente <- 3005, diasAlquiler <- 4)
]
elClub := Videoclub(
pelis <- lasPelis,
alquileres <- losAlquileres
)
return (diasQueAlquilo(elClub, 3001)) }
se les va a pedir:
Que piensen la estrategia.
Que deleguen y dividan en subtareas.
Que no repitan código (y usen las abstracciones existentes).
Que pongan buenos nombres (que hagan código expresivo).
¡Que piensen! ¡Que usen su criterio!
¿Por qué se les va a pedir esto? Porque en su vida profesional ayuda a construir software de
calidad , flexible y robusto .
Bueno, y siempre van a usar ideas como tipo, lista, registro, variable, procedimiento,
función, recorrido, instrucción, alternativa condicional, repetición, filtro, recolección,
búsqueda …. ¡AGH! ¡Es BOCHA lo que saben! Pero fuera de estas cosas, la lista de arriba
es la importante.
Posible solución para el ejercicio 2.11 de Funcional en Mumuki: [Link]
[Link]/exercises/18-funcional-practica-valores-y-funciones-pinos
Raw
[Link]
Resultados. Ejercicios
Ejercicio
Solución:
/**
* Método que devuelve si un número -a- es mayor que otro -b-
*/
boolean esMayor(int a, int b)
{
return (a>b);
/*
* Setencia equivalente a:
* if (a>b) return true;
* else return false; */
}
Solución:
/**
* Método que devuelva si una cadena de caracteres es igual a otra
*/
boolean esIgualCadena(String cadena1, String cadena2)
{
return ([Link](cadena2));
}
/**
* Dados tres reales devolver el mayor de los tres.
*/
double mayorDouble(double a, double b, double c)
{
double mayor = 0;
if (a>mayor) mayor = a;
if (b>mayor) mayor = b;
if (c>mayor) mayor = c;
return mayor;
}
o Solución:
/**
* Dados dos reales devolver el menor de los dos
* ¿Qué pasa si son iguales? -> Devuelve b
*/
double menorDouble(double a, double b)
{
if (a<b) return a;
else return b;
}
Solución:
/**
* Suponga que está escribiendo un programa para un ordenador de
una caja de * una tienda.
* Tiene que escribir un método que reciba el total de la compra
efectuada y * el importe que entrega el cliente.
* El método mostrará por pantalla "falta importe" cuando la
cantidad
* entregada sea inferior al total de la compra.
* "gracias por su compra" Si el importe entregado es exacto al de
la compra. * Y, "su cambio es X", donde X es el
* cambio a devolver si la cantidad entregada supera al total de
la compra.
*/
void cambioCaja(double total, double entregado)
{
if (total == entregado)
[Link]("Gracias por su compra.");
else if (total > entregado)
[Link]("Falta importe.");
else
[Link]("Su cambio es: " + [Link](entregado
- total));
}
o Solución:
/**
* Suponga que se presenta por pantalla un menú de usuario.
Con las opciones:
* 1.- Seleccionar cliente.
* 2.- Insertar cliente.
* 3.- Editar cliente.
* 4.- Borrar cliente.
* 5.- Salir.
* Se pide implementar un método que reciba un caracter
correspondiente a la opción * seleccionada
* por el usuario y que imprima por pantalla una cadena
significativa de la opción *que ha seleccionado.
*/
void opcionMenu(char opcion)
{
switch (opcion)
{
case '1':
[Link]("Seleccionar cliente.");
break;
case '2':
[Link]("Insertar cliente.");
break;
case '3':
[Link]("Editar cliente.");
break;
case '4':
[Link]("Borrar cliente.");
break;
case '5':
[Link]("Salir cliente.");
break;
default:
[Link]("Opcion no reconocida");
}
}
.
Solución:
/**
* traductor de días de la semana.
* Escribir un método que reciba la inicial del día de la semana y
devuelva * el nombre completo del día.
*/
void diaSemana(char dia)
{
switch (dia)
{
case 'L':
[Link]("Lunes");
break;
case 'l':
[Link]("Lunes");
break;
case 'M':
[Link]("Martes");
break;
case 'm':
[Link]("Martes");
break;
case 'X':
[Link]("Miercoles");
break;
case 'x':
[Link]("Miercoles");
break;
case 'J':
[Link]("Jueves");
break;
case 'j':
[Link]("Jueves");
break;
case 'V':
[Link]("Viernes");
break;
case 'v':
[Link]("Viernes");
break;
case 'S':
[Link]("Sabado");
break;
case 's':
[Link]("Sabado");
break;
case 'D':
[Link]("Domingo");
break;
case 'd':
[Link]("Domingo");
break;
default:
[Link]("Dia incorrecto");
}
}
Ejercicio 8.
Solución:
/**
* Escriba un método que en base a una palabra de entrada en inglés
* permita mostrar
* su traducción al castellano. De momento, sólo se podrán traducir los
* términos:
* computer, mouse y keyboard
*/
void traductor(String termino)
{
if ([Link]("computer"))
[Link]("ordenador");
else if ([Link]("mouse"))
[Link]("raton");
else if ([Link]("keyboard"))
[Link]("teclado");
else
[Link]("Termino no reconocido.");
Ejercicio 9.
Solución:
/**
* Escriba un método que calcule la cuantía económica que supone un exceso
de
* velocidad en una autopista. Las reglas son las siguientes:
* - De 0 a 60Kms/h: multa de 300 euros por velocidad por debajo de
límites.
* - Mayor que 60 Kms/h hasta 120 kms/h: no hay multa.
* - Mayor que 120 Kms/h y menor igual que 140 Kms/h, multa de 300 euros.
* - Ente 140 y 180, multa de 600 euros.
* - Mayor que 180, 1000 euros de multa.
* Nota: Estos valores son inventados.
*/
void calculoMulta(int velocidad)
{
if ((velocidad > 0) && (velocidad <=60))
[Link]("300 euros por velocidad bajo límites");
else if ((velocidad>60) && (velocidad<= 120))
[Link]("Sin multa");
else if ((velocidad>120) && (velocidad<=140))
[Link]("300 euros por exceso de velocidad");
else if ((velocidad>140) && (velocidad<=180))
[Link]("600 euros por exceso de velocidad");
else
[Link]("1000 euros por exceso de velocidad");
}
Ejercicio 10
Solución:
class Figura
{
private int numLados;
/**
* Constructor de la clase.
*/
Figura (int numLados)
{
[Link] = numLados;
}
boolean equals(Figura f)
{
if ([Link] == [Link])
return true;
else
return false;
// Más elegante escribir; return ([Link] = [Link]);
}
} // class Figura
/**
* Dados dos objetos Figura, donde simplemente, una figura está
* representada por el número de lados. Crear un método que reciba dos
* objetos de la clase Figura y determine si son iguales.
*/
boolean figurasIguales(Figura figura1, Figura figura2)
{
return ([Link](figura2));
}
Introducción a JavaScript
3.1. Variables
Las variables en los lenguajes de programación siguen una lógica similar a las
variables utilizadas en otros ámbitos como las matemáticas. Una variable es un
elemento que se emplea para almacenar y hacer referencia a otro valor. Gracias a
las variables es posible crear "programas genéricos", es decir, programas que
funcionan siempre igual independientemente de los valores concretos utilizados.
De la misma forma que si en Matemáticas no existieran las variables no se podrían
definir las ecuaciones y fórmulas, en programación no se podrían hacer programas
realmente útiles sin las variables.
Si no existieran variables, un programa que suma dos números podría escribirse
como:
resultado = 3 + 1
El programa anterior es tan poco útil que sólo sirve para el caso en el que el primer
número de la suma sea el 3 y el segundo número sea el 1. En cualquier otro caso, el
programa obtiene un resultado incorrecto.
Sin embargo, el programa se puede rehacer de la siguiente manera utilizando
variables para almacenar y referirse a cada número:
numero_1 = 3
numero_2 = 1
resultado = numero_1 + numero_2
Los elementos numero_1 y numero_2 son variables que almacenan los valores que
utiliza el programa. El resultado se calcula siempre en función del valor almacenado
por las variables, por lo que este programa funciona correctamente para cualquier
par de números indicado. Si se modifica el valor de las variables numero_1 y
numero_2, el programa sigue funcionando correctamente.
Las variables en JavaScript se crean mediante la palabra reservada var. De esta
forma, el ejemplo anterior se puede realizar en JavaScript de la siguiente manera:
var numero_1 = 3;
var numero_2 = 1;
var resultado = numero_1 + numero_2;
La palabra reservada var solamente se debe indicar al definir por primera vez la
variable, lo que se denomina declarar una variable. Cuando se utilizan las variables
en el resto de instrucciones del script, solamente es necesario indicar su nombre.
En otras palabras, en el ejemplo anterior sería un error indicar lo siguiente:
var numero_1 = 3;
var numero_2 = 1;
var resultado = var numero_1 + var numero_2;
var numero_1 = 3;
var numero_2 = 1;
resultado = numero_1 + numero_2;
La variable resultado no está declarada, por lo que JavaScript crea una variable
global (más adelante se verán las diferencias entre variables locales y globales) y le
asigna el valor correspondiente. De la misma forma, también sería correcto el
siguiente código:
numero_1 = 3;
numero_2 = 1;
resultado = numero_1 + numero_2;
No obstante, a veces las cadenas de texto contienen tanto comillas simples como
dobles. Además, existen otros caracteres que son difíciles de incluir en una variable
de texto (tabulador, ENTER, etc.) Para resolver estos problemas, JavaScript define un
mecanismo para incluir de forma sencilla caracteres especiales y problemáticos
dentro de una cadena de texto.
El mecanismo consiste en sustituir el carácter problemático por una combinación
simple de caracteres. A continuación se muestra la tabla de conversión que se debe
utilizar:
Si se quiere incluir... Se debe incluir...
Un tabulador \t
De esta forma, el ejemplo anterior que contenía comillas simples y dobles dentro
del texto se puede rehacer de la siguiente forma:
var texto1 = 'Una frase con \'comillas simples\' dentro';
11.2. Ejercicio 2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"[Link]
<html xmlns="[Link]
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Ejercicio 2 - Mostrar mensajes complejos</title>
<script type="text/javascript">
var mensaje = "Hola Mundo! \n Qué facil es incluir \'comillas simples\' \n
y \"comillas dobles\" ";
alert(mensaje);
</script>
</head>
<body>
<p>Esta página muestra un mensaje complejo</p>
</body>
</html>