0% encontró este documento útil (0 votos)
163 vistas150 páginas

Fundamentos de Programación Con Ruby

Este documento presenta los conceptos fundamentales de la programación con Ruby. Incluye temas como tipos de datos, estructuras de control, funciones, estructuras de datos como arreglos y hashes, y programación orientada a objetos. El documento está dirigido a personas que quieren aprender programación con un lenguaje de alto nivel como Ruby.

Cargado por

Fran Rios
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
163 vistas150 páginas

Fundamentos de Programación Con Ruby

Este documento presenta los conceptos fundamentales de la programación con Ruby. Incluye temas como tipos de datos, estructuras de control, funciones, estructuras de datos como arreglos y hashes, y programación orientada a objetos. El documento está dirigido a personas que quieren aprender programación con un lenguaje de alto nivel como Ruby.

Cargado por

Fran Rios
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 150

1

FUNDAMENTOS
DE
PROGRAMACIÓN
CON RUBY
LUIS EDUARDO MUÑOZ G.

2
Las opiniones expresadas en los artículos son de exclusiva
responsabilidad de los autores y no indican, necesariamente,
el punto de vista de la Corporación CIMTED.

Todo el contenido de este libro está protegido por la ley


según de los derechos materiales e intelectuales del editor
(Corporación CIMTED) y del autor que participó en este libro,
por tanto, no está permitido copiar o fragmentar con
propósitos comerciales todo su contenido sin la respectiva
autorización del autor y citar las respectivas fuentes.

Más informes [email protected]


Publicación electrónica editada en Colombia.
Editado en Medellín, Antioquia - Colombia
Editor: Corporación Cimted © - Noviembre 2022

© Luis Eduardo Muñoz Guerrero


Autor
Profesor Titular
Universidad Tecnológica de Pereira

3
PÁGINA LEGAL
Título de la obra: Fundamentos de programación con
Ruby
ISBN: 978-958-53925-6-4
Sello editorial: Corporación Centro Internacional de
Marketing Territorial para la Educación y el Desarrollo
(978-958-53925)
Materia: Programación
Tipo de contenido: Computación y sistemas
Público objetivo: Formación de adultos
Idioma: Español
Tipo de soporte: Libro digital descargable
Formato: Pdf (.pdf)
Editorial: Corporación Centro Internacional de Marketing
Territorial para la Educación y el Desarrollo
Número de identi cación tributaria: 811043395-0
Email: [email protected]
Fecha de publicación: Noviembre 2022
Depósito digital:

Autor: Luis Eduardo Muñoz Guerrero

4
fi
AGRADECIMIENTOS
A mi hijo Alejandro, por siempre recordarme el signi cado
de la palabra amor verdadero.

A mi madre Ana Guerrero, quien ha sido un ejemplo


fortaleza a lo largo de mi vida.

Agradezco de manera especial al estudiante Miguel Ángel


López Fernández, por sus aportes y el trabajo realizado en el
desarrollo del libro.

5
fi
INTRODUCCIÓN
Este documento presenta los conceptos fundamentales
de la programación imperativa desarrollada con el lenguaje
de Ruby. Se hablará sobre temas relacionados con los tipos
de datos, las funciones y estructuras de control, hasta el
manejo de estructuras de datos como arreglos, listas, pilas,
colas y hashes, así como introducir el tema de la algoritmia y
el paradigma de la programación orientada a objetos
(conocida también como POO).

Se propone como lectura para personas que quieran


iniciarse en la programación con la ayuda de un lenguaje de
alto nivel, es decir, mucho más fácil de comprender. El
documento sirve como base conceptual para experimentar a
futuro con temas más avanzados. Por otra parte, cada
sección está dividida en un componente de teoría y
conceptos, de nición de sintaxis y ejemplos de prueba, y otra
que usa ejercicios que recopilen la información analizada
mediante su desarrollo y análisis. Ciertos apartados disponen
de ejercicios prácticos para ayudar al lector en un proceso de
retroalimentación.

Al nal se menciona la bibliografía usada como


fundamento teórico para desarrollar toda la información aquí
presente, es de recordar que todo es con base a la
documentación o cial soportada por los creadores del
lenguaje Ruby, además de otras referencias como videos y
textos poco académicos que colaboran al desarrollo. De igual
manera se provee de otros recursos como un repositorio en
GitHub con todo el código desarrollado durante la lectura del
texto, y algunas páginas que ayudan a interiorizar temas
como algoritmos, estructuras de datos y la sintaxis del
lenguaje.

6
fi
fi
fi
Tabla de contenido
Página legal 4
AGRADECIMIENTOS 5
INTRODUCCIÓN 6
Tabla de contenido 7
Lista de ilustraciones 11
Lista de tablas 12

INTRODUCCIÓN AL LENGUAJE DE RUBY 13


1. 14
El lenguaje de programación Ruby 14
1.1 ¿Qué es? 14
1.2 Particularidades de Ruby 15
1.3 Ruby en la actualidad 17
1.4 Descarga, instalación y con guración de interfaz de
trabajo 18
1.5 Primeras interacciones con el entorno 21
1.6 Retroalimentación del capitulo 22

COMO ESCRIBIR CÓDIGO EN RUBY 23


2. 24
Estructura de código 24
2.1 Tipos de operadores 24
2.1.2 Operadores lógicos 26
2.2 Tipos de datos 29
7
fi
2.3 Variables 33
2.4 Constantes 37
2.5 Imprimir y obtener información por pantalla 37
2.6 Ejemplos de lo visto en el capítulo 40
2.7 Ejercicios propuestos 44
2.8 Retroalimentación del capítulo 46

3 48
Estructuras de control y ujo 48
3.1 Condicionales 48
3.2 Iteradores 51
3.3 Ejemplos de uso 52
3.4 Ejercicios propuestos 68
3.5 Retroalimentación del capítulo 71

4. 73
Funciones 73
4.1 Estructura 74
4.2 Funcionamiento 75
4.3 Ejercicios propuestos 83
4.4 Retroalimentación del capitulo 83

5 85
Manejo de tipos de datos compuestos (estructuras
de datos) 85
5.1 Conceptos sobre las estructuras de datos 85
8
fl
5.2 Arreglos 87
5.3 Listas 93
5.4 Ejemplos del uso de Listas y arreglos 95
5.5 Pilas y colas 101
5.6 Hashes 103
5.7 Operaciones fundamentales entre estructuras 104
5.8 Retroalimentación del capitulo 107

6 109
Lista de algoritmos más utilizados en
programación 109
6.1 ¿Qué son los algoritmos? 109
6.2 Características y clasi cación de los algoritmos 110
6.3 Pseudocódigo 112
6.4 Algoritmos populares 114
6.5 Retroalimentación del capitulo 129

7 130
POO (programación orientada a objetos) 130
7.1 ¿Qué es la programación orientada a objetos (POO)? 131
7.2 ¿Qué es un objeto?, ¿qué hay de las clases? 131
7.3 Polimor smo 146
7.4 Retroalimentación del capitulo 147

8 148
Anexos 148
9
fi
fi
REFERENCIAS DE BIBLIOGRAFÍA Y WEBGRAFÍA
149

10
Lista de ilustraciones

Ilustración 1. Página web de la herramienta


Homebrew 19
Ilustración 2. Ejecución de comandos propios de
Ruby 20
Ilustración 3. Ejecución del programa que imprime
una pirámide invertida 62
Ilustración 4. Ejecución del programa que imprime
una pirámide 62
Ilustración 5. Representación del proceso que
desarrolla la función sort(). 80
Ilustración 6. Representación grá ca de un arreglo 87
Ilustración 7. Arreglo multidimensional 89
Ilustración 8. Arreglos bidimensionales 90
Ilustración 9. Representación grá ca de una lista 93
Ilustración 10. Representación de una pila 102
Ilustración 11. Representación de una cola 103
Ilustración 12. Funcionamiento de datos en las tablas
hash 104
Ilustración 12. Representación de las operaciones
Push() y Pop() 106
Ilustración 13. Funcionamiento de merge sort 124

11
fi
fi
Lista de tablas

Tabla 1. Operadores aritméticos 25


Tabla 2. Simbología de operado 26
Tabla 3. Operadores lógicos 26
Tabla 4. Tabla de verdad para el operador lógico y
binario and (&&, &). 27
Tabla 5. Tabla de verdad para el operador lógico y
binario or (||, |). 27
Tabla 7. Operadores bianarios 28
Tabla 8. Operadores de comparación 28
Tabla 9. Niveles de procedencia de operadores 29
Tabla 10. Operaciones fundamentales entre
estructuras 105
Tabla 11. Operaciones fundamentales de las pilas 106
Tabla 12. Recursos adicionales 148

12
INTRODUCCIÓN AL LENGUAJE DE RUBY

Se dará una breve contextualización y explicación sobre el


lenguaje de programación Ruby, el campo de acción al cual
pertenece, su historia y particularidades. Se indicarán
también las instrucciones para poder instalar y manejar la
sintaxis Ruby

13
1.
EL LENGUAJE DE PROGRAMACIÓN
RUBY

1.1 ¿Qué es?


Ruby es un lenguaje de programación que se destaca por
su facilidad en el aprendizaje, se puede implementar en
varios campos tecnológicos siendo este un gran bene cio
para abordar problemas desde un modelo funcional,
imperativo, abstracción a objetos, o entre muchos otros.

Fue diseñado principalmente para mejorar en los


programadores facultades como la productividad,
mantenimiento y comprensión de sus códigos. Esta última se
enfatizó hasta el punto de hacer de Ruby un lenguaje
divertido de aprender, incluso, su creador piensa que en el
mundo de la tecnología los humanos somos los masters,
ellos (las computadoras) los esclavos, por lo que todo
debería ser en pro del entendimiento humano para controlar
una máquina, no al contrario.

La losofía de Ruby se generaliza en tres grandes


características: la primera es el principio de bajo asombro, la
segunda es la fácil disposición de recursos para un enfoque
más pragmático en la escritura de código, y la tercera es la
transparencia del lenguaje. El principio de bajo asombro
(POLA, por sus siglas en inglés principle of low astonishment)
indica que el lenguaje de programación arbitrario debe de
14
fi
fi
comportarse según lo esperado por quien lo usa para así
evitar tener momentos de confusión y asombro-impacto. La
segunda indica la capacidad de Ruby para proveer de
servicios que permitan al programador tener a su disposición
múltiples vías de solución a un problema en especí co, ya
sea por medio de algún paradigma de programación[1],
funciones que reduzcan el tiempo invertido, sintaxis
comprensible, adaptación de funcionalidades de otros
lenguajes, simpleza, entre muchas otras. Como tercera, se
entiende como transparencia del lenguaje la capacidad de
explicarse por sí mismo, esto es debido gracias a la
particularidad que tiene Ruby de ser de alto nivel[2].

1.2 Particularidades de Ruby


Ruby tiene un gran listado de características que lo
convierten en una gran herramienta para la solución de los
problemas tecnológicos del día a día.

[1] Los paradigmas de programación son, de manera


general, modelos conceptuales que indican la forma en cómo
se dan las instrucciones a la maquina (computador). Algunos
de los paradigmas más conocidos y utilizados son el
funcional (encargado de indicar que instrucciones hacer, esto
generalmente por medio de funciones ya diseñadas), el
imperativo (encargado de indicar como hacer las
instrucciones, es decir, explicar a detalle la orden a ejecutar),
el proposicional (encargado de ejecutar instrucciones
partiendo de estructuras lógicas formales, es decir,
basándose en la lógica de proposiciones).
[2] Se conoce como lenguaje de alto nivel aquel que se
maneja y expresa de manera similar al lenguaje humano.
Algunos ejemplos además de Ruby son Python, ALGOL,
Basic, Erlang, etc.
15
fi
Algunas de las características más sobresalientes son (1):

● Lenguaje expresado y trabajado completamente con la


programación orientada a objetos, soportando herencia y
simulación de herencia múltiple con mixins (estructuras
propias del lenguaje), así como el uso de la meta
programación.
● Es de tipado dinámico, es decir, que no es obligatorio
expresar el tipo de dato a utilizar. También incorpora mucho
azúcar sintáctico en muchas funciones, esto sin contar la
sintaxis propia del lenguaje que también se le atribuye el
término mencionado. Azúcar sintáctico se re ere a
facilidades de sintaxis que provee el lenguaje.
● Sintaxis que permite acercarse al dominio de un
problema, así como permitir la exibilidad y conveniencia
observada en otros programas como Python. En otras
palabras, una forma de programar muy amigable para los
usuarios.
● Estructuras propias basadas en lenguajes mucho más
antiguos como Smalltalk (relación de semántica) o Perl
(relación de sintaxis).
● Dinamicidad en el manejo de variables (tanto para
clases como para métodos).
● Recolector de basura, lo que indica que el uso de la
memoria en tiempo de ejecución, conocida como memoria
de HEAP es gestionada (su uso y liberación) de manera
automática.
● Manejo de excepciones, facultad necesaria para
realizar pruebas o tests en un código determinado.
● Manejo de Hilos.
● Actualmente existen muchas implementaciones del
lenguaje Ruby con características propias como relación de
cambio a otros lenguajes como C o Java.
● Tiene su propio sistema de gestión de paquetes
conocido como RubyGems.

16
fl
fi
● Es un lenguaje compilado justo en el tiempo de
ejecución (Just-in-time compilation por sus siglas en inglés).
Esta es una técnica que utiliza las dos formas de traducción
de código conocidas, la compilación y la interpretación,
donde primero se compila el código volviéndolo a código de
bits (bytecode en inglés) para luego justo al momento de
iniciar la ejecución del programa, se inicie al mismo tiempo la
interpretación (traducción) del código de bits anterior al
código de máquina.

1.3 Ruby en la actualidad


1.3.1 ¿Se usa Ruby hoy en día?

Como ya se ha mencionado, Ruby es un lenguaje de


propósito general trayendo consigo múltiples modelos de
programación, imperativa, funcional, lógica, POO, entre otras.
Es por ello por lo que la capacidad que provee para
solucionar y desarrollar múltiples ideas es bastante grande.

A continuación, se comparten algunos de los proyectos


documentados en la web los cuales están hechos
principalmente en Ruby (2):

● Hackety Hack, un programa que enseña a programar


usando Ruby.
● Sass (Syntactically Awesome Style Sheets), una
extensión en el trabajo con CSS (Cascading Style Sheet)
usando variables en la expresión de reglas de estilo. Fue
originalmente escrito en Ruby, y actualmente se encuentra
bajo C/C++.
● Homebrew, el instalador de paquetes utilizado en el
documento para descargar Ruby.
● Discourse es un aplicativo web que funciona como
listero de mensajes, foros de discusión y salas de chat. Está
escrito en Ruby con ayuda de otras herramientas como bases
17
de datos Postgres o el framework Rails (también escrito en
Ruby).
● Sinatra es un DSL (Domain Speci c Language, o
lenguaje de dominio especí co) creado netamente en Ruby y
dedicado a resolver problemas relacionados con el
desarrollo web de una manera más e caz.

Existen docenas y docenas de proyectos elaborados con


Ruby, unos por completo y otros como ayuda en aspectos
muy especí cos.

Lo anterior son algunos ejemplos de lo que se puede


lograr con Ruby, que en su mayoría están relacionados con el
desarrollo Web. Por otra parte, no se debe aislar la idea de
que este lenguaje no pueda ser usado para crear
aplicaciones de escritorio, juegos en 2D, inteligencias
arti ciales y machine learning, entre otras.

1.4 Descarga, instalación y con guración de


interfaz de trabajo
La instalación de Ruby puede hacerse de varias maneras
algunas por medio de la descarga del paquete o archivo que
contiene al lenguaje de Ruby, como también se pueden usar
sistemas de gestión de paquetes desde la terminal de cada
equipo. Para este caso se usará la última opción, la cual
proporciona ciertos bene cios como resumir la instalación y
con guración del lenguaje en el equipo a través de solo un
conjunto de instrucciones.

18
fi
fi
fi
fi
fi
fi
fi
fi
1.4.1 Procesos de descarga e instalación (Mac)

Conociendo el método, los pasos a seguir son los


siguientes:

● Descargar un gestionador de paquetes como por


ejemplo Homebrew.

Ilustración 1. Página web de la herramienta Homebrew


Fuente: The Missing Package Manager for macOS (or Linux) — Homebrew

● Se deben de seguir las instrucciones mencionadas allí


(ubicadas bajo el comando de terminal debajo del título
Install Homebrew).
● Otra manera de instalarlo es por medio del terminal del
equipo digitando el siguiente comando:

$ brew -v

19
● Luego de tener la herramienta gestionadora de
paquetes, se debe de correr el siguiente comando en
terminal:

$ brew install ruby

● Posteriormente, se debe de con gurar la variable de


entorno[3] con el siguiente comando en terminal:

