library(karel)
cargar_super_karel() # cargar funciones especiales de karel
generar_mundo("mundo014")
Conceptos básicos de programación II
Aprende a programar con Karel
Ahora, antes de comenzar un tema más avanzado (estructuras de control), considera este ejemplo:
Carga el mundo mundo001
y ejecuta el siguiente código:
- Este mundo es relativamente sencillo. Si queremos que Karel llegué al final (columna 20), podemos decirle que avance 19 veces.
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
avanzar()
ejecutar_acciones()
- Este código, aunque sencillo, es muy ineficiente. Si queremos que Karel avance 100 veces, tendríamos que escribir
avanzar()
100 veces. - En programación, queremos evitar la repetición de código tanto como sea posible.
- Para esto, usamos estructuras de control, que nos permiten controlar el flujo de las acciones de un algoritmo o programa.
- Por ejemplo, podríamos solucionar este problema de estas dos formas:
- Podemos decirle a Karel que avance 19 veces (en lugar de escribir 19 veces que avance)
- Podemos decirle a Karel que avance hasta que encuentre una pared.
- El primer caso, es un ejemplo de una estructura de control secuencial, mientras que el segundo es un ejemplo de una estructura de control iterativa. Vamos a revisarlas:
Estrucutras de control
- Frecuentemente, cuando ejecutamos un programa, queremos que ciertas partes del código se ejecuten solo si se cumple una condición específica, o queremos que se repitan ciertas partes del código varias veces.
- Esto se logra con estructuras de control, que son el conjunto de reglas que permiten controlar el flujo de las acciones de un algoritmo o programa.
- Se dividen en secuenciales, condicionales e iterativas.
Estructuras de control secuenciales
- Estas estructuras están compuestas por un número definido de acciones que se ubican en un orden específico y se suceden una tras otra.
- Los ejemplos anteriores están conformados por este tipo de estructura.
Estructuras de control iterativas
- A veces, queremos que ciertas partes del código se repitan varias veces.
- Para esto, usamos estructuras de control iterativas, que permiten repetir un bloque de código mientras se cumpla una condición específica.
- Las estructuras de control iterativas más comunes son
for
ywhile
. for
se usa cuando sabemos cuántas veces queremos que se repita un bloque de código.while
se usa cuando no sabemos cuántas veces queremos que se repita un bloque de código.- La estructura de
for
en R es la siguiente:
for (i in <valor_inicial>:<valor_final>) {
/es...
...Acción }
- La letra i se usa para representar la cantidad de repeticiones.
- Por ejemplo, si queremos repetir un bloque de código 5 veces, escribimos
for (i in 1:5)
. - Ahora, vamos a ver un ejemplo de cómo usar un
for
para que Karel avance 19 veces en el mundomundo014
.
generar_mundo("mundo014")
for (i in 1:19) {
avanzar()
}ejecutar_acciones()
En este caso, Karel avanzará 19 veces en lugar de tener que escribir
avanzar()
19 veces.Por otro lado, la estructura de
while
en R es la siguiente:
while (condición) {
/es a repetir...
...Acción }
Estas iteraciones pueden continuar mientras que se verifique alguna condición.
el conjunto de instrucciones se repite mientras que se siga evaluando como VERDADERO una condición declarada al inicio del bloque.
Cuando la condición ya no se cumple, el proceso deja de ejecutarse.
En vez de usar un for en el cual hay que especificar la cantidad de veces que el proceso debe repetirse, podemos usar un while para que Karel siga avanza hasta que encuentre una pared en el mundo
mundo014
.El paquete de Karel nos da algunas condiciones que podemos evaluar:
frente_abierto()
…no hay una pared enfrente de Karelfrente_cerrado()
… hay una pared enfrente de Karelizquierda_abierto()
…no hay una pared a la izquierda de Karelizquierda_cerrado()
…hay una pared a la izquierda de Karelderecha_abierto()
…no hay una pared a la derecha de Karelderecha_cerrado()
…hay una pared a la derecha de Karelhay_cosos()
…hay cosos donde se encuentra Karelno_hay_cosos()
…no hay cosos donde se encuentra Karelkarel_tiene_cosos()
…Karel tiene cosos en su mochilakarel_no_tiene_cosos()
…Karel no tiene cosos en su mochilamira_al_este()
…Karel está mirando al estemira_al_norte()
…Karel está mirando al nortemira_al_oeste()
…Karel está mirando al oestemira_al_sur()
…Karel está mirando al sur
Vamos a ver un ejemplo de cómo usar un
while
para que Karel avance hasta que encuentre una pared en el mundomundo014
.
generar_mundo("mundo014")
while (frente_abierto()) {
avanzar()
}ejecutar_acciones()
A veces es más facil visualizar el flujo de un script con un diagrama de flujo. Por ejemplo, el siguiente diagrama de flujo representa el código anterior:
- En este caso, Karel avanzará hasta que encuentre una pared en lugar de tener que escribir
avanzar()
19 veces. - Este código es más flexible ya que Karel seguirá avanzando hasta que encuentre una pared, sin importar cuántas veces tenga que avanzar.
- Si desconociéramos cuántas veces Karel tiene que avanzar, sería más conveniente usar un
while
en lugar de unfor
.
- Es importante tener cuidado con las estructuras de control iterativas, ya que si no se especifica una condición de salida, el programa podría entrar en un bucle infinito.
- Observa lo que pasa en el siguiente código.
- Como es un loop infinito, tendrás que parar el código manualmente. Esto se hace presionando el botón de interrupción en la consola de R (botón rojo o tecla escape).
# No correr! (o sí, para ver que pasa)
generar_mundo("mundo003")
while (no_hay_cosos()) {
girar_izquierda()
}ejecutar_acciones()
- Qué pasa en este código? Karel girará a la izquierda hasta que encuentre un coso, pero en este mundo no hay cosos, por lo que Karel girará indefinidamente.
- Ahora, imagina que Karel está en un mundo totalmente rodeado de paredes.
- Qué pasa si ejecutamos el siguiente código?
generar_mundo("mundo015")
while (frente_abierto()) {
avanzar()
ejecutar_acciones()
}
- Como ves, Karel no puede avanzar porque no hay un espacio libre frente a él.
- En este caso, algo que nos resultaría útil es primero preguntar si hay un espacio libre frente a Karel.
- Esto es un ejemplo de una estructura de control condicional, que veremos a continuación.
Estrucutras de control condicionales
- Para resolver problemas, podemos necesitar que ciertas partes del código se ejecuten solo si se cumple una condición específica.
- Podemos pedirle al código que este se ejecute solo si una condición es verdadera (
TRUE
) o falsa (FALSE
).
if
- La estructura
if
es la más básica de las estructuras condicionales. - Se usa para ejecutar un bloque de código si una condición es verdadera.
- La sintaxis básica de un
if
en R es la siguiente:
if (condición) {
# Código que se ejecuta si la condición es TRUE
}
- Si la condición es verdadera, el código dentro de las llaves
{}
se ejecutará. - Karel tiene las siguientes evaluaciones que podemos usar en un
if
:frente_abierto()
: Evalúa que no exista una pared frente a Karel.izquierda_abierto()
: Evalúa que no exista una pared a la izquierda de Karel.hay_cosos()
: hay cosos donde se encuentra Karelkarel_tiene_cosos()
: Evalúa si Karel tiene al menos un coso en su mochila.mira_este()
: Evalúa si Karel está mirando hacia el este.
- Mira como podemos modificar el código anterior para que Karel avance solo si hay un espacio libre frente a él.
generar_mundo("mundo013")
if (frente_abierto()) {
avanzar()
ejecutar_acciones()
}
- Observa que Karel no avanza porque la condición de
frente_abierto()
es falsa. En este caso, no tenemos error en el código, simplemente no se ejecuta la acción de avanzar. - Algo útil que podríamos hacer es decirle a Karel que, si no hay un espacio libre frente a él, nos diga que no puede avanzar.
if else
- A veces, queremos que se ejecute un bloque de código si una condición es verdadera y otro bloque si es falsa.
- Para esto, usamos la estructura
if else
, que significa “si no es verdadero, entonces realiza esto”. - la sintaxis básica de un
if else
en R es la siguiente:
if (condición) {
# Código si la condición es TRUE
else {
} # Código si la condición es FALSE
}
generar_mundo("mundo013")
if (frente_abierto()) {
avanzar()
ejecutar_acciones()
else {
} print("no tengo espacio")
}
- Ahora Karel nos dirá que no tiene espacio para avanzar si no hay un espacio libre frente a él.
if else if
- A veces, queremos evaluar múltiples condiciones.
- Para esto, usamos la estructura
if else if
, que significa “si no es verdadero, entonces evalúa esta otra condición”. - La sintaxis básica de un
if else if
en R es la siguiente:
if (condición1) {
# Código si condición1 es TRUE
else if (condición2) {
} # Código si condición2 es TRUE
else {
} # Código si ninguna condición es TRUE
}
- Considera el siguiente ejemplo
- en este mundo, podemos hacer lo siguiente:
- Primero, preguntar si hay frente_abierto(), si es verdadero, avanzar.
- Si no es verdadero, preguntar si hay derecha_abierto(), si es verdadero, girar_derecha() y avanzar.
- Si no es verdadero, girar_izquierda() y avanzar.
generar_mundo("mundo024")
if (frente_abierto()) {
avanzar()
else if (derecha_abierto()) {
} girar_derecha()
avanzar()
else {
} girar_izquierda()
avanzar()
}
ejecutar_acciones()
- Cómo podemos mejorar este código para que Karel avance hasta que encuentre una pared y en el proceso recolecte todos los cosos que encuentre?
- En este caso, podemos usar un
while
para que Karel avance hasta que encuentre una pared y unif
para que recolecte los cosos que encuentre y revise que haya frente libre. - Vamos a solucionar los problemas por partes y luego los juntaremos.
- El primer problema que tenemos que resolver es que Karel avanze mientras tenga frente abierto.
generar_mundo("mundo024")
while (frente_abierto()) {
avanzar()
ejecutar_acciones()
}
- Como en este mundo no hay frente abierto, karel simplemente no avanzará.
- Ahora, vamos a resolver el segundo problema, que Karel revise si tiene el frente_izquierdo() abierto y si es cierto, avanzar.
- Primero, podemos poner crear una función que nos permita avanzar hasta que encuentre una pared (código pasado). Depsués, podemos decirle a Karel que gire izquierda y avance hasta que encuentre una pared.
generar_mundo("mundo024")
<- function() {
moverse_hasta_pared while (frente_abierto()) { # Mientras el frente esté abierto
avanzar() # Avanza
}
}
girar_izquierda()
moverse_hasta_pared()
ejecutar_acciones()
- Podemos hacer más eficiente el código anterior con una condición
if
que evalúe si hay izquierda_abierto() y si es verdadero, girar_izquierda() y avanzar().
generar_mundo("mundo024")
<- function() {
moverse_hasta_pared while (frente_abierto()) { # Mientras el frente esté abierto
avanzar() # Avanza
}
}
if (frente_abierto()) {
moverse_hasta_pared()
else {
} girar_izquierda()
moverse_hasta_pared()
}
ejecutar_acciones()
- Pero ahora, considera que podemos mejorar este código. Imagina que Karel no tiene el frente izquierdo libre, no podría avanzar.
- Podemos hacer esto con un
if
else
if
. - En el siguiente código, primero preguntamos si la condición
frente_abierto()
es verdadera, si es así, avanzamos. Si no, giramos a la izquierda. Ahora, antes de avanzar, preguntamos si tenemos el frente abierto, si es así, avanzamos. Esto evita errores si Karel no tiene la izquierda abierta.
generar_mundo("mundo024")
<- function() {
moverse_hasta_pared while (frente_abierto()) { # Mientras el frente esté abierto
avanzar() # Avanza
}
}
if (frente_abierto()) {
moverse_hasta_pared()
else {
} girar_izquierda()
if (frente_abierto()) {
moverse_hasta_pared()
}
}
ejecutar_acciones()
- Ahora, nos falta que Karel recolecte los cosos que encuentre en el camino.
- Podemos hacer esto con un
if
que evalúe si hay cosos y si es así, recolectarlos. - Esto lo podemos hacer dentro de la función
moverse_hasta_pared()
.
generar_mundo("mundo024")
# Función para avanzar y recolectar coso si hay frente abierto
<- function() {
moverse_hasta_pared while (frente_abierto()) { # Mientras el frente esté abierto
avanzar() # Avanza
if (hay_cosos()) { # Si hay un coso, lo recolecta
juntar_coso()
}
}
}
# Función principal que controla el movimiento de Karel
<- function() {
explorar_laberinto if (frente_abierto()) { # Si el frente está abierto
moverse_hasta_pared() # Avanza y recolecta cosos hasta que el frente se cierre
else { # Si el frente está cerrado
} girar_izquierda() # Gira a la izquierda para verificar
if (frente_abierto()) { # Si el lado izquierdo está abierto
moverse_hasta_pared() # Avanza y recolecta cosos en esa dirección
}
}
}
# Ejecución del programa
generar_mundo("mundo024")
explorar_laberinto()
ejecutar_acciones() # Ejecuta las acciones en el mundo de Karel
Como observarás, este es un caso en el que se combinan estructuras de control secuenciales, condicionales e iterativas.
Construir programas complejos a partir de estructuras de control más simples es una de las claves de la programación.
Sin embargo, muchas veces las soluciones más eficientes no son las más complejas, sino las más sencillas y fáciles de entender.
Regresemos al ejemplo del laberinto del final de la lección pasada:
Karel está en un laberinto como el siguiente, donde el coso indica la salida.
- Tip: Hay una estrategia para resolver cualquier laberinto: si cuando entrás tocás con tu mano la pared a tu derecha y caminás sin dejar de tocarla nunca, eventualmente llegarás a la salida.
- implementa el algoritmo de la mano derecha que le ayude a Karel salir del laberinto del mundo
mundo009
.
El flujo del código debe representar la siguiente lógica:
Mostrar Respuesta
generar_mundo("mundo009")
while (no_hay_cosos()) {
girar_derecha()
while (frente_cerrado()) {
girar_izquierda()
}avanzar()
}ejecutar_acciones()
Ejercicios Extra
- en el mundo ‘mundo021’, crea una función que le permita a karel recolectar todos los cosos.
- Intenta lo mismo en el
mundo018
- Haz que Karel recorra los bordes del
mundo020
. - Vuelve a realizar los ejercicios de las últimas dos lecciones y trata de resolverlos con distintas estructuras de control.
- Si tienes problemas, trata de resolver el código por pequeñas partes. Una vez que tengas una parte del código funcionando, puedes agregar más partes. En cada paso puedes usar la función
ejecutar_acciones()
para ver cómo se comporta Karel en cada paso. - Adicionalmente, trata de construir un diagrama de flujo para cada uno de los ejercicios que realices.