$ echo 'export PATH="/usr/local/opt/ruby/bin:$PATH"'


>>~/.bash_profile

● Para nalizar se puede veri car si la instalación fue


correcta aplicando los siguientes comandos:

$ ruby -v //Para verificar la versión


$ ruby -e "puts 'Hello World'" //Para correr una instrucción con Ruby

Como resultado se debe de obtener:

Ilustración 2. Ejecución de comandos propios de Ruby

La instalación solo se describe para sistemas operativos


IOS (como por ejemplo los portátiles Mac), más, sin embargo,
al nal del documento en la sección de recursos se dejarán
algunos enlaces que proporcionarán información al respecto.

20
fi
fi
fi
fi
1.5 Primeras interacciones con el entorno

Nota: En el presente documento se desarrollarán


ejercicios que propicien el uso del lenguaje de Ruby a partir
del IDE RubyMine por JetBrains. Es decisión propia de cada
lector sobre que IDE’s utilizar.

1.5.1 Hola mundo desde Ruby

Se comienza con la creación de una de las frases más


célebres en el mundo de la programación. “Hello, ¡world!”, un
pequeño programa nacido en 1978 con el documento “The C
programming language”.

Ahora bien, dependiendo del IDE seleccionado, se debe


crear un nuevo archivo en una carpeta cualquiera (puede ser
una destinada al desarrollo de este libro) y nombrarlo como
helloWorld.rb (rb es la extensión de Ruby). Luego de abrir el
archivo se debe de digitar allí lo siguiente:

[3] Las variables de entorno es un concepto atribuido a


variables pertenecientes al sistema operativo que corre
dentro de un equipo de cómputo las cuales permiten
almacenar, acceder y obtener cierta información de este,
como rutas de archivos, funciones y especi caciones. Por
ejemplo, la variable de entorno PATH almacena las rutas de
ciertos archivos a ejecutar.

1 puts "Hello World!! from RubyMine"

Se debe de ejecutar en la línea anterior (es decir correr el


archivo) y obtener la siguiente salida:

$ Hello World!! from RubyMine

21
fi
¡¡¡Con ello se ha creado el primer código en Ruby!!!

1.6 Retroalimentación del capitulo

En este capítulo se dio contexto sobre el objeto del libro,


el lenguaje de programación Ruby. En qué se basa, algo de
historia y sus principios, su desempeño en la actualidad, el
procedimiento para instalarlo en computadoras con sistema
operativo IOS y un primer contacto con el distintivo mensaje
“Hello World”.
De todo ello, se puede resumir que:

 Ruby fue diseñado para mejorar en los programadores


las facultades de productividad, mantenimiento y
comprensión de código, mediante herramientas que fueran
de bajo asombro y multipropósito, es decir, que aborden
problemas desde un modelo funcional, imperativo,
abstracción a objetos, entre otros.

 Otras características de Ruby son: tipado dinámico (no


se necesita expresar el tipo de cosas que se utilizan),
lenguaje de alto nivel comparable con la sintaxis de Python,
cuenta con su propio recolector de basura (quien maneja la
memoria al momento de usar Ruby), maneja su propio
sistema de gestión de paquetes, entre otras.

 Algunos ejemplos de los resultados que puede otorgar


la programación con Ruby son Sass (herramienta usada para
CSS), el gestionador de paquetes de Mac y Ruby on Rails,
framework para el desarrollo de sitios web.

22
COMO ESCRIBIR CÓDIGO EN RUBY
Se hablará sobre la estructura de la sintaxis utilizada en
Ruby para la escritura de pequeños programas, así como
analizar temas fundamentales para la programación como los
tipos de datos y operadores, estructuras de control y ujo,
funciones, estructuras de datos. Por último, se explicarán
temas más avanzados como la programación orientada a
objetos.

23
fl
2.
ESTRUCTURA DE CÓDIGO

2.1 Tipos de operadores

Al igual que las matemáticas en donde se utilizan ciertas


operaciones representadas por cierta simbología como lo
son la suma (+), resta (-), multiplicación (*), división (/), o
incluso operaciones más complejas como derivadas e
integrales, la programación también maneja su propio
conjunto de símbolos comúnmente llamados operadores. El
nombre permite conocer cuál será la función de dicha
simbología, y en este caso se trata de poder ejecutar
operaciones entre uno, dos o más datos entre sí.

A continuación, se presentarán los operadores aritméticos


propios de la computación actual, y que con los cuales se
nos permite relacionar matemática y lógicamente un conjunto
de instrucciones escritas por computadora.

2.1.1 Operadores aritméticos

Estos operadores (por su nombre, aritmética) permiten


representar las operaciones elementales realizables con
números. Dichas operaciones son las conocidas sumas,
resta, multiplicación y división.

24
Nombre Suma Resta Multiplicación División

Símbolo + - * /

Tabla 1. Operadores aritméticos

Ruby utiliza generalmente la notación in ja para poder


realizar las operaciones descritas por estos operadores. Por
ejemplo:
1+1
2 - 100
1 * 56
90.2 / 2.5678

Si bien se recuerda, en las matemáticas se cuenta con


niveles de jerarquía de operadores, incluso de otros símbolos
como el paréntesis, la llave y el corchete. De manera similar
ocurre en la programación Ruby. Dichos niveles de jerarquía
indican el orden de importancia (y de ejecución) de las
operaciones dadas en una expresión, por ejemplo:

(4 *(3*(3+90) / (78*1034)))

(20/45*(89+17*49))

En ambas expresiones se debe comenzar (por regla


general) por resolver los paréntesis más internos,
posteriormente se efectúa la jerarquía de operaciones
presentada continuación:

Orden
Multiplicación (*)
División (/)

25
fi
Suma (+)
Resta (-)
Tabla 2. Simbología de operado

2.1.2 Operadores lógicos

Al igual que los operadores de la suma o de la resta existen


expresiones que determinan las comparaciones usadas en la
lógica de proposiciones como el Y (AND) o el O (OR). Este
grupo indica el cumplimiento de una condición u operación
lógica y son elementos muy comunes en estructuras de
control como condicionales e iteradores.

Nombre Símbolo Función

NOT !,~, not Niega un dato booleano (dato


verdadero o falso)
AND &&, and Evalúa dos elementos a la vez.
Los dos deben de tener el
mismo valor (verdadero o
falso), caso contrario
devuelve falso.
OR ||, or Escoge el valor entre dos
datos, ya sea verdadero o
falso.
Tabla 3. Operadores lógicos

Ejemplo

Partiendo de la simbología anterior, se puede presentar el


siguiente código:

1 print("NOT: < true -> (Not) -> #{!true} > || < false -> (Not) -

2 > #{not false} >\n")

3 print("AND: < true (And) false -> #{true && false} > || < false

26
4 (And) false -> #{false and false} >\n")

5 print("OR: < true (or) false -> #{true || false} > || < false

6 (And) false -> #{false or false} >\n")

La ejecución del código es:


$ NOT: < true -> (Not) -> false > || < false -> (Not) -> true >

AND: < true (And) false -> false > || < false (And) false -> false
>
OR: < true (or) false -> true > || < false (And) false -> false >

Se pueden presentar varias posibilidades en cuanto al uso


de operadores lógicos con respecto a una salida booleana
(verdadero o falso), por ende, se proporcionan las tablas de
verdad de cada uno de los operadores según sean los casos
posibles:

A B A && B
Verdad Verdad Verdad
Verdad Falso Falso
Falso Verdad Falso
Falso Falso Falso
Tabla 4. Tabla de verdad para el operador lógico y binario and (&&, &).

A B A II B
Verdad Verdad Verdad
Verdad Falso Verdad
Falso Verdad Verdad
Falso Falso Falso
Tabla 5. Tabla de verdad para el operador lógico y binario or (||, |).

27
Nombre Símbolo
NOT !,~
AND &
OR |

Tabla 7. Operadores bianarios


Las operaciones dadas a partir de estos operadores son
basadas en la lógica de circuitos y sistemas binarios.

Nota: En este documento no se comentará respecto a la


teoría detrás de los operadores binarios, más si se
recomienda al lector consultar al respecto.

Cabe tener en cuenta que los resultados mostrados para


los operadores lógicos, incluyendo la clasi cación de las
tablas de verdad son los mismos para los operadores
binarios con la diferencia de que al ser sistemas binarios
estos se manejan con 0’s y 1’s representando falso y
verdadero respectivamente.

2.1.3 Operadores de comparación

Como su nombre lo indica, es la simbología encargada de


comparar dos o más datos. Su representación es:

Tabla 8. Operadores de comparación


28
fi
Estos operadores son utilizados principalmente en la
evaluación de condiciones, de frases o expresiones en las
que se pueda determinar si algo es verdad o no. Por esta
razón se pospondrá su ejempli cación hasta llegar al tema
de condicionales

2.1.4 Niveles de procedencia de los operadores

A continuación, se presenta la siguiente tabla que indica


el nivel de procedencia de los operadores descritos
anteriormente

Tabla 9. Niveles de procedencia de operadores

2.2 Tipos de datos


Los seres humanos pueden clasi car toda la información
que les llega a niveles sumamente singulares, es decir, al
punto de dividir y categorizar cada parte en lo más mínimo
posible. Por ejemplo, a partir de un texto se puede destacar
que este se puede componer de letras, que juntas conforman
palabras y consecutivamente forman oraciones, ideas, y
párrafos completos; en ellos pueden estar inmersos números;
caracteres especiales, inclusive imágenes; en general, un
gran conjunto de información representativa de un objeto o

29
fi
fi
idea. Todo ello permite desglosar un cúmulo de información
en partes trabajables y analizables. Del mismo modo pasa en
las computadoras, y de una manera mucho más rígida.

En las computadoras actuales se pueden identi car


distintos tipos de información, desde datos primitivos, es
decir datos atómicos (que están en su forma mínima; en un
contexto de funcionamiento de un programa, no son
reducibles, por ejemplo, los números enteros), hasta
estructuras compuestas por lo anterior. Toda esta abstracción
permite modelar el mundo en el que actualmente todo ser
humano habita.

2.2.1 Primitivos

Antes de comenzar la clasi cación cabe mencionar que


en Ruby directamente no existen los tipos de datos
primitivos, como se menciona anteriormente en el
documento, Ruby es un lenguaje puramente orientado a
objetos, lo que indica que todo se concibe como un objeto.
Tanto los datos a mencionar como los literales de operadores
están representados bajo los conceptos de clases y objetos.
Sin embargo, se mantendrá la noción de datos primitivos
para comprender a nivel general los tipos de datos dentro
del mundo de la programación.

● Enteros

En lenguaje de código se les conoce como integer o int,


son un tipo de dato que representa una cantidad numérica
entera positiva o negativa. Existe también su versión sin
signo, la diferencia radica en la cantidad de memoria usada
para representar cada uno.

30
fi
fi
● Flotantes

Abarcan los números con decimales. Se destacan por


precisar la magnitud de un número, si es grande o pequeño
en comparación con otros. En código se les conoce como
oat.

● Booleanos

Son datos con dos posibles valores, verdadero o falso. En


programación suelen representarse como 0’s o 1’s, al igual
que utilizando las convenciones de true o false. Los
operadores lógicos y de comparación producen resultados
de este tipo de dato, usados mayormente en estructuras de
control como el for, while o if.

● Caracteres

Un carácter es la forma más básica de representación de


las letras y todo lo relacionado a texto y símbolos. Ruby
decide ser más general y agrupara todo esto como un solo
tipo de dato, string.

● Strings

Como su nombre en inglés indica (cadenas en español)


son cadenas compuestas por caracteres. En los lenguajes de
alto nivel Ruby el manejo de este tipo de datos suele ser
bastante dinámico y exible.

2.2.2 Compuestos

Anteriormente se determinaron los datos compuestos


como una estructura formada por los datos primitivos
conocidos. Si bien es cierto que la de nición es muy general,
por lo que una breve estandarización podría ser el verlos
31
fl
fl
fi
como un conjunto de datos que abstraen aún más la
información proporcionada con respecto a su organización y
manejo. Estos tipos de datos pueden ser conocidos con el
término de TAD o tipos de datos abstractos.

Estos TAD comparten con los otros tipos de datos


mencionados el objetivo de representación y manejo de la
información, con la diferencia de que estos son a grandes
escalas mientras que aquellos que son primitivos son a
escalas de unidad.

La existencia de este tipo de datos se debe al hecho de la


creciente cantidad de información generada por la raza
humana, y con el n de poder hacer algo se optó por
proporcionar una herramienta (de todo el equipo conceptual
y por ende material necesario) que fuera útil en el manejo de
esta. Otra de sus razones es el bene cio de velocidad y
e ciencia (en algunas) de espacio físico y virtual en el
tratamiento de la información.

Tipos de datos abstractos

Algunos de los tipos de datos abstractos más utilizados y


conocidos son:

● Arreglos
● Matrices
● Listas
● Pilas y colas
● Tablas hash
● Árboles binarios

Más adelante en el desarrollo del documento se ahondará


en el concepto y funcionamiento de algunos de estos tipos
de datos.

32
fi
fi
fi
2.3 Variables

Se identi can como elementos que almacenan


información la cual puede estar cambiando. Este concepto
permite referenciar algún valor clasi cado según los tipos de
datos vistos anteriormente.

La particularidad de Ruby de ser un lenguaje puramente


orientado a objetos permite tratar todo lo expresado por
medio del lenguaje de manera tal que no se tenga que
especi car en cada momento el tipo de dato a utilizar, y de
aquí proviene su capacidad de ser de tipado dinámico. Por
ejemplo, si se comparan la forma de hacer una suma entre
dos números entre el lenguaje de Ruby, con otro que no
posea la característica anterior como lo puede ser C++, se
tendría el siguiente resultado:

Código escrito en C++ Resultado

33
fi
fi
fi
Código escrito en Ruby Resultado

Antes que nada, se puede observar la longitud de cada


código, por un lado, la solución propuesta en C++ es más
grande en comparación con el programa propuesto en Ruby.
Por otra parte, el nivel de detalle de instrucciones es mucho
más grande en el primero por lo que este se convierte
naturalmente en un lenguaje con una sintaxis demasiado
rígida, mientras que Ruby tiene la capacidad de poder
ejecutar la expresión de la manera más general posible.

Nótese que las variables utilizadas en la operación suma


en el programa de C++ deben de estar necesariamente
indicadas con el tipo de dato que almacenarán (si es int, char,
bool, entre otros), mientras que Ruby permite tomar cualquier
expresión como si fuese una variable que almacenará un
valor que pueda ser cambiado en cualquier momento,
diferencia radical con su contraparte, las constantes.

Esta clasi cación de los datos permite poder dividir y


analizar problemas de una manera mucho más fácil y
concreta puesto que todo lo observable se compone de
datos, información. Con ello se podrá desenvolver el lector a
lo largo de los capítulos observando los ejemplos planteados,
y en los que no cabe duda de que se utilizará toda la
información relacionada al respecto.

2.3.1 Clasi cación de las variables según su alcance

34
fi
fi
Cuando se menciona “según el alcance” se hace
referencia al espacio existente accesible por una variable, es
decir, dependiendo del lugar en donde se cree o se “declare”
la variable, se determinará si esta es local o global.

● Globales: Variables que pueden ser usadas en todo


momento y lugar.

● Locales: Variables que pueden ser únicamente


utilizadas en su ambiente de declaración como por ejemplo
las funciones, estás utilizan variables locales para de nir su
funcionamiento interno.

Un ejemplo sería el siguiente:

La ejecución del código anterior es:

Para entender un poco más lo que sucede con el código,


hay que tener claro lo que se intenta hacer. A partir de la
de nición de una función something() y de las expresiones a
y b, una dentro de la función y otra fuera , lo que se quiere
intentar es tratar de demostrar el concepto de variable local.
Tanto a como b son variables locales, a solo puede ser usada
35
fi
fi
en el ambiente de de nición de la función donde ha sido
declarada, es decir, something(), mientras que b solo puede
ser usada en su ambiente de declaración, es decir por fuera
de la función something().

La ejecución indica que se está usando algo de forma


inde nida en el método (o función a término general) llamado
“b”, lo cual vendría siendo el intento de poder acceder a una
variable local por fuera del ambiente que se está trabajando.
b está de nido por fuera de la función, más como se
caracteriza por ser local esta no puede ser accedida dentro
de la función, lo que resultaría entonces en el error arrojado.
Caso similar ocurrirá si se intenta acceder a la expresión a.

Ahora bien, para ilustrar el caso de una variable global se


puede hacer lo siguiente:

La ejecución de lo anterior sería:

En el código anterior se tiene un cambio que permite dar


grandes diferencias en cuanto al tipo de expresiones que se
han mencionado. Nótese que la variable b esta vez viene
expresada por el carácter especial $. Esta nueva expresión
indica que una variable puede ser usada en cualquier
ambiente, ya sea dentro de la declaración de alguna función
o por fuera de la misma. A la par se tiene también que para
36
fi
fi
fi
poder acceder a ella se debe de usar tal cual la forma como
se declaró incluyendo el carácter mencionado. Gracias a
todo esto se pueden ejecutar las operaciones dadas.

2.4 Constantes

Al igual que las variables, son expresiones que almacenan


valores con información constante, que no puede ser
cambiada en ningún momento. En un programa pueden
de nirse matemáticamente valores constantes como el valor
pi (π) o el número de Euler (e).

También se dividen en dos grupos, constantes globales y


constantes locales, que al igual que en las variables, la
primera será de uso general para todo momento y espacio,
mientras que la segunda sólo podrá utilizarse en su ambiente
declarativo (donde se crea).

2.5 Imprimir y obtener información por


pantalla
La información que es utilizada por un programa puede
ser obtenida únicamente por medio del ingreso de datos
como por ejemplo el uso de la terminal o de interfaces
grá cas, usado en programas como aquellos conectados a
una base de datos, archivos de texto o los que se disponen
en formato CSV (Comma Separated Values). Por esta razón
es primordial conocer los métodos de obtención de
información, y en este caso, su estructura en el lenguaje de
Ruby.

Cada programa tiene sus particularidades al respecto, en


el caso de Ruby se tiene una simpleza deslumbrante para el
manejo del control de ujo de los datos, la cual radica en el
uso de funciones que gestionan la obtención y

37
fi
fi
fl
almacenamiento de información como su manejo para
mostrarse al usuario. Dichas funciones son:

● Puts: Poner en inglés, es la función que permite


mostrar datos en pantalla principalmente dentro de una
terminal.

● Gets: Obtiene la información introducida (también por


medio de la terminal). Una particularidad es que esta
información para ser almacenada se debe usar alguna
expresión como lo son las variables o incluso las constantes.

Ahora bien, un ejemplo de lo anterior sería:

La salida del código anterior:

Hay una característica particular en el código anterior,


nótese que en el mensaje “cuál es su edad” la idea de la
echa es indicar que justo después de ella debe ir el
mensaje, más sin embargo en la ejecución no se muestra de
esa forma. Esto es debido al uso de la herramienta puts, la
cual siempre muestra mensajes en pantalla dejando al nal
un salto de línea. Por suerte Ruby soporta el uso de otras
generalidades como la expresión print() que se comporta de
manera similar, con la diferencia que este último no deja
saltos de línea. Con el siguiente ejemplo se puede obtener lo
deseado:

38
fl
fi
La ejecución:

Otro aspecto para tener en cuenta es el detalle mostrado


en el mensaje de la línea 3 de los dos códigos anteriores.
Como se puede observar, se muestra un pequeño texto en
donde con ayuda de la expresión #{…} se pueden mostrar
variables de una manera más e ciente, ordenada y corta; es
por esa razón que dentro de las llaves precedidas por el
numeral se escribe la variable o expresión que almacén los
datos guardados por gets. Por otra parte, la impresión de
variables y múltiples datos puede ser presentado de la
manera convencional:

Se observa que gracias a la operación de suma aplicado


para textos o cadenas de caracteres se puede manejar
múltiples de un mensaje, como se muestra en la línea

El resultado sería:

Surge otra situación y es el cambio de línea luego de


mostrar la edad ingresada. Esto es debido a la función gets
que de modo automático asigna un salto de línea en la
ejecución luego de que el valor fue ingresado. Si se desea
esto puede solucionarse utilizando algunos de los métodos
diseñados para gets (recordar que todo en Ruby es un
objeto):

39
fi
Su ejecución:

Nota: La sintaxis de Ruby no es rígida en cuanto al uso de


paréntesis y otros caracteres que de nen una expresión. Por
ello se tiene la libertad de llamar métodos de un objeto con o
sin paréntesis, aunque estos dependen de si el método a
usar tiene o no parámetros de nidos, como de si se
necesitan mandar o no parámetros extras, entre otras cosas.

2.6 Ejemplos de lo visto en el capítulo

En esta sección se explicarán algunos códigos que


agrupan toda la información recopilada en el capítulo 2 de
este documento. En ellas se podrá observar el uso de los
operadores aritméticos en forma de expresiones
matemáticas, variables (en su caso locales, las globales
podrán ser mejor entendidas en el uso de funciones) que
almacenen datos ingresados por el usuario, entre otras
características.

Ejemplo #1

Números enteros y otantes

Se presenta a continuación el siguiente código:

40
fl
fi
fi
En el código anterior se utilizan las funciones necesarias
para la obtención e impresión de datos por pantalla (puts,
print, gets). Este funciona de tal manera que se pueda
obtener dos números con los cuales se podría realizar
operaciones aritméticas básicas como la suma, resta,
multiplicación y división. Por otra parte, hay ciertas
características como el uso de la estructura #{…} o el manejo
de castings (término que se re ere a cambio o paso de un
tipo de dato a otro, por ejemplo, pasar un dato de tipo string
a uno de tipo entero) para poder convertir la información
ingresada por el usuario en datos manejables por las
operaciones descritas.

Una prueba de lo que puede realizar el código anterior es:

41
fi
42
Ejemplo #2

Manejo de expresiones matemáticas por medio de la


jerarquía de operandos

El código anterior permite evaluar la expresión


polinomio_arbitrario al ingresar datos para los elementos que
lo conforman, estos son number_a, number_b, number_c,
number_d. Estos elementos son variables locales las cuales
almacenan el valor retornado por la función gets en
compañía de chomp y casting hacia un dato de tipo entero.

Analice un momento la expresión de polinomio_arbitrario,


observe que se compone únicamente de paréntesis como
indicadores de jerarquía. Debe de existir un número igual de
parejas de paréntesis para abrir o cerrar, de lo contrario se
arrojaría un error al momento de la ejecución. Para veri car
que el concepto de jerarquía de operadores se entiende se
propone calcular el valor que calcularía polinomio_arbitrario
si los valores de sus elementos fuesen:

43
fi
number_a = 1
number_b = 2
number_c = 3
number_d = 4

Nota: al tener una división entre números enteros, Ruby


de modo automático aproxima el resultado a la parte entera
que lo compone si no se especi ca el tipo de resultado
obtenido, es decir, para el caso que se tiene del ejemplo
donde 32 es dividido por 3, el resultado debería de ser
10.66666667, sin embargo, al no especi carse que será un
dato de tipo otante el valor que toma la variable donde esta
operación se almacena es 10.

Ahora bien, si se ejecuta el código, el resultado de


polinomio_arbitrario debe de ser igual:

Como se puede observar, el análisis escrito de la


expresión polinomio_arbitrario es correcto comparado con
los resultados que arroja el código sugerido.

2.7 Ejercicios propuestos

Se proponen los siguientes ejemplos:

44
fl
fi
fi
1. Crear un programa que permita calcular el volumen
de un cilindro, una esfera y un cono. Las ecuaciones de
las respectivas guras son:

Al ejecutar el programa, este deberá estar en capacidad


de recibir los datos necesarios para el cálculo de todos los
volúmenes y mostrar los resultados en pantalla.

2. Escribir un código que reciba un número entero


correspondiente a la edad de un usuario y que retorne
el número de meses, días y horas que lleva vivo.

3. Crear un programa que partiendo del ingreso de


números enteros y otantes permita calcular polinomios
de segundo y tercer grado.

Se recomienda que el lector consulte temas relacionados


con el uso de librerías matemáticas, como por ejemplo
funciones que calculen la raíz cuadrada de un número
natural.

4. En un teatro un cliente paga $20 por la entrada y


cada función le cuesta al teatro $300. Por cada cliente
que ingresa al teatro debe pagar un costo de $2 por
aseo adicional (es decir, $20 de la entrada más $2 del
aseo). Desarrollar un programa que reciba el número de
clientes de una función y devuelva el valor de las
ganancias obtenidas por el teatro entendiendo que
para que el teatro obtenga ganancias, el número de

45
fi
fl
clientes que se deben de alcanzar es más de 15 (es
decir, cliente >= 15).
5. En una tienda de variedades se ofrecen descuentos
en cada categoría. Las categorías son: juguetes para
niños con 10%, cuadros de arte con el 5%, productos de
maquillaje con el 7% y ropa con el 9%. Un cliente desea
llevar $100.000 en juguetes para niños, $50.000 en
cuadros de arte, $35.000 en productos de maquillaje y
$100.000 en ropa. Se necesita calcular el descuento
total por cada tipo de producto, posteriormente
entregar el valor total a pagar con descuento y sin
descuento.

6. Descomponer un número de 5 cifras en unidades (1),


decenas (10), centenas (100), milésimas (1000) y diez
milésimas (10000) y decir la cantidad correspondiente,
ejemplo: el número 631 tiene 1 unidad, 3 decenas y 6
centésimas.

7. Del ejercicio anterior, descomponer un número e


invertirlo, por ejemplo, 582 pasaría a ser 285.

2.8 Retroalimentación del capítulo

En este capítulo se analizaron los componentes


fundamentales para realizar operaciones aritméticas, además
de introducir los tipos de datos y el concepto de variable.
De manera general, se puede resaltar que:

 Existen diferentes operadores según el tipo de


operación, estas pueden ser aritméticas (suma, resta,
multiplicación, división), lógicas (NOT, AND, OR, TRUE,
FALSE), comparativas (igual, menor y menor o igual que,
mayor y mayor o igual que, diferente). A su vez se determinó
que todos estos grupos siguen un orden de jerarquía que

46
determina la prioridad con la que se leen al momento de
crear y evaluar expresiones

 Hay diferentes tipos de datos, desde los primitivos en


donde se encuentran los numero enteros, otantes (o
decimales), booleanos (valores lógicos como verdad y falso)
caracteres y cadenas (ambos relacionados a las palabras que
se usan en un abecedario y los textos formados a partir de
estos); hasta los compuestos que son de nidos por un
programador según una necesidad en particular y que
pueden estar hechos de varios de los tipos de datos
primitivos mencionados.

 Las variables se pueden entender como aquellos


elementos que almacenarán un valor que puede o no
cambiar a lo largo de un tiempo. Dicho valor corresponde a
un tipo de dato especí co. Se sabe también que estas
variables pueden ser globales, donde todo el código conoce
de su existencia, como locales, donde solo es conocida por
una pequeña porción de código.

 Al igual que las variables, existen las constantes,


elementos que almacenan un valor y que este no podrá ser
cambiado en el momento en que un código se está
ejecutando.

 La interacción entre las personas y el código en Ruby


se puede dar por medio de instrucciones que permiten el
ujo de lectura y escritura, por ejemplo, usando las
funciones print o put para imprimir un texto en pantalla y la
función gets para leer información digitada por el usuario.

47
fl
fi
fi
fl
3
ESTRUCTURAS DE CONTROL Y FLUJO

Son aquellas que permiten modi car el ujo (continuidad


o camino) de información ya sea por medio de pausas para
realizar operaciones como la de evaluar una determinada
condición, o muchas otras más. Basándose en la
programación estructurada o por módulos, Ruby permite
estructuras de repetición y estructuras condicionales, las
cuales tienen como característica común de trabajar de
acuerdo con una condición generada por la obtención de un
valor a través de uno o más procesos.

Cada estructura es similar en la mayor parte de lenguajes


de programación con la diferencia que existen múltiples
versiones con mejoras como la sintaxis y posiblemente
comandos o funciones especiales.

3.1 Condicionales
También llamadas de selección, son estructuras que
proporcionan un conjunto de acciones siempre y cuando se
cumpla una condición dada. Algunos modelos son:

3.1.1 Estructura de condición if

Gracias a la característica de Ruby ya mencionada su


sintaxis se vuelve mucho más amigable por lo que la
estructura de los condicionales se representaría como:

48
fi
fl
If <condiciones>
<Bloque de instrucciones #1>
else
<Bloque de instrucciones #2>

El campo de condiciones puede ser reemplazado con


expresiones que retornan un valor booleano ya sea
verdadero o falso lo que repercutirá en que se ejecute el
bloque de instrucciones #1 o el bloque de instrucciones #2
según sea el caso respectivamente. Por otra parte, estas
expresiones pueden componerse a partir del uso de
operadores aritméticos y de operadores lógicos.

Este modelo permite el anidamiento de estructuras, es


decir, la creación de nuevas estructuras dentro de una
estructura inicial, así como el uso de múltiples condiciones
con su versión if-elsif-else.

3.1.1.1 Estructura de condición if-elsif-else

De manera similar cumple el mismo papel que la


estructura base de una condición if, con la diferencia de que,
en lugar de tener únicamente dos opciones resultantes de
una sola comparación, esta permite evaluar múltiples
condiciones en una sola estructura y proveer múltiples
opciones resultantes. Su representación es:

If( <condiciones> )
<Bloque de instrucciones #1>
elsif
<Bloque de instrucciones #2>
.
.
.

else

49
<Bloque de instrucciones #n>

3.1.2 Condicional case-when

Al igual que el if, es un condicional que ejecuta un


conjunto de operaciones dependiendo de un valor
resultante. Se compone de lo siguiente:

case <variable>

when <Valor 1 de variable> {<bloque de


instrucciones #1>}
when <Valor 2 de variable> {<bloque de
instrucciones #2>}
.
.
.
when <Valor n de variable> {<bloque de
instrucciones n>}
else {<bloque de instrucciones>}
end

De forma general el conjunto de pasos que se ejecutan


es:

● El campo variable almacena un valor el cual servirá


para determinar un cierto conjunto de instrucciones a
ejecutar.
● Se recorren todos los when, buscando aquel que sea
igual al valor de variable.
● Si se encuentra aquel que cumpla la condición se
ejecuta el bloque de instrucciones respectivo.
● Caso contrario se ejecuta el else (si se propone) con su
bloque de instrucciones. Este else será un caso default o
caso general que servirá para indicar que no hay valores en
la estructura case-when que coincidan con el valor de
variable.
50
● Se termina la ejecución del case-when al llegar al end.

3.2 Iteradores

Los iteradores o estructuras de repetición componen un


conjunto de operaciones que se ejecutan un determinado
número de veces, esto hasta que se cumpla o se mantenga
una condición. Algunos de los modelos más utilizados son el
for y el while. Estos pueden variar su presentación, algunas
de ellas son:

3.2.1 For
for <variable> in [variable o tope] do
<bloque de operaciones>
end

<variable> es un tipo de contador, es decir que almacena


el número de veces que se ha repetido el <bloque de
operaciones>. La parte de [variable o tope] permite de nir un
límite, evitando así la ejecución de las operaciones de
manera in nita.

3.2.2 While

while <condición> do
<bloque de operaciones>
end

De manera similar al for, el while se ejecuta un cierto


número de veces según lo determine una condición. Esta
condición puede tener un contador como ocurre con un ciclo
for utilizando el valor de verdad de las expresiones lógicas, o
simplemente no tenerlo.

51
fi
fi
3.2.3 While modi cado

<bloque de instrucciones> while <condición>

También:

begin
<bloque de instrucciones>
end while <condición>

O también haciendo uso de una estructura until:

Nota: Para nes prácticos del lector, until hará el mismo


trabajo que while, su única diferencia radica en que uno
permite tener mejor comprensión del código escrito al
momento de leerlo.

until <condición> do
<bloque de instrucciones>
end

3.3 Ejemplos de uso


En esta sección se observarán algunos ejemplos sobre el
funcionamiento de las estructuras iterativas y de condición,
tanto su desarrollo general como algunas de sus variantes.

3.3.1 Ejemplos que involucren condiciones

● Crear un programa que mediante los datos ingresados


por un usuario se retornen características de un producto con
su respectivo pecio. En este caso los productos a manejar
serán componentes de un computador, celulares, gadgets
tecnológicos, entre otros.

52
fi
fi
Código solución

Las opciones son manejadas usando la estructura if-elsif-


else y case-when. A partir del ingreso de un número
comprendido entre 1 y 6 el cual se almacena en la variable
opcion (y que a la vez maneja las funciones para recibir datos
53
por teclado) este se evalúa en ambas estructuras y según sea
el caso se ejecutan diferentes instrucciones, por ejemplo, al
ingresar el número 5 la respuesta será las características del
producto speed max, mientras que si se ingresa el número 3
la respuesta será las características del producto RTX 3080
ti. Cabe aclarar que el uso de una estructura u otra
dependerá de la situación y los valores que se manejen, una
estructura compuesta por if-elsif-else puede tornarse algunas
veces más larga de escribir que un case-when, o en un caso
distinto, representar dos valores de una condición con un
case-when puede no ser la mejor opción comparado con una
estructura if-else.

Como regla general, cuando se usan muchos valores se


usa la estructura case-when, mientras que cuando se usan
muchas condiciones diferentes pero relacionadas se usa la
estructura if-elsif-else. Como un caso base, siempre se puede
partir de un if-else.

Con todo lo anterior, al momento de ejecutar el código se


obtienen los siguientes resultados:

 Con la opción No. 1:

54
Cabe resaltar que lo que causa los grandes espacios en
blanco entre cada mensaje producido es el uso de las
funciones puts y los caracteres especiales ‘\n’ que indica que
la siguiente impresión se haga en una nueva línea. Existen
otros tipos de caracteres especiales (conocidos como
caracteres de escape) como ‘\t’ para tabulación, ‘\s’ o
‘\space’ para generar un nuevo espacio, entre otros.

● Crear un programa que indique (usando solo


condicionales), cual es el menor de 3 números.

Código solución

55
El código anterior resuelve la situación descrita por dos
métodos diferentes, cada uno se emplea según los
siguientes análisis:

● El método #1 se basó en el hecho de que al tener tres


números (a,b,c), independiente del hecho que estén o no
ordenados, se deben de hacer las comparaciones
empezando por el primero y avanzando por cada uno si no
se cumple el requisito de que uno tiene que ser menor que
los restantes.

En el código, más especí camente la forma en la que se


estructura la condición indica que al iniciar con cada
comparación hay la posibilidad de que se eviten números en
comparaciones futuras, es decir, si el proceso de
comparación empieza por el número a comparándolo con b y
c, y dado el caso que a no sea menor que alguno de los dos
anteriores, se puede concluir que la respuesta esté
comparando únicamente b y c, que serían los números más
pequeños después de a. En ese orden de ideas, continuaría
el proceso con el número b comparándose con c, repitiendo
el patrón anterior de descarte.

● El método #2 se basa en la idea de que un número es


menor a otro si y sólo ese número es menor que todos los
demás, por ejemplo, de los tres números (a, b, c)
mencionados, a sería el menor si es menor que b y c, y así
sucesivamente sucedería con b y c.

Al ejecutar el código y probarlo con la tripleta de números


1, 2, 3 en algunas de sus combinaciones se obtiene que:

56
fi
● Usando el método No. 1:

● Usando el método No. 2:

3.3.2 Ejemplo que involucren iteraciones

● Calcular la serie de Fibonacci a partir de un límite


especi cado.

Código solución

57
fi
La famosa serie de Fibonacci, la cual inicia como
0,1,1,2,3,5,8,13,21,34, 55, [n = (n-2) + (n-1) + … ] también puede
ser representada por medio de una estructura de iteración
for. El razonamiento para poder ejecutar lo solicitado es el
siguiente:

● A partir de la fórmula que describe la serie y dada su


secuencia puede ser in nita, se escoge por determinar un
límite de cálculo, es decir, escoger el número de dígitos
pertenecientes a la serie que serán calculados.
● Con un límite de nido, utilizando una estructura de
iteración se puede determinar la fórmula iterando desde el
caso base, el número cero hasta el dígito limite. El cálculo de
un número perteneciente a la serie se logra mediante la
de nición de n_menos_1, n y suma, variables las cuales
representarán n-2, n-1 y ((n-1) +(n-2)) respectivamente. Cabe
mencionar que los primeros valores presentes en la serie al
ser casos base pueden ser proporcionados como valores
iniciales en la declaración de cada una de las variables
mencionadas.
● Con todo lo de nido hasta este punto se puede crear
el programa, en este caso se pide ingresar la información de
un límite para el cálculo de la serie, posteriormente luego de
la declaración de las variables se crea una estructura for que
itera desde un rango de cero hasta el límite ingresado. En
este se de ne y se reasignan valores en la operación de la
serie de Fibonacci, los cuales a su vez serán presentados en
la pantalla de la terminal.

Ejecutando lo anterior se obtienen los siguientes


resultados:

● Usando como límite de cálculo el número 100:

La serié de Fibonacci en un rango de [0....limite] es:

58
fi
fi
fi
fi
fi
● Elaborar un programa que permita imprimir en pantalla
las siguientes guras:

59
fi
Código solución

Forma #1

60
Forma #2

Los dos casos anteriores manejan un análisis similar, es


decir, para poder representar dichas pirámides se debe de
tener en cuenta los siguientes factores:

● El número de niveles a representar.


● El número de espacios que hay para poder imprimir
cada nivel.
● El número de asteriscos impresos por cada nivel.
● Determinar el momento en el cual la impresión de
asteriscos debe detenerse para poder dar la gura indicada.
● Entre otros.
Ejecutando el código se logra obtener la gura solicitada:

61
fi
fi
Figura #1

Ilustración 3. Ejecución del programa que imprime una pirámide invertida

Figura #2

Ilustración 4. Ejecución del programa que imprime una pirámide

Para entender un poco mejor lo que se está realizando se


explicará el primer ejemplo de forma detallada, y el segundo
quedará a disposición del lector.

Caso #1

La situación que se pide analizar propone la formación de


una pirámide invertida usando asteriscos.

Nótese que en la gura #1 las las se completan de forma


descendente y consecutiva hasta llegar a un tope
determinado por el usuario. También se puede observar que
el número de asteriscos con los que se completa cada la es

62
fi
fi
fi
par de dos en dos, siendo un caso de ejemplo con un límite
de las igual a seis, la primera la estaría con doce
asteriscos, la segunda con diez, la tercera con ocho, y así
sucesivamente hasta llegar al mínimo posible que en este
caso sería dos.

Para crear la gura se deben conocer las relaciones


existentes y saber cómo debe comportarse el programa en
múltiples situaciones. Lo primero es que se tiene que
conocer el límite de la pirámide, es decir, la altura o número
de las. Para ello se pueden utilizar variables que almacenen
dicho valor (y el cual deberá ser suministrado por teclado
según el ejercicio). Para el caso del código propuesto se
usará el nombre de niveles.

Otra de las relaciones para tener en cuenta, y que está


mencionada anteriormente en el texto, es la impresión del
número correcto de espacios por la. Se puede observar que
a medida que el número de asteriscos impresos por la
disminuye, el número de espacios aumenta; dicho
comportamiento es de mucha utilidad al momento de
desarrollar el componente principal. Con esto en mente se
sabe que espacios al ser un número cambiante, también
puede ser almacenado por una variable, que para el código
mencionado será espacios.

Ahora bien, en este momento se puede hacer un gran uso


de las estructuras de iteración puesto que se debe de repetir
un proceso un número determinado de veces, que es la
impresión de espacios y asteriscos. Para ello se utilizará la
estructura for.

En el código se crea lo siguiente:

for iteracion in (niveles).downto(0) do



End
63
fi
fi
fi
fi
fi
fi
Hay una particularidad con lo anterior y es la forma en
cómo se indica que el número de iteraciones irá
decreciendo. Se escogió la manera decremental debido a
que es el comportamiento con que se deben imprimir los
asteriscos en pantalla y, por ende, teniendo en cuenta la
forma de de nir la estructura for, iteracion sería la variable
que iterara de forma decreciente en el rango determinado
ayudando en el proceso de impresión.

Continuando con la característica de cómo se de ne una


estructura for que va en decremento, nótese que se usa un
función llamada downto() a partir del límite almacenado en
niveles. Primero, esta notación se debe a la característica
principal de Ruby, por ser un lenguaje completamente
orientado a objetos este será un tema de discusión en
capítulos posteriores. Segundo, si se intenta leer y
comprender la línea que utiliza la función anterior se podrá
observar que ello indica una disminución desde niveles hasta
cero, un proceso de decremento que es justamente lo que se
desea. La forma de nida allí es una de las múltiples que
existen de hacer una estructura iterativa en decremento.

Estando en el interior de la estructura for se tienen las


siguientes operaciones:

print(" "*espacios)
numero_asteriscos = (2*iteracion)

La primera es la encargada de mostrar el número de


espacios necesarios. Recuerde que espacios se inició en
cero, esto es debido a que según la forma de la pirámide, al
desarrollar la primera la esta se hace sin ningún espacio,
además la función print() permite operaciones entre cadenas
de texto o strings y números, como lo es “ ” * espacios,
indicando que la cadena con un espacio “ ” se debe
multiplicar por el número de espacios asignado en esa
64
fi
fi
fi
fi
iteración. La segunda operación calcula el número de
asteriscos a imprimir.
Posteriormente se decide crear otra estructura for; como
ya se ha mencionado se necesita imprimir una cierta cantidad
de asteriscos por cada la hasta un límite asignado. Este
proceso es a la vez un conjunto de iteración o repetición de
tareas, por lo que, así como se utiliza una estructura de
iteración que contiene todas las operaciones para crear la
pirámide, la impresión de los asteriscos también debe de
hacer algo similar.

Luego de pasar por todo lo mencionado hasta este punto,


se debe de tener en cuenta el comportamiento de los
espacios con respecto al número de asteriscos por cada la.
Si uno disminuye el otro aumenta, siendo lo último referente
a la variable espacios. En el código se especi ca el
incremento con espacios += 1.

Por último, en ciertas líneas se implementa la impresión de


una cadena vacía en una nueva línea con el n de pasar a la
siguiente la por cada iteración.

En este punto ya se ha terminado el seguimiento y análisis


del código anterior, concluyendo un programa que arroja los
resultados esperados.

3.3.3 Usando iteraciones y condicionales

● Un programa que se ejecute “in nitamente” lanzando


operaciones matemáticas sencillas que el usuario pueda
responder. Si este se equivoca en la respuesta el programa
se debe seguir ejecutando, todo esto hasta recibir una
respuesta correcta.

Las operaciones serán suma, resta, multiplicación,


división, todas entre números aleatorios.

65
fi
fi
fi
fi
fi
fi
Código solución

El código anterior utiliza una iteración “in nita” (while true)


para poder simular el bombardeo constante de operaciones
al usuario hasta el punto en donde se acierte con el resultado
de alguna de ellas. El programa utiliza una lista (una
estructura de datos que se profundizará más adelante) que
almacena los caracteres de los operadores aritméticos
elementales (suma, resta, multiplicación, división) así como
las funciones rand() que retornan valores aleatorios
66
fi
referentes al tipo de operación a utilizar y los dos dígitos o
números que conformarán la operación aritmética. Cuando
se computa la operación y se proporciona al usuario, el
computador recibe la respuesta y la almacena en respuesta,
variable que posteriormente será comparada con el resultado
esperado de la operación computada por medio de la
estructura case-when. Se mostrará un mensaje de
"RESPUESTA CORRECTA!!!\n" o "RESPUESTA
INCORRECTA, SIGUE INTENTANDO\n" según el valor
retornado de la comparación anterior.

Ejecutando el código anterior se obtiene lo siguiente:

Un último aspecto para detallar es que en la operación de


96 * 7 se introdujeron dos espacios vacíos a causa de
presionar la tecla Enter dos veces. En otras situaciones el
código hubiese dejado de ejecutarse puesto que en este
proceso toda entrada de información es válida (el Enter
proporciona información, por ejemplo, el número cero) más
sin embargo el código está adecuado para evitar este tipo de
situaciones. Se recomienda al lector comenzar a indagar para

67
encontrar cual es la pieza de código encargada de esta tarea
de veri cación.

3.4 Ejercicios propuestos


1. Crear un programa que reciba una serie de números
y calcule el sumatorio de todos estos.

2. Escribir una función que calcule cuántos números


pares hay comprendidos entre dos números límites (sin
incluirlos).

3. Crear un programa que calcule si un número es


primo o no.

4. Crear un programa que simule el comportamiento


de un reloj digital escribiendo en formato de horas,
minutos y segundos, iniciando desde las 00:00:00
horas hasta las 23:59:59 horas. Cuando se ejecute el
programa, deberá aparecer en pantalla el transcurso
desde las cero horas hasta las 23:59:59. Puede utilizar
varias alternativas, desde imprimir cada hora en
pantalla, como limpiar esta última cada vez que la hora
cambie.

5. Generar los primeros diez números perfectos.

Un Número perfecto es aquel que es igual a la suma de


sus divisores, excluyéndose el propio número. Por ejemplo, el
número 28 presenta 5 divisores menores y distintos de 28,
que son: 1, 2, 4, 7 y 14. Al sumarlos da como resultado 28.

6. Según la reseña histórica cuenta el inventor del


juego de ajedrez que el rey que le solicitó hacerlo le
preguntó cómo quería que este le pagase, a lo cual el
inventor le contestó que ubicara un grano de trigo en el

68
fi
primer cuadro del tablero, en el segundo ubicará el
doble del primero, en el tercero el doble del segundo y
así sucesivamente hasta completar los 64 cuadrados.

Se debe crear un programa que muestre un tablero (sin


líneas) en donde se indique el número de granos de trigo
que debió colocar el rey por cada cuadro.

Ejemplo:

7. Realizar las siguientes guras para n niveles:

8. Crear el siguiente cuadrado:


69
fi
9. Crear el siguiente conjunto de guras:

10. Leer número e imprimir una pirámide de dígitos:

70
fi
11. Leer un número positivo e imprimir las sumas de
números enteros positivos consecutivos que den el
número introducido. Por ejemplo:

50 = 8 9 + 10 + II + 12
50 = II + 12 + 13 + 14

3.5 Retroalimentación del capítulo


En este capítulo se analizan estructuras que permiten
hacer operaciones más complejas, estructuras de control y
de ujo tales como la representación y manejo de
condiciones y ciclos-iteraciones.
Es entonces que para cada estructura se puede decir que:
 Para las condiciones se utiliza los distintivos if-else
que permiten representar dos posibilidades que indican si
una condición se cumple o no. Su estructura es:

If <condiciones>
<Bloque de instrucciones #1>

else
<Bloque de instrucciones #2>

Cuando se tiene que una condición evaluada puede


obtener muchos valores distintos, se recomienda utilizar la
estructura case-when que se de ne de la siguiente manera:

case <variable>
71
fl
fi
when <Valor 1 variable> {<bloque de
instrucciones #1>} when <Valor 2
variable> {<bloque de instrucciones
#2>}
.
.
.
when <Valor n variable> {<bloque de
instrucciones n>} else {<bloque de
instrucciones>}
end

 Para ejecutar una operación o un conjunto de estas un


número de veces determinado se utiliza el for:

for <variable> in [variable o tope] do

<bloque de operaciones>

End

Por otra parte, cuando la ejecución de una operación


múltiples veces depende de una condición se recomienda
usar la estructura while:

while <condición> do
<bloque de operaciones>
End

72
4.
FUNCIONES

En la programación es muy escuchado el término


“Funciones” al momento de escribir código. Hay situaciones
en las que se requiere escribir programas que realicen
múltiples tareas, cada una mediante una cierta cantidad de
pasos de forma ordenada y repetitiva. En este tipo de
situaciones se utiliza uno de los lemas más aplicables a todo
problema, “divide y vencerás”, es decir, descomponer la
situación descrita en partes mucho más pequeñas para
poder resolver el problema.

Un ejemplo del lema puede ser el siguiente:

“Dados los catetos de un triángulo rectángulo se debe


crear un programa capaz de calcular su hipotenusa”

Nota: Siempre, antes de iniciar a resolver un problema se


debe identi car la información solicitada.

El ejercicio planteado consiste en crear un programa


capaz de calcular la hipotenusa de un triángulo rectángulo
conociendo los catetos de este. Si se empieza por dividir el
problema en sus partes más pequeñas se tendría lo
siguiente:

● Recibir y almacenar cada uno de los datos que


menciona el ejercicio.

73
fi
● Encontrar alguna expresión que relacione la
información (catetos) con el resultado solicitado.
● Proponer los pasos que se deberán de seguir para
encontrar el resultado solicitado.
● Pruebas de escritorio, es decir, probar los pasos
generados en el punto anterior antes de pasar a su
implementación en código.
● Implementación del punto anterior.
o Resolver la expresión encontrada para el cálculo del
valor solicitado en cada una de las partes que la componen.
● Pruebas.
● Dar solución con la implementación en código del
ejercicio.

Cada una de esas pequeñas partes o divisiones


mencionadas se conocen como funciónes (o subrutinas).
Estas son las encargadas de realizar una acción especí ca
dentro de un conjunto de acciones más amplio. Se
componen de órdenes o instrucciones las cuales indican el
proceso que se ejecuta cada vez que estas subrutinas son
utilizadas. Son una parte fundamental de la programación ya
que permiten una codi cación modularizada (es decir, por
módulos o partes destinadas a una sola tarea), mantener una
e ciencia tiempo-espacio, calidad de estética, entre muchos
otros componentes.

4.1 Estructura
La creación de funciones en Ruby sigue la siguiente
estructura:

def name (<lista de parámetros>)

*Cuerpo de instrucciones

end
74
fi
fi
fi
La expresión name indica el nombre de la función, (<lista
de parámetros>) es el apartado donde se podrán determinar
variables externas que se pueden necesitar. Lo anterior es
una característica fundamental para las funciones. En algunas
situaciones se requieren cálculos con procedimientos
distintos a los que ya se encuentran ejecutados por la
función en donde se requiere dicho parámetro, por lo que
surge la necesidad de tomarlos como parámetros que
ingresan en otras funciones y no como expresiones locales
de la función.

El *cuerpo de instrucciones almacena todas las


operaciones de las cuales se encargará la función. Es de
buenas prácticas relacionar el nombre de la función (name)
con el procedimiento u objetivo a lograr, por ejemplo, si se
desea hacer una función que calcule la media de un conjunto
de datos suministrado, lo más ideal es que la función se
llame con algún nombre o palabra relacionable a la
operación, ya sea usando media, promedio, etc.

Nota: Para las buenas prácticas en la escritura de código


en general se recomienda leer el libro de Clean Code por
Robert C. Martin.

La palabra end indica el lugar donde termina la de nición


de la función. Si se escribe código después de este
indicativo, esa parte del código no se ejecutará cuando la
función sea utilizada.

4.2 Funcionamiento
Como ya se indicó en la sección anterior del capítulo, las
funciones ayudan a determinar tareas generales a partir de
pequeñas partes o divisiones, contribuyendo todas a un
objetivo general. Por tal motivo el uso de las funciones tiene

75
fi
ciertas particularidades en la programación; términos y
expresiones como “llamado de una función”, “paso de
valores”, entre otras, serán algunas de las cuales se aclaran a
continuación.

4.2.1 Declaración y uso de funciones

El término “declaración” hace referencia al proceso de


crear una función, es decir, usar la estructura general de una
función de nida anteriormente. Para ello se utilizará como
ejemplo el siguiente código:

Lo anterior es la declaración (creación) de la función sort().


Esta es la encargada de ordenar ascendentemente un
conjunto de valores numéricos enteros dados.

Ahora bien, está la de nición de la función y fuera de esta


se indica su nombre con una característica particular, y es
que cada nombramiento contiene un conjunto de datos que
precisamente será el evaluado por la función. Toda esta
situación se conoce como llamado de una función y el paso
de sus argumentos. Es de esta manera en cómo las
funciones declaradas se ejecutan.
76
fi
fi
Como se observa en el ejemplo, si se ejecuta el código el
resultado esperado sería el arrojado por la función. Sin
embargo, de no ser llamada o nombrada en algún lugar no se
retornará ningún valor proveniente de esta.

Con todo ello, se podría re-de nir la estructura de las


funciones de la siguiente manera:

def name (<lista de parámetros>)


*Cuerpo de instrucciones
end

name(<lista de argumentos>) #Aquí se llama-utiliza la


función

Por otra parte, la nueva estructura de ne el conjunto de


elementos con los que trabajará la función como <lista de
argumentos>, sin embargo, nótese que en la de nición de la
función aparece un nombre distinto. Cada uno indica
situaciones con características completamente distintas,
como se muestra a continuación:

• Argumento: Son los valores con los cuales se


desarrollan las instrucciones almacenadas en la función.
Estos valores son asignados en el momento de llamar a una
función. Por ejemplo el conjunto de elementos [6,5,4,3,2,1]
asignado al llamado de sort() en la línea 16.

• Parámetro: Indican el tipo de datos que se pueden


usar en el algoritmo que desarrolla la función en su cuerpo
de instrucciones. Se construyen al momento de crear la
función; almacenan valores cuando esta última es “llamada” y
asignada con los argumentos necesarios.
Hay que tener en presente el hecho de que no siempre se
tendrán que usar argumentos y parámetros en las funciones,
77
fi
fi
fi
todo depende de si estas necesitan obtener valores
externos; en un caso donde no se utilicen, sólo será
necesario el llamado.

Nota: A partir de la facilidad proporcionada por la sintaxis


de Ruby en los casos en donde no se utilicen parámetros, y
por ende argumentos, el llamado a la función podrá hacerse
con o sin paréntesis, más sin embargo es recomendable
utilizarlos ya que facilitan la comprensión del código.

Ejecutando la función sort()

Al pasarle los argumentos:

 [6,5,4,3,2,1]
 [456,643,887,362,7378,589957,4,34557684,672,1]

el resultado esperado sería:

 [1, 2, 3, 4, 5, 6] para el primer argumento

 [1, 4, 362, 456, 643, 672, 887, 7378, 589957,


34557684] para el segundo argumento

Por ende, la ejecución retorna lo siguiente:

Ejecutando la función sort()

Al pasarle los argumentos:

 [6,5,4,3,2,1]
 [456,643,887,362,7378,589957,4,34557684,672,1]

el resultado esperado sería:

 [1, 2, 3, 4, 5, 6] para el primer argumento


78
 [1, 4, 362, 456, 643, 672, 887, 7378, 589957,
34557684] para el segundo argumento

Por ende, la ejecución retorna lo siguiente:

Detallando las instrucciones ejecutadas por sort() se tiene


que a nivel general se está ejecutando un algoritmo bastante
conocido en el campo de la programación, este es el bubble
sort u ordenamiento de burbuja. Se basa en comparar
repetidamente un valor contra el resto de los elementos en
un conjunto de datos y determinar en cada comparación que
condición de ordenamiento cumple (mayor o menor) para de
esta manera poder reposicionarlos.

Para una mayor comprensión, una representación del


funcionamiento de sort() es la siguiente:

79
Para un arreglo de tres elementos [3,2,1]

Ilustración 5. Representación del proceso que desarrolla la función sort().

En código se utilizan dos estructuras de iteración for que


servirán para comparar cada elemento con el resto de los
elementos dentro del conjunto. Por otra parte, en el for más
interno es donde se realiza la comparación e intercambio
entre los dos números analizados. Allí se utiliza el siguiente
proceso de intercambio:

● La variable auxiliar almacena el valor que cumple la


condición analizada, que para el caso del código sería
set_of_items[i], en donde i indica la posición en la que se
encuentra dicho valor dentro de set_of_items.
80
● El ordenamiento es dado en las expresiones
set_of_items[i] = set_of_items[j] y set_of_items[j] = auxiliar,
las indican que al acceder al valor almacenado en
set_of_items dado por las posiciones i o j, dicho valor debe
ser cambiado por el valor que hay en la posición j dentro del
conjunto de datos o el valor almacenado en auxiliar, todo
depende según la operación observada.

Nota: Se deja como deber del lector analizar la razón por


la cual los límites hasta los cuales deberán de iterar cada
estructura for son distintos.

4.2.2 Retorno de valores

Las funciones dentro de su propia de nición pueden


devolver información al lugar en donde fueron llamadas y
posteriormente visualizarla.

Para comprender mejor se tiene el siguiente ejemplo:

def sort(set_of_items)

end

sort([6,5,4,3,2,1])

El código anterior presenta la versión de la función sort


descrito en la sección anterior y que muestra el resultado
desde la función:

def sort(set_of_items)

return set_of_items
end

81
fi
answer = sort([6,5,4,3,2,1])
print( answer)

Esta nueva versión retorna el resultado de la función hacía


el lugar donde fue llamada para luego almacenarlo en una
variable llamada answer.

Al ejecutar los dos códigos se podrá esperar que en


pantalla se muestre un resultado arbitrario, más el tema al
cual se debe poner mayor atención es al hecho que una de
las dos versiones utiliza el retorno de información, mientras
que otra solo imprime el cálculo por sí misma.

Return

Gracias a la palabra de nida por el lenguaje de Ruby,


return, es posible el paso de valores como nueva información
para otras variables. Esto permite crear programas más
robustos en cuanto a las operaciones que este ejecute.

Para el manejo del return se debe de tener claridad en la


información que se desea retornar como la forma en que
desea guardarla luego de hacer el llamado a la función. Otras
características que pueden colaborar en el uso del return
son:

● Luego de escribir código justo después de una línea


que contenga return, este nuevo código no será ejecutado.
Esto es dado gracias a que return se indica como un
elemento nalizador de una función, por ende, se entiende
que cuando se llega a ejecutar es porque la función ha
terminado con todos sus cálculos.
● Return permite devolver datos tanto en cantidad como
en naturaleza, es decir, que con ello se pueden retornar uno
o más valores de una misma y distinta naturaleza (enteros,

82
fi
fi
otantes, estructuras, etc.). Este proceso se realiza de la
siguiente manera:

def name (…)



Return información1, información 2, ….,
información k
end

Es en el caso del uso del return que se hace pertinente


que el lector comprenda el tema de Variables y su
clasi cación (ver capítulo 2, apartado 2.3), relacionando el
tema de expresiones locales y globales.

4.3 Ejercicios propuestos


De modo general, con los capítulos desarrollados hasta
este punto se puede proponer que el lector, con el objetivo
de a anzar los conceptos indicados aplique las estructuras
de funciones a los ejercicios propuestos de cada capítulo
anterior.

4.4 Retroalimentación del capitulo


Este capítulo trató uno de los temas centrales dentro de la
programación, las funciones y su manipulación. De ello se
puede destacar que:
 Una función es aquella encargada de realizar una
acción especí ca dentro de un conjunto de acciones más
amplio.

 Ayuda a solucionar un problema dividiéndolo en


partes más pequeñas.

 En Ruby, una función se de ne de la siguiente manera:


def name (<lista de parámetros>)
83
fl
fi
fi
fi
fi
*Cuerpo de instrucciones
End

 Para usar una función, solo basta que con después de


haberla de nido esta sea “llamada” en la zona del código
donde se necesite. Esto se logra escribiendo el nombre de la
función acompañada con los argumentos entre paréntesis.

 Muchas veces se escuchará el uso tanto de


argumentos como parámetros, la diferencia de cada uno es
la zona en donde se esté analizando, es decir, argumentos
hace referencia a la información pasada en el llamado de una
función, mientras que parámetros son aquello datos que
recibe la función y que se indican en el momento de su
de nición.

 Las funciones pueden o no retornar valores, esto lo


decide el programador según lo necesite.

84
fi
fi
5
MANEJO DE TIPOS DE DATOS
COMPUESTOS (ESTRUCTURAS DE
DATOS)
5.1 Conceptos sobre las estructuras de
datos
Una estructura de datos es la herramienta que permite
unir, organizar y manipular datos de uno o distintos tipos con
el objetivo de proveer un uso más e ciente y comprensible
de la información.

Su uso también es atribuido al manejo de grandes


volúmenes de información, lo que ayuda en procesos del
desarrollo del software como el internet, bases de datos,
cálculos de gran magnitud; incluso la implementación de
algoritmos, permitiendo estandarizar la solución de un
problema en un conjunto de pasos.

5.1.1 Clasi cación

La variedad de estructuras se clasi ca con base en el


tamaño y procesamiento. De lo anterior, se tienen lo
siguiente:

85
fi
fi
fi
5.1.1.1 Tamaño

● Estático: Estructura de datos que mantiene un tamaño


de almacenamiento jo en ejecución, es decir, solo permite
una cantidad de datos sin posibilidad de nuevos ingresos. Al
operar con estructura de este tipo no se podrá cambiar el
valor de su tamaño a menos que se utilice una nueva
estructura y se re-posicionen los datos almacenados en la
estructura inicial.

Ejemplos: arreglos.

● Dinámico: Estructura de datos que permite cambiar su


tamaño de almacenamiento en ejecución. Al operar con
estas estructuras, en medio de la ejecución pueden
almacenar o eliminar información con gurando a la vez el
tamaño que las describe.

Ejemplos: Listas, pilas, colas, árboles, grafos.

5.1.1.2 Procesamiento

● Lineal: Los elementos dentro de estas estructuras se


organizan uno tras otro.

Ejemplos: arreglos, listas, pilas, colas.

● No lineal: En comparación con el caso anterior, los


datos en estructuras no lineales se organizan de forma
aleatoria.

Ejemplos: Árboles, grafos.

Nota: Algunas estructuras de datos teóricamente sólo


manejan datos de una sola naturaleza, sin embargo, algunas

86
fi
fi
herramientas permiten implementar estructuras manejando
múltiples tipos de datos.

5.2 Arreglos
Los arrays (en español como arreglos) son estructuras de
datos que utilizan la memoria del computador para
almacenar información del mismo tipo.

Ilustración 6. Representación grá ca de un arreglo

Se observa en la imagen anterior un conjunto de cuadros


almacenados dentro de uno mucho más grande. Son cinco
en total más la que contiene a todas. Esta representación
indica de forma muy aproximada la abstracción que se hace
de un arreglo a la estructura de un computador. El recuadro
azul es una pequeña parte de la memoria de un computador
mientras que los cuadros contenidos allí son pedazos de
dicha parte de memoria tomada que almacenan información
que en este caso son números enteros. Observe que cada
elemento es contiguo con el siguiente, además de que se
describen por un tamaño general (que sería el conteo de
87
fi
todos los cuadros contenidos) más un índice que de ne su
posición con respecto a los otros. Con ellos se puede
manipular un arreglo y la información que este almacene.

Son creados en la memoria de un computador de forma


consecutiva (un solo bloque de memoria subdividido en más
bloques), lo que genera que cada dato se almacene de forma
secuencial.
De esta manera se puede manipular un arreglo y la
información que este almacene.

Nota: Los arreglos pueden entenderse como variables


que almacenan valores de un mismo tipo.

5.2.1 Características de los arreglos

A comparación de otras estructuras, los arreglos disponen


de las siguientes particularidades:

● Son estructuras que almacenan información, por ende,


necesitan de un tamaño especí co.
● Al usar la memoria de un computador, este tipo de
estructuras indican la cantidad necesaria a usar para
almacenar la información, es por ello por lo que se catalogan
como estructuras estáticas.

Nota: Cabe resaltar que, para esta última descripción, el


tamaño no es re-asignable para un mismo arreglo.

5.2.2 Arreglos en más de una dimensión

Pueden describirse como arreglos que contienen


arreglos, en donde cada arreglo contenido sería una
“dimensión” según el nombre dado. Una posible
representación sería:

88
fi
fi
Ilustración 7. Arreglo multidimensional
Fuente: MathWorks

Para mayor comprensión del término “dimensión”, se


puede pensar en los ejes del plano cartesiano, y que cada
eje sería representado por un arreglo, y se sabe que
teniendo un plano cartesiano en tres dimensiones (x,y,z) se
tendría tres vectores que lo componen. Por otra parte, la
ilustración 7 determina el concepto de dimensión con los
aspectos de las (row), columnas (columns) y página (page)
para un vector en tres dimensiones.

Los arreglos multidimensionales pueden poseer un


número cualquiera de arreglos representados. Comúnmente
se manejan máximo hasta dos dimensiones (o arreglos
bidimensionales).

Los arreglos bidimensionales son también conocidos


como matrices, es decir, vectores que mantienen una
relación similar a la de una tabla de la ilustración 8 donde se
observan las las y columnas.

89
fi
fi
Ilustración 8. Arreglos bidimensionales

Nota: El concepto de matriz también puede expandirse a más de dos


dimensiones, como matrices tridimensionales, …, n-dimensionales, entre otras.

5.2.3 Creación e inicialización de arreglos

La expresión Arreglo o Array es tratada en el lenguaje de


Ruby como una clase, concepto que se podrá detallar en los
capítulos siguientes.

<nombre del arreglo> = Array.new(<tamaño del arreglo>)

Array.new es la clase (representación que de ne a un


arreglo) ejecutando una función que permite crear a nivel de
memoria la estructura que se conoce como el arreglo. Un
ejemplo podría ser el siguiente:

Como resultado se obtiene:

El arreglo creado a contiene 5 espacios disponibles en


memoria, más sin embargo dichos espacios no han sido

90
fi
inicializados, por lo que se muestra por defecto el valor de
nil.

Otra manera de crear un vector sin inicializar es mediante


el uso del símbolo []:

El resultado es:

La razón del resultado arrojado es que no hay elementos


dentro de a, es decir, no hay ninguna expresión dentro de [].

Ahora bien, el proceso de inicialización, es decir de


asignación de valores a las posiciones mencionadas se
puede hacer de las siguientes maneras:

Forma #1

Forma #2

La forma #1 especi ca los datos y a partir del número total


de estos se determina el tamaño del arreglo, que en este
caso es la variable a. Por otra parte, la forma #2 permite
tener un proceso más automático en donde por medio de
ciclos se pasara por cada posición del arreglo de nido, esto
es, durante 6 ciclos se llenarán los 6 espacios de memoria
91
fi
fi
de nidos en a, con un valor correspondiente a la variable
number, que es equivalente a uno de los ciclos dentro del
rango de 0..5. Aquí también se pone como opción la
de nición de un vector bien sea con corchetes o mediante el
uso de la clase Array, cualquiera de las dos formas dará el
mismo resultado:

Existe una característica particular con el código y es que


puede surgir la pregunta “¿qué pasaría si el tamaño del
vector y de la estructura de ciclos (número de iteraciones) es
diferente?”. Una respuesta rápida es “nada, este se acoplará
al tamaño”. La respuesta detallada será vista en el análisis de
la siguiente estructura de datos, las listas.

5.2.4 Arreglos bidimensionales

El lenguaje de Ruby adapta el concepto de una matriz o


arreglo bidimensional de “arreglos que contienen arreglos”
de la siguiente manera:

Como resultado podemos obtener lo siguiente:

92
fi
fi
Observe que directamente se aplicó el concepto dado
para arreglos bidimensionales, y para mantener una
comprensión más amplía de la estructura y poderla ver como
una tabla se decidió por usar variables de row y col.

5.3 Listas
Las listas al igual que los arreglos son un conjunto de
elementos contiguos que bien pueden ser de igual o
distintos tipos. Es una estructura muy exible dentro de la
programación ya que siempre permite modi car su tamaño
en tiempo de ejecución.

Una lista se puede representar de la siguiente manera:

Ilustración 9. Representación grá ca de una lista

93
fi
fl
fi
5.3.1 Listas desde un enfoque de la memoria de un
computador

Gracias a la facilidad de manejo que provee la sintaxis de


Ruby las listas pueden ser comprendidas de una manera
particularmente sencilla, sin embargo, en lenguajes
enfocados al bajo nivel, es decir, la comunicación directa con
el computador usando un lenguaje estricto y funcionalidades
más complejas, las listas son estructuras manejadas por la
manipulación de un apartado de la memoria de un
computador. Este concepto de listas trae consigo nueva
terminología como el concepto de punteros, direcciones de
memoria, nodos, entre otras las cuales no serán estudiadas
en este documento, sin embargo, es pertinente que sean
mencionadas.

Las listas desde un enfoque de la memoria de un


computador son bloques que contienen elementos (también
llamados nodos) y que se comunican entre sí mediante
direcciones las cuales son gestionadas por variables o
punteros. Es entonces a partir de este concepto que existe
una clasi cación de las listas enlazadas según sea la
comunicación entre sus elementos, que son la siguientes:

● Listas doblemente enlazadas: Estructuras en donde


cada bloque de elementos o nodos almacena un puntero con
la dirección del elemento anterior y el elemento siguiente, es
decir, cada elemento posee dos direcciones de memoria.
● Lista circular: Estructuras que comunican su último
elemento con el primero.
● Listas circulares doblemente enlazadas: Una
combinación de los dos tipos de listas anteriores. Consiste en
una estructura en que cada elemento contiene la dirección
del siguiente y anterior elemento, con la adición de que el
último elemento está conectado con el primero.

94
fi
5.4 Ejemplos del uso de Listas y arreglos
Ruby internamente no di ere entre estos, en su lugar
pre ere tratar ambas bajo una misma de nición adoptando
características particulares de cada estructura, como lo es la
adaptación del tamaño proveniente las listas, y la
organización en memoria por parte de los arreglos.

Teniendo en cuenta las condiciones de uso de las listas y


arreglos, de ahora en adelante cuando se mencione trabajar
con alguna de ellas se sabrá que desde Ruby se utiliza una
estructura general para las dos.

Algunos ejemplos propuestos son los siguientes:

1) Generar un programa que permita revertir los


elementos de una lista con:

a. El uso de un vector auxiliar.


b. Sin el uso de un vector auxiliar.

Nota: Gracias a los capítulos anteriores el lector podrá


estar en capacidad de comprender el código, y es desde
este punto en donde ya comienza a jugar un papel
importante el desarrollo de la lógica de programación. Por
ende, los ejercicios desarrollados no serán explicados paso
por paso, en su lugar se propone al lector intentar deducir la
lógica detrás para obtener los resultados obtenidos.

95
fi
fi
fi
Desarrollo
Para el caso a se tiene el siguiente código

o también:

Ambos dando como resultado:

Para el caso b se tiene:

96
Nótese que se puede aplicar para cualquier tamaño de
lista, par o impar, por lo que para el caso de la lista
presentada en el código se tendría el siguiente resultado:

2) Generar un programa que permita codi car un


mensaje de longitud cualquiera mediante los cifrados del
Cesar.

Desarrollo

El cifrado de Cesar (nombrado así por su creador Julio


Cesar, quien lo usaba para comunicarse con sus generales
de manera secreta) podría realizarse de la siguiente manera:

97
fi
Al ejecutar el código anterior tenemos lo siguiente:

3) Desarrollar un algoritmo que permita crear un


cuadrado mágico (donde la suma total de la diagonal
principal, secundaria, las o columnas es igual a 15).

Desarrollo

El código del ejercicio es el siguiente:

98
fi
99
100
Al ejecutar el código anterior tenemos lo siguiente:

5.5 Pilas y colas


5.5.1 Pilas

Llamada en inglés Stack, son una estructura de datos que


almacena y recupera información a través del sistema de
acceso LIFO (Last In First Out), donde el último elemento en
entrar es el primero en salir.

101
Las pilas manejan elementos de un solo tipo. Estos son
manipulados a través de los extremos de un conjunto total de
elementos, es decir, el primer y último elemento. De lo último,
se conocerá como TOS (Top of stack o tope de pila) al último
elemento, y al primero se le llamará FOS (Front of stack o
Frente de la pila).

Lo anterior se puede representar con la siguiente imagen:

Ilustración 10. Representación de una pila

5.5.2 Colas

Estructuras de datos que almacenan y recuperan


información a través del sistema de acceso FIFO (First In First
Out), es decir, los datos son insertados en memoria a través
de un extremo (llamado nal) y eliminados a través de otro
extremo llamado frente.

Esto se puede entender mejor con casos de la vida


cotidiana como las las en los supermercados o la impresión
de documentos; los dos centran sus procesos en el momento
102
fi
fi
en que va llegando la información, la persona o documento
que se posicione de primero, será de la misma manera lo
primero en salir.

Ilustración 11. Representación de una cola

5.6 Hashes

Nota: Este apartado de Hashes-tablas hash solo será


mencionado y no será temario de este libro.

Los Hashes o tablas hash son estructuras de datos que


crean relaciones entre ciertos elementos (denominados
llaves) con otros (denominados valores), mediante una
función conocida como función hash y un arreglo de tamaño
arbitrario. Puede también de nirse partiendo del
funcionamiento de un diccionario en donde se pueden tener
un cierto conjunto de palabras-elementos que traen consigo
una de nición-asignación.

A manera grá ca, la representación de una tabla hash es


la siguiente:

103
fi
fi
fi
Ilustración 12. Funcionamiento de datos en las tablas hash

La ilustración 12 intenta mostrar el proceso de rápido


indexado para cada una de las llaves proporcionadas y que
las cuales son relacionadas con un valor luego de ser pasada
por la función hash. De manera general, esta función
(conocida también como función picadillo) permite convertir
la información que se almacena en un formato más fácil de
manipular, para que luego el resto de la estructura se
encargue de determinar las posibilidades de lugares en
donde dicha información podrá ser almacenada (índices)
junto a sus valores asociados (pares).

Se dice entonces que estas estructuras se destacan por


su bajo costo computacional, la posibilidad de comprimir la
información y la rápida indexación de elementos, por lo que
suelen ser elementos muy usados en la programación.

5.7 Operaciones fundamentales entre


estructuras

De modo general todas las estructuras manejan


operaciones similares entre sí. Al tratar con información
ciertas acciones son de común uso, tales como:

104
Tabla 10. Operaciones fundamentales entre estructuras

Nota: Algunas de estas operaciones dependen del tipo de


estructura, especialmente en pilas y colas.

Puede ser el caso que en muchos otros lenguajes de


programación estas operaciones se nombren de manera
distinta, e incluso usados como generalidad en otras
estructuras, pero siempre mantendrán el objetivo de cada
una de ellas.

5.7.1 Operaciones fundamentales sobre pilas y colas

Las operaciones fundamentales sobre estructuras deben


de adaptarse al funcionamiento que describe cada
estructura, por ejemplo, las pilas funcionan (describiéndolas
en la vida real) con cada elemento situando uno encima del
otro, y para poder acceder a alguno de ellos basta con
eliminar elementos que hayan encima de este hasta llegar al
solicitado. Las colas por su parte necesitan que cada
elemento llegue al principio de la cola para poder ser
manipulado.

De lo anterior, se de nen las siguientes operaciones:

105
fi
Tabla 11. Operaciones fundamentales de las pilas

De manera grá ca, estas operaciones Push() y Pop()


pueden representarse de la siguiente manera:

Ilustración 12. Representación de las operaciones Push() y Pop()

En la ilustración 12 se presentan las operaciones Push() y


Pop(). Las echas continuas representan Push() (insertar) y las
106
fl
fi
echas punteadas representan Pop() (eliminar). Nótese que la
manipulación de cada elemento perteneciente a la pila se
realiza siempre por un mismo extremo.

5.8 Retroalimentación del capitulo


En este capítulo se analizaron las estructuras de datos
básicas en el campo de la programación. Estas ayudan a
poder organizar y manipular de manera e ciente la
información generada por alguna operación, interacción con
el usuario u otras aplicaciones.
Las estructuras vistas fueron:

 Arreglos
 Listas
 Pilas y colas
 Hashes
Todas estas comparten ciertas características, algunas en
los métodos que usan para manipular información como la
inserción y borrado de datos, algunos permiten búsqueda de
elementos mediante índices como sucede con los arreglos y
las listas, otros mediante la indicación de una llave como los
hashes. Por otra parte, cada uno se diferencia entre sí
teniendo en cuenta las siguientes características:
 Los arreglos permiten almacenar un conjunto de
elementos del mismo tipo en un espacio de memoria
estático, esto quiere decir que debe ser especi cado al
momento de de nirlo.

 Las listas almacenan datos de cualquier tipo y su


tamaño es dinámico, es decir que siempre se adapta al
número de elementos.

 Las pilas y colas almacenan datos de un mismo tipo y


se diferencian en el orden de entrada y salida de los
elementos, siendo la pila manejada por un modelo LIFO (Last
107
fl
fi
fi
fi
In First Out, ultimo en entrar primero en salir en inglés)
mientras que la cola es manejada por un modelo FIFO (First
In First Out, primero en entrar primero en salir en inglés).
Pude asimilarse como si se estuviese hablando de una pila
de libros o de una cola de supermercado respectivamente.

 Los hashes o tablas hash funcionan almacenando


datos de cualquier tipo y accediendo a estos como si tratase
de un diccionario en donde para obtener el resultado es
necesario de conocer una llave o identi cador del resultado.

Cabe aclarar que algunas de estas estructuras son


uni cadas en Ruby, como ocurre con los arreglos, pilas y
colas siendo representadas mediante una lista. A pesar de
esto es necesario conocer cada tipo de estructura y su
debida caracterización para que en algún momento de pasar
a un lenguaje más detallista se puedan asimilar fácilmente.

108
fi
fi
6
LISTA DE ALGORITMOS MÁS
UTILIZADOS EN PROGRAMACIÓN

6.1 ¿Qué son los algoritmos?

En las matemáticas, ciencias de la computación, lógica y


relacionadas, un algoritmo se conoce como un conjunto de
instrucciones no-ambiguas, ordenadas y nitas que permiten
la solución de un problema o la realización de cierta
actividad. Componen la base que conlleva el proceso de
programar, codi car una aplicación, implementación, entre
otros.

También son fundamentales en la vida cotidiana para el


desarrollo de ciertas habilidades como el razonamiento
matemático con las tablas de multiplicar, o incluso el álgebra
con las operaciones matriciales. También se incluye en
actividades particulares como atarse los zapatos o la manera
de realizar un ejercicio.

109
fi
fi
6.2 Características y clasi cación de los
algoritmos
Características

Algunas propiedades que caracterizan a un proceso como


algoritmo son (4):

● Es preciso y ordenado, avanzando a secuencialmente


paso a paso.
● Es de nido, independiente del número de veces que
se realice, el resultado siempre tiene que ser el mismo.
● Es nito.
● Limita el ingreso de datos entre cada secuencia que
realiza.

Clasi cación

Los algoritmos se dividen de la siguiente forma:

● Según su sistema de signos, es decir, el uso de


caracteres verbales, matemáticos y computacionales.

● Cualitativos: Instrucciones que se describen usando


palabras. Las recetas o instrucciones serían un ejemplo de
ello.
● Cuantitativos: Se usan números y operaciones
matemáticas para encontrar un resultado o con gurar un
procedimiento. Por ejemplo, las operaciones en el cálculo
matemático.
● Computacionales: Instrucciones de alta complejidad
ejecutadas por una computadora.
● No computacionales: Algoritmos manuales, no
necesitan ayudas computacionales, ya sea enfocados al
ámbito de optimización, dinamismo, incremento, búsqueda,
110
fi
fi
fi
fi
fi
entre otros. Suelen ser muy usados por ambientes
empresariales y comerciales.
● Marcaje: Estudia al cliente, evalúa sus actitudes frente
a distintas circunstancias (precios de lanzamiento, rebajas,
aumentos, etc.) y crea un sistema que genera las posibles
opciones de venta que más se acomoden.
● Programación dinámica: Resuelve situaciones en las
que se involucran problemas de maximizar o minimizar un
valor a partir de un conjunto de restricciones.
● Vuelta atrás: Conocido también como backtracking, se
encarga de analizar toda la ruta de comportamiento de una
situación particular y encontrar aquel que cumpla con ciertas
condiciones. Por ejemplo, en las operaciones de mercado,
los precios, el trá co de personas en la red, entre otras, se
analiza el impacto de estos respecto del tiempo para así
determinar si dado su comportamiento en un caso anterior
repercute en un aumento o bajada de la cantidad actual.
● Ordenamiento: Organizan una secuencia de datos
numéricos en un orden especí co.
● Búsqueda: Encuentra un dato particular en un conjunto
de información.
● Según la estrategia usada para el alcance de un
resultado son:

● Probabilístico: Estima la probabilidad cierta o incierta


de algún proceso o situación particular.
● Cotidianos: Describen las actividades usuales de una
persona.
● Escalada: A través de varios resultados, mediante
ensayo y error va progresando hasta encontrar un resultado
satisfactorio.

111
fi
fi
6.3 Pseudocódigo
Utilizado para la descripción general de los algoritmos, es
un lenguaje de alto nivel, es decir que permite expresar ideas
en un lenguaje que sea altamente comprensible para el
humano más que para la computadora; este permite usar
conceptualmente estructuras vistas en la codi cación como
las iteraciones, condicionales, funciones, entre otras.

Un ejemplo de ello podría ser la forma en cómo se puede


veri car que un elemento dentro de un vector es primo, si
cumple se muestra en pantalla, caso contrario su valor
cambia a cero y se continúa recorriendo el vector:

función esPrimo(númeroIngresado):
númeroDeDivisores = 0
por cada númeroEnAumento hasta el rango del
intervalo [1,númeroIngresado +1] hacer
si el númeroIngresado módulo
númeroEnAumento es igual a 0 entonces
sume uno a númeroDeDivisores

si el númeroDeDivisores es distinto de 2 y
el númeroIngresado es distinto de 1 entonces
retorne Falso
de lo contrario
retorne Verdadero

función main():
números = [datos aleatorios en un rango de 0
a 100]

por cada número en números hacer


si no esPrimo(número):
índice = índice de número en números
números[índice] = 0
112
fi
fi
por cada índice hasta el rango de la longitud de
números hacer
sí números[índice] es distinto de cero
entonces
mostrar en pantalla números[índice]

Lo anterior puede leerse de manera directa, permitiendo


comunicar instrucciones claras de que hacer para lograr el
objetivo propuesto, por ende, al intentar pasar lo anterior a
código tendríamos lo siguiente:

113
Como resultado de su ejecución se puede obtener:

6.4 Algoritmos populares


Actualmente aquellos relacionados con el mundo de la
programación, en algún momento de su aprendizaje
escucharán nombre como bubble sort, binary search, merge
sort, DFS y BFS, entre muchos otros. Estos y más son algunos
de los algoritmos más utilizados por la industria tecnológica
en el desarrollo de software.

6.4.1 Algoritmos de búsqueda (search)

6.4.1.1 Concepto

Partiendo del concepto de algoritmo, esta clasi cación se


de ne como instrucciones que buscan un elemento que
cuenta con ciertas propiedades dentro de un conjunto de
elementos. Pueden verse casos como buscar un número
dentro de un arreglo, como el tener que encontrar la ruta más
apta en un conjunto de caminos o incluso calcular el
siguiente mejor movimiento en una partida de ajedrez.

Los algoritmos de búsqueda se dividen según dos


características:

● El ambiente proporciona o no información (informado


vs no informado). En este caso solo se mencionan algunas de
las categorías de no informados.
● Si se tienen secuencias de elementos.

114
fi
fi
6.4.1.2 Algoritmos de búsqueda no informados

El término de “no informados” se usa actualmente gracias


a su uso en la inteligencia arti cial, más sin embargo
mantiene su característica de que el ambiente o contexto
provea de información como por ejemplo sucede con los
árboles binarios (estructuras de datos). Algunos de los
algoritmos no informados más utilizados son:

● Algoritmos heurísticos: conocidos también como


algoritmos aproximados, son un conjunto de instrucciones
que calculan una respuesta a un problema dado más no se
asegura que la mejor respuesta pueda ser calculada, esto
suponiendo que los formas “normales y usuales” no
funcionen. Algunos de ellos son:

o Greedy algorithm: Aplicado normalmente a problemas


de optimización como conocer el mínimo número de
monedas que deben devolverse en un cambio de dinero.

El Greedy algorithm es el siguiente:

● Elementos a tener en cuenta:

C : conjunto de candidatos
Clen : longitud-tamaño del conjunto de
datos en C
S : solución
x : elemento perteneciente a C

S = 0
while (S != solución y Clen != 0) {
x = C[indice de elemento]
C = C – {x}
if (S {x} es factible) {
S = S∪{x}

115
fi
}
}

if (S es una solución) return S;


else return “No se encontró una
solución”;

A nivel general, el algoritmo se basa en iterar sobre un


conjunto de elementos veri cando si alguno de ellos
satisface una condición dada (S {x} es factible).

Para entender mejor su funcionamiento, observe la


siguiente implementación para la situación dada:

Dado un valor y una colección de monedas, el objetivo es


encontrar la cantidad mínima de monedas que sumen igual al
valor dado. Por ejemplo:

Monedas = {6, 3, 1, 4}
Valor : 23
Cambio : 6 6 6 4 1
[Note que el cambio [6 6 6 4 1] es el mínimo de
monedas de las cuales su suma da como resultado 23]

Valor : 13
Cambio: 6 6 1
[Note que el cambio [6 6 1 ] es el mínimo de monedas de
las cuales su suma da como resultado 13]

116
fi
Código

117
Como resultado se obtiene lo siguiente:

o A* algorithm: Plantea su aplicación en los grafos, este


permite conocer las rutas de menor coste entre un nodo
origen y un nodo destino
o Escalada simple (hill climbing algorithm): Ambos
algoritmos permiten encontrar una solución óptima de nida
dentro de un conjunto de elementos, es decir, una solución
óptima local. Es un algoritmo iterativo que inicia con una
solución arbitraria y comienza a variar incrementalmente un
único elemento de dicha solución hasta que no se obtengan
más soluciones óptimas.
● Algoritmos de búsqueda con adversario: Como su
nombre lo indica, son algoritmos dedicados a analizar juegos
entre dos personas, donde se podrá analizar cuál será la
siguiente mejor jugada. Algunos de ellos son:

o Mini-max: Es un algoritmo recursivo que minimiza la


pérdida en juegos con adversario, en este caso se analiza
cuál será la siguiente mejor jugada suponiendo que el
adversario elije la peor opción para nosotros.

Algoritmos de búsqueda secuenciales

Se usan frecuente en conjuntos de datos como arreglos


(vectores), listas, entre otras estructuras relacionadas. Se
basa en ir comparando elemento por elemento hasta
encontrar el indicado, esto sin importar si el conjunto de
datos está o no ordenado. Algunos de ellos son:

● Búsqueda secuencial ordinaria (lineal): Consta de


recorrer todo el conjunto de datos (de aquí la razón de ser
118
fi
lineal) comparando elemento por elemento con el criterio de
búsqueda. La complejidad de este algoritmo suele ser lineal
ya que depende del número de elementos existentes en el
conjunto de datos, sin embargo esta complejidad puede ser
amortizada o incluso cambiada según la posición en donde
se encuentre el criterio de búsqueda; pueden existir tres
casos, el caso más favorable que es el elemento a buscar
situado al inicio del conjunto de datos, un caso medio que es
dicho elemento situado en la zona central del conjunto, y el
peor caso que es cuando el elemento en búsqueda se
encuentra al nal del conjunto ( estos mismos escenarios
pueden ser analizados en otros tipos de algoritmos, incluso
mantener la idea de cambio de complejidad).

Su pseudocódigo es:

Vector v= [conjunto de datos de tamaño n]


NumeroABuscar = #

por cada numeroEnAumento hasta el rango


del intervalo [0,
tamaño de v] hacer
si el NumeroABuscar es igual al
número en la posición
de v[numeroEnAumento] entonces
Retornar un valor indicando
que el número a buscar
SI está en el conjunto de
datos

Si no:
Retornar un valor indicando
que el número a buscar
NO está en el conjunto de
datos

119
fi
Nota: Se deja a disposición del lector el formular el código
respectivo.

● Búsqueda binaria (binary search): Este conjunto de


reglas permite encontrar un elemento dentro de un grupo de
elementos aplicando el principio de divide y vencerás.
Este algoritmo se aplica a conjuntos de elementos
ordenados (de forma ascendente o descendente),
reduciendo el número de comparaciones a realizar y por
ende generando una complejidad de cómputo agradable en
relación tiempo-cantidad de datos.
Binary search se basa en buscar por mitades el elemento
solicitado, es decir, comparar el elemento del medio con el
solicitado y si estos dos coinciden se retorna el índice del
elemento, caso contrario si el elemento solicitado es menor o
mayor al elemento de la mitad se procede a dividir el
conjunto de elementos en dos partes y continuar ya sea por
izquierda o por derecha según corresponda con la condición
anterior respectivamente. En esta nueva fase se repiten los
pasos iniciales, continuando de esta manera hasta llegar al
momento en donde no se puedan realizar más divisiones.

Su implementación es la siguiente:

120
El resultado del código anterior es:

121
6.4.2 Algoritmos de ordenamiento

6.4.2.1 Counting sort

Este algoritmo se basa en ordenar un conjunto de


elementos, generalmente números según su frecuencia de
aparición. Por ejemplo, suponga el siguiente arreglo:

[2,2,3,3,4,1,1,1,1,3,4,5,2,2,2,3]

Se sabe que cada elemento contenido en esta lista es


contable, asignándole a cada uno un índice. Por otra parte,
hay algunos de esos elementos los cuales están repetidos.

El counting sort propone entonces ordenar este conjunto


de elementos a partir de la frecuencia de aparición de cada
uno. Este usa una nueva estructura, en este caso otro arreglo
en donde se ordenarán las frecuencias del conjunto teniendo
como criterio de indexación el valor del elemento de la
frecuencia analizada.

A continuación, se presenta la implementación en código


de este algoritmo:

122
El resultado obtenido es:

De manera general el algoritmo se compone de tres


iteraciones-loops, el primero en donde se encuentra la
frecuencia para cada elemento dentro de elements. El
segundo que permite de nir la posición en la cual deberán
de ir los elementos en el arreglo de salida, es decir, en este
paso se ordena el arreglo. El tercero y último donde se hace
el reposicionamiento de elementos.

123
fi
Es necesario mencionar también que la complejidad de la
ejecución de este algoritmo depende del intervalo de
números utilizado, es decir, a mayor rango dentro del
intervalo de los elementos, mayor podría ser el tiempo que
se toma el algoritmo para ordenar el conjunto de elementos.

6.4.2.2 Merge sort

Es un algoritmo basado en la losofía de “divide y


vencerás” que se resume en estos dos principios:

1. Divida la lista sin ordenar en n sublistas, cada una


de las cuales contiene un elemento (una lista de un elemento
se considera ordenada).
2. Fusione repetidamente sublistas para producir
nuevas sublistas ordenadas hasta que solo quede una
sublista. Esta será la lista ordenada.

Para comprender mejor el funcionamiento general,


observe la siguiente imagen:

Ilustración 13. Funcionamiento de merge sort

124
fi
Como se observa en la imagen, partiendo de un conjunto
de elementos s, se comienza a subdividir este último hasta
obtener partes compuestas por un solo elemento (las cuales
se consideran que ya están ordenadas). Posteriormente se
inicia la comparación entre parejas hasta lograr reagrupar el
conjunto, pero esta vez estando ordenado. Se debe resaltar
que el paso nal en donde se resultan dos mitades ya
ordenadas en sí mismas, estas podrán ser comparadas de
varias maneras, una de ellas es elemento por elemento.

Actualmente hay varias implementaciones de este


algoritmo, una de ellas presentadas aquí es la siguiente:

Ejecutando el código se obtiene lo siguiente:

125
fi
Ejecutando el código se obtiene lo siguiente:

126
6.4.2.3 Quick sort

El quick sort trabaja de cierta manera similar al merge sort,


con la diferencia de que toda su acción principal ocurre en la
división de elementos y no en la combinación de estos. Los
pasos generales que componen a este algoritmo son:

Dado un conjunto de elementos desordenado, se realiza


lo siguiente:

1. Escoger un punto de referencia de ordenamiento


conocido como pivote. Luego se deben recolectar los
elementos que sean menores y mayores a este pivote.
La elección del pivote determinará cuán rápido/e caz será
el algoritmo. Por costumbre se suele escoger el elemento
que se encuentre más a la derecha dentro del conjunto, por
ejemplo, una lista como la siguiente [3,2,5,7,2,3,7,4,8,4]
tendría como posible pivote el elemento 4 situado en la
última posición.
2. Ordenar los elementos menores y mayores al
pivote seleccionado anteriormente.
Este paso se resuelve de manera recursiva realizando el
paso anterior, es decir que para cada sub-lista generada de
una división se escogerá un valor pivote que servirá para
determinar números mayores y menores a este. Este
procedimiento se realiza hasta que las listas resultantes sean
de un solo elemento.
3. Combinar todas las sub-listas generadas. Solo
combinar ya que es lo único que queda por realizar, esto
gracias a que en cada división se determinó que parte era
mayor y que parte era menor.

La implementación de este algoritmo con su ejecución es


la siguiente:

127
fi
Ejecutando el código se obtiene lo siguiente:

128
6.5 Retroalimentación del capitulo
En este capítulo se analizaron los algoritmos, que son y
porque son tan importantes al momento de iniciar en la
programación. Por otra parte, se vieron otros aspectos
propios de este como la representación por pseudocódigo y
algunas de las clasi caciones de algoritmos más usados
actualmente.

En este apartado se destaca lo siguiente:


 Un algoritmo se conoce como un conjunto de
instrucciones no-ambiguas, ordenadas y nitas que permiten
la solución de un problema o la realización de cierta
actividad.

 Un algoritmo es preciso, ordenado, nito y consistente

 Los algoritmos según su descripción son Cualitativos,


cuantitativos, computacionales y no computacionales,
ordenamiento, búsqueda, entre otros.

 El pseudocódigo es usado para describir un algoritmo


en un lenguaje que es altamente comprensible para los
humanos.

 Algunos de los algoritmos más populares son:

o Búsqueda: lineal, binaria.


o Búsqueda no informados: Usados en inteligencia
arti cial. Algunos de los más mencionados en esta categoría
son el algoritmo greedy, el A* y el Hill climbing.

o Ordenamiento: Counting, merge, Quick.

129
fi
fi
fi
fi
7
POO (PROGRAMACIÓN ORIENTADA A
OBJETOS)

Este paradigma de programación ha permitido a la


industria tecnológica actual desarrollar soluciones de
software para problemas de gran complejidad, de los cuales
generalmente se relacionan con el cómo entender una
situación de la vida real en donde este permite recrear lo que
el humano fácilmente puede observar y sentir, de manera
virtual. Algunos casos de aplicación a nivel global podrían ser
la inteligencia arti cial y su colaboración en la elaboración de
modelos de aprendizaje para un campo en especí co como
los autos que se conducen por sí mismos (vehículos
autónomos), también están los programas que permiten
manejar los cohetes construidos por empresas como la
NASA o Space X, incluso las soluciones que se han
propuesto para el sector del internet y consumo como lo son
las páginas web y los datos generados (bases de datos), así
mismos se tienen otros que se consideran actualmente
sencillos como un sistema operativo y sus funciones, el uso
de un lenguaje de programación, entre muchos otros.

130
fi
fi
7.1 ¿Qué es la programación orientada a
objetos (POO)?
La programación orientada a objetos es la forma cómo se
puede escribir un código de manera tal que el programa que
se esté desarrollando pueda representar lo más el posible
una situación en especí co, es decir, intenta re ejar el
funcionamiento de un momento particular de la vida real. Un
ejemplo muy común de ello sería un programa que permita
manejar la contabilidad de una empresa de venta de
productos. Se entiende que allí participan personas como
vendedores, operarios, jefes, y contadores, estos últimos se
encargan de registrar las acciones realizadas por los
vendedores (quienes interactúan con los clientes) tales como
registrar una venta. De esta manera y más suceden un
conjunto de situaciones las cuales podrían ser planteadas
por medio de este modelo de programación.

Comprendiendo el objetivo de la POO (OOP, siglas en


inglés para Object Oriented Programming) se debe conocer
las particularidades y funcionamiento de este modelo. Como
ya se mencionó, el objetivo es representar una situación de
la vida real y esto se logra mediante la abstracción de la
situación a una forma la cual se pueda entender, en este caso
la forma es clasi cando todo lo observable como aquello que
es una clase y aquello que es un objeto.

7.2 ¿Qué es un objeto?, ¿qué hay de las


clases?
Cada término se puede entender de la siguiente manera:

 Clase: Hablamos de una clase como si se tuviera un


molde con el cual se pueden elaborar múltiples elementos
similares entre sí. De una manera más elaborada se

131
fi
fi
fi
fl
entendería la clase como aquella clasi cación de elementos
que cumplen con ciertas características en común, pero que
cada elemento es diferenciable de otro. Por ejemplo, lo que
sucede con los moldes de galletas, si bien se pueden crear lo
que se conoce como galletas, una mezcla en su mayoría de
harina huevos y azúcar, cada una de estas puede tener
características distintas como su ancho y alto, las formas, e
inclusive el mismo contenido. Se obtienen en general
galletas, solo que cada una tiene características distintas.

Otro ejemplo de clase en la vida real podrían ser los


animales, si bien existen muchos tipos y clasi caciones,
todos ellos comparten características como el sentido de
supervivencia, la acción de comer, sus necesidades
siológicas, el hecho de que todos deben de permanecer en
un ambiente especí co para su pleno desarrollo, entre
muchas otras. Es entonces, al igual que sucede con el molde
de galletas expuesto, cada animal es diferenciable de otros
por lo que describen sus características.

• Objeto: Las clases se entienden como una


clasi cación de elementos similares pero que cada uno
es diferenciable del otro, por lo tanto, un objeto es
entonces el elemento clasi cado por dicha clase.
Continuando con los ejemplos anteriores para el caso
en el que se habla de galletas, si se conoce que la
clase es galletas y que cada galleta será diferenciable
una de otra por una o más características distintas,
entonces se puede a rmar que un objeto de la clase
galleta sería la galleta que está hecha de azúcar y
harina de trigo, así como que otro objeto independiente
que pertenece a la clase de galletas sería aquella
galleta hecha de mantequilla y harina de avena.

Para el caso de los animales, un objeto podría ser un león,


otro podría ser un elefante, un pájaro, un perro, un gato,
entre otros.
132
fi
fi
fi
fi
fi
fi
fi
Puede verse que el concepto de clase abarca la
generalidad de todo lo que se presenta en una situación de
la vida real, mientras que un objeto identi ca solo una parte
de ella.

7.2.1 Atributos y métodos

Hasta el momento se tienen las herramientas que


permiten representar una situación de la vida real de una
forma general, más no debe pasar por alto los detalles que
en gran medida determinan el comportamiento descrito.
Estos términos de atributos y métodos son características
propias de los objetos y las clases respectivamente los
cuales permiten añadir más información, así como
representar relaciones.

● Atributos: representan las características de los


objetos de nidos por una clase. Para el caso en el que se
tiene como clase los animales, los objetos que saldrían de allí
podrían tener como características su descripción física
(color, tamaño, extremidades, si tiene pelaje, garras, cola), su
hábitat de desarrollo, su alimento, comportamiento,
información acerca de su capacidad y manera reproducción,
entre muchas otras.
Para el caso de la clase galletas, cada objeto (es decir,
cada galleta proveniente del molde de galletas) tendría como
características el sabor, color, olor, textura, forma, rigidez,
ingredientes con los que fue elaborado, tiempo sometido al
calor, adiciones, entre otras.

● Métodos: se sabe que en la vida real todo puede tener


una acción, desempeñar alguna actividad, ejercer ciertas
funciones. Los animales tienen funciones/acciones de comer,
hacer sus necesidades, atacar, caminar, correr, hacer sonidos.
Las galletas por su parte pueden verse con funciones de

133
fi
fi
aportar nutrientes, cambiar de forma a medida que son
ingeridas, preservarse en el tiempo modi cando ciertas
características, entre otras. De lo anterior se puede a rmar
entonces que los métodos describen las acciones de cada
objeto.

7.2.2 Principios de la programación orientada a


objetos

La POO establece ciertos principios que ayudan a


determinar de una manera más precisa la representación de
la vida real por medio de las clases y objetos. Estos principios
son los siguientes:

● Encapsulamiento: indica que todos los elementos se


deben de encontrar a un mismo nivel de abstracción, es
decir, de representación. Menciona también que todo debe
de ser correctamente “empaquetado” según sea su nivel de
procedencia, por ejemplo, en la clase animales se determinó
que cada animal proveniente tendría ciertas características, y
que estás características serían únicamente de cada animal,
se estaría cumpliendo entonces el principio mencionado.
● Polimor smo: como lo indica su nombre, poli hace
referencia a “múltiples” mientras que mor smo se entiende
como “formas”, es entonces este principio el que permite
representar casos en donde se tengan múltiples
comportamientos e inclusive elementos distintos, pero que
puedan ser referenciados con un mismo nombre. Esto se
puede ver en el caso de la clase animal, en donde un
comportamiento distinto en cada objeto sería los sonidos que
realiza, como que una vaca suena diferente a un perro y a un
gato, pero al nal la acción es la de hacer un sonido.
● Herencia: es una forma de relacionar clases formando
jerarquías. Permite crear clases a partir de otras preexistentes
cediendo sus métodos y generalmente todos sus atributos.
La clase animales puede ser el inicio de una jerarquía mayor,

134
fi
fi
fi
fi
fi
en donde partiendo de esta clase inicial se pueden crear
otras que representan por ejemplo a los animales marinos,
terrestres, aéreos y an bios. Estos a su vez servirán para
clasi car nuevamente el término “animal” en casos
particulares. Mediante esta forma de relación se desarrollan
de una manera más clara los principios anteriores de
polimor smo y encapsulamiento.
● Principio de ocultación: se basa en aislar un objeto de
otros, esto para evitar accesos y cambios no permitidos. Esta
visión es más guiada hacia el desarrollo de código, más a
nivel práctico podría entenderse como que los objetos son
los únicos quienes pueden acceder directamente a sus
características. Por ejemplo, un animal ejerce la acción de
comer, cambiar su pelaje, mover las extremidades por su
propia cuenta. Por otra parte, si bien el acceso directo de
otros objetos o de cualquier otro individuo hacia los atributos
de otro está negado, se es permitido proveer información
acerca de ellos. Este concepto se podrá asimilar de una
mejor forma en los ejemplos de código posteriores.

7.2.3 Ejemplos

A continuación, se mostrarán las fases necesarias para


crear una clase:

7.2.3.1 De nir una clase

Al momento de de nir las clases siempre se debe de usar


la palabra reservada class seguido del nombre de dicha
clase. Este último siempre deberá de ir en mayúscula ya que
es de esta forma en cómo el lenguaje de Ruby entiende que
lo que se quiere es crear una clase.

135
fi
fi
fi
fi
fi
7.2.3.2 De nición de atributos

Antes de mencionar los atributos es necesario conocer los


tipos de variable que se presentan:

● Variables locales: Se presentan en la de nición de


funciones/métodos. Por lo anterior, siempre desempeñan
funciones internas a las cuales no se puede acceder (esto
encaminado al principio de encapsulamiento y ocultación).
Para su representación se utiliza la escritura lower case o
también el uso del guion bajo ‘_’ como pre jo del nombre de
la variable.

● Variables de instancia: También conocidas como


atributos, son las variables que dan las características
particulares para cada objeto/instancia de una clase, es decir
entonces que los valores que cada una toma serán distintos
entre objetos. Para su representación se utiliza la escritura
lower case y el uso del signo ‘@’ como pre jo del nombre de
la variable.

● Variables de clase: Como su nombre lo indica, estas


variables estarán presentes en la clase, es decir que todos
los objetos provenientes de esta última tendrán esta
característica con iguales valores. Para su representación se
utiliza la escritura lower case y el uso doble del signo ‘@’
como pre jo del nombre de la variable.

● Variables de globales: Este tipo de variables pueden


ser usadas en múltiples clases al mismo. Visto de otra forma,
son de nidas por fuera de una clase y a la par dicha clase
puede acceder a esta. Para su Representación se utiliza la
escritura lower case y el uso del signo ‘$’como pre jo del
nombre de la variable.

136
fi
fi
fi
fi
fi
fi
fi
Nota: Las variables globales no serán ejempli cadas en
los ejemplos posteriores. Se deja a recomendación del lector.

Ahora bien, ejempli cando los conceptos anteriores se


tiene lo siguiente:

Como se puede observar en el código, se han descrito


atributos de un animal tales como Nombre de su tipo
(typeName), color, comida, hábitat y años. Observe que
estos han sido escritos dentro de una función llamada
initialize(), esta hará la función de constructor-creador de
objetos, es decir que cada vez que se requiera crear un
objeto de la clase animal se deberá de usar esta función.

7.2.3.3 Creación de objetos

Desde el apartado anterior se observó la creación de un


método que ayudaría en la creación de objetos, siendo este
el método initialize(). Ahora bien, en conjunto con otra
palabra reservada de ruby, new, se pueden crear objetos de
la siguiente manera:

*Añadir las siguientes líneas al código mostrado en la


sección anterior.

137
fi
fi
Primero que todo, se decidió usar una función main() para
mantener allí el núcleo de ejecución de todo el código.
Segundo, la línea número 2 indica que se está creando un
nuevo objeto de la clase Animal con las características de
“Cat", "white", "Fish", "My house" y 2, es decir que se creará
un gato blanco que vive en una casa, come pescado y tiene
dos años, y que toda esa información será asignada a la
variable myAnimal1.

Gracias a initialize() y al hecho de que dicho método


recibe parámetros los cuales inicializan las variables de
instancia, es que es posible crear objetos con más
información que los describa. Sin embargo, es posible crear
objetos que no utilicen el método anterior, con la diferencia
de que estos no tendrán información en sus atributos por lo
que de una u otra forma tendrán que poder ser reasignados
en algún otro momento, por ejemplo

Observe que la clase Example sólo tiene un método con


el cual imprime un texto en pantalla, no posee atributos más
sin embargo es válido usar la palabra reservada new. Si en
algún momento se crearán atributos en otros métodos
138
distintos a initialize(), estos deben de ser llamados después
de la creación del objeto usando la forma anterior.

7.2.3.4 Getters y setters para la manipulación de


atributos

Estos dos tipos de manipuladores permiten, como su


nombre en inglés indica, acceder u obtener (getters) y
cambiar (setter) los valores almacenados en los atributos de
un objeto. Estos emplean el principio de ocultamiento
mencionado anteriormente que nos indicaba la privacidad
que se debe de asegurar con respecto a una clase y sus
características internas.

Ruby de ne estas herramientas de la siguiente manera:

class <nombre de la clase>


attr_reader : <nombre
del atributo a leer>
attr_writer : <nombre del
atributo a cambiar su valor>
attr_accessor : <nombre del
atributo a leer y/o cambiar su
valor>
end

● attr_reader: De ne el acceso a los atributos. Indicando


los atributos con esta herramienta genera que se pueda
acceder al valor almacenado en ellos en cualquier instante
de tiempo.
● attr_writer: De ne el acceso y cambio de información
de los atributos. Indicando los atributos con esta herramienta
genera que se pueda acceder y cambiar el valor almacenado
en ellos en cualquier instante de tiempo.
● attr_accessor: De ne las acciones de las dos
herramientas anteriores.

139
fi
fi
fi
fi
Para la clase usada como ejemplo se tendrían los
siguiente getters y setters:

En el Código se de nió entonces que solo se podrían leer


los atributos de typeName, color, food y hábitat, mientras que
abilities y years serían atributos los cuales podrían cambiar
sus valores en cualquier instante de tiempo, por lo que es
necesario conocerlos y actualizarlos constantemente.

Usando las características de myAnimal1 se obtiene lo


siguiente:

El resultado de la ejecución es:

140
fi
Observe que, de ahora en adelante, para poder acceder a
las características de nidas de un objeto se debe de usar un
punto después de la variable que almacena el objeto, luego
se escoge la característica a manipular. Observe por ejemplo
c ó m o s e a c c e d e a l a e d a d d e m yA n i m a l 1 c o n
myAnimal1.years.

Por otra parte, siempre se debe de veri car el tipo de


acceso de nido para cada atributo, como en el caso de
myAnimal1.habitat, el cual solo se de nió para que fuese de
lectura más sin embargo en un intento de cambiar su valor
almacenado se generó un error (señalado con (error)).

Nota: el error es solo una señal, ello no será indicado de


esa forma al momento de que el lector lo ejecute.

7.2.3.5 De nición de métodos

También conocidos como funcione, estos pueden ser


de nidos exactamente igual a como se describen estas
últimas en el lenguaje de Ruby. Por ejemplo, observe los
siguientes métodos:

141
fi
fi
fi
fi
fi
fi
142
Los nuevos métodos de nidos para la clase Animal son
animalWalk(), animalEat(), animalRun() y avoidCazadores(), a
la vez que se anexaron tres nuevos atributos a los cuales se
les de nió como herramienta de acceso la de tipo
attr_accesor, lo que permite conocer y actualizar el valor
almacenado en ellos.

Al ejecutar el código anterior se obtiene lo siguiente:

143
fi
fi
Se puede observar el cambio de los atributos a medida
que se utilizan nuevos métodos. Por otra parte, se logra usar
la variable global de nida como $hunters por fuera de la
clase, dentro del último método llamado.

7.2.4 Aplicación de los principios de herencia y


polimor smo

7.2.4.1 Herencia

Recordando la teoría mencionada al respecto de este


principio, la herencia se basa en adquirir el comportamiento
de una clase dentro de otra. Continuando con el ejemplo de
la clase animal se podría hacer lo siguiente:

144
fi
fi
El resultado de ejecutar el código anterior es:

Analizando el código anterior se observa la adición de


nueva sintaxis:

● De nición de herencia con el símbolo ‘<’:


Clase que hereda < clase que cede
● El uso de una función reservada de Ruby, super(..)
● La posibilidad de reescribir un método de nido en una
clase padre (aquella que cede su comportamiento) dentro de
la clase hija (aquella que hereda)

El código intenta representar la descripción general de un


animal y de la cual por medio del uso del principio de

145
fi
fi
herencia se logra de nir a un tipo de animal más especí co
que a su vez abarca un gran conjunto de elementos.

Dentro de la función main se logra acceder a los atributos


y métodos desde un objeto perteneciente a la clase Animal
como también acceder a los atributos y métodos otorgados
desde Animal hacia Dog, comprobando el correcto
funcionamiento del proceso de herencia.

Obsérvese entonces que este tipo de herramientas


permiten desarrollar programas más complejos en el sentido
de que abarquen múltiples tipos de datos de nidos por una
persona permitiendo a su vez desarrollar tareas complejas de
una manera comprensible.

7.3 Polimor smo


Este aspecto de la POO dicta la posibilidad de describir
una misma acción para diferentes elementos, por ejemplo,
todos los animales pueden hacer un sonido, diferenciándose
únicamente en lo que producen.

Dado el ejemplo anterior de herencia, se puede comenzar


a hablar de polimor smo desde el momento en que se
produce una herencia de animal hacia otras clases,
permitiendo la sobre escritura de métodos, como lo es el
caso de animalEat que presenta salidas distintas para los
objetos de myAnimal1 y aDog.

Dicho lo anterior, si se desean crear más clases las cuales


heredarán el comportamiento de Animal, como por ejemplo
Bird, Lion, Fish, entre otras, será entonces posible la
de nición de métodos similares para cada uno, pero con
salidas distintas.

146
fi
fi
fi
fi
fi
fi
7.4 Retroalimentación del capitulo

En este capítulo se introdujo la programación orientada a


objetos, el paradigma encargado de abstraer un problema de
la vida real a una representación sistemática que es
comprensible por un computador. Dicha representación se
hace mediante clases (se pueden entender como moldes) y
objetos (resultados de usar los moldes).
También se mencionan ciertos principios que permiten de
la programación orientada a objetos ser una gran alternativa
en la solución de problemas mediante la programación.
Dichos principios son:

 Encapsulamiento: Entender todo como un empaquetado


y que cada paquete es independiente de los demás, pero
que aun así se comparten características.

 Polimor smo: Un objeto puede presentarse en muchas


formas, por ejemplo un animal puede ser un gato, un perro,
una ave, entre otros.

 Herencia: Una forma de relacionar cosas mediante


jerarquía. Aquí las características y comportamientos de un
elemento pueden ser usadas por otro.

 Ocultación: Comparándolo con el funcionamiento de las


personas, todas comparten características, pero cada uno
tiene cualidades propias que no comparte con nadie y que
nadie tiene acceso a ellas. Es básicamente que cada
elemento a representar tenga privacidad frente a los demás
elementos con los que interactúa.

147
fi
8
ANEXOS

A continuación se disponen algunos recursos que servirán


como ayudas al proceso de aprendizaje para el lector
durante la lectura de este documento.

Página Concepto
https://github.com/ Repositorio con códigos
MiguelALF12/recursos- utilizados en la lectura del libro
fundamentos-de-programacion-
con-ruby.git
visualising data structures Visualización de estructura
and algorithms through de datos y algoritmos mediante
animation - VisuAlgo animaciones

Codeforces, HackerRank, Sitios web para practicar


Leetcode algoritmos de programación.

IDE for Ruby Ambiente de desarrollo


integrado (IDE) para Ruby.
Tutorial de Ruby por Tutorial de YouTube donde se
FreeCodeCamp dan los conceptos básicos de
Ruby de manera practica además
de algunos trucos en la
programación con este lenguaje.

Tabla 12. Recursos adicionales

148
REFERENCIAS DE BIBLIOGRAFÍA Y
WEBGRAFÍA
Dane, M. (06 de 06 de 2018). Ruby programming
language.
Dave Thomas, C. F. (2004). Programming Ruby - The
Pragmatic Programmer's Guide. United states: Pragmatic
Programmers.
Seifer, J. (26 de 11 de 2014). teamtreehouse. Obtenido de
blog.teamtreehouse: https://blog.teamtreehouse.com/coolest-
ruby-projects-ever
Flanagan, D., & Matsumoto, Y. (2008). The Ruby
programming language: Everything you need to know.
California: O'Reilly Media Inc.
Olsen, R. (2011). Eloquent Ruby (Addison-Wesley
Professional Ruby Series). Manhattan: Pearson Education Inc.
J. Jones, P. (2015). E ective Ruby: 48 Speci c Ways to
Write Better Ruby. Estados Unidos: Pearson Education Inc.
Fitzgerald, M. (2015). Ruby Pocket Reference: Instant help
for ruby programmers. California: O'Reilly Media Inc.
A. Shaw, Z. (2015). LEARN RUBY THE HARD WAY THIRD
EDITION. Pearson Education Inc.
Shaughnessy, P. (2013). Ruby under a microscope: An
Illustrated Guide to Ruby Internals. No Starch Press.
Carlson, L., & Richardson, L. (2015). Ruby Cookbook:
Recipes for Object-Oriented Scripting. O'Reilly Media Inc.
T. Brown, G. (2009). Ruby Best Practices:Increase Your
Productivity - Write Better Code. O'Reilly Media Inc.
A. Black, D., & Leo III, J. (2019). The Well-Grounded
Rubyist. Manning.
Wikipedia. (17 de 07 de 2022). Wikipedia the free
encyclopedia. Obtenido de Algoritmo: https://
es.wikipedia.org/wiki/Algoritmo

149
ff
fi
Wikipedia. (14 de 07 de 2022). Wikipedia the free
encyclopedia. Obtenido de Ruby (programming language):
https://en.wikipedia.org/wiki/Ruby_(programming_language)
ruby-doc.org. (s.f.). ruby-doc. Obtenido de ruby-doc.org:
https://ruby-doc.org/core-3.0.1/doc
Castello, J. (Marzo de 2015). rubyguides. Obtenido de
https://www.rubyguides.com/2015/03/caesar-cipher-in-ruby/
kalkicode. (20 de Septiembre de 2021). Obtenido de
https://kalkicode.com/coin-change-using-greedy-algorithm-in-
ruby
Megan. (25 de Abril de 2020). DEV. Obtenido de https://
d e v. t o / m w o n g 0 6 8 / m e r g e - s o r t - i n -
ruby-28n1#:~:text=Merge%20Sort%20in%20Ruby%201%20Ov
erview.%20If%20you%27ve,...%203%20Actual%20Code.
%20...%204%20Resources.%20

150

También podría gustarte