1. Diseño procedimental: algoritmos

1.1. Índice de masa corporal

Diseño algorítmico en pseudocódigo usando los ocho tipos de instrucciones (en español):

Listado 1. bmi.pseudo
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Procedimiento Clasificar personas por índice de masa corporal:
  Repita mientras hayan datos
    Clasifique una persona por índice de masa corporal

Procedimiento Clasificar una persona por índice de masa corporal:
  Leer masa, altura en cm
  Imprimir masa, altura en cm
  Si la masa es válida y la altura en cm es válida entonces
    Crear altura en metros como altura en cm / 100
    Crear imc como masa / (altura en metros)^2
    Crear estado nutricial como el resultado de clasificar indice de masa corporal
    Imprimir imc, estado nutricional
  De lo contrario
    Imprimir "invalid data"

Función Es la masa válida:
  Retorne la masa > 0 y la masa es <= 1000

Función Es la altura válida:
  Retorne la altura > 0 y la altura es <= 300

Función Clasificar indice de masa corporal:
  Si imc < 18.5 entonces
    Retorne "underweight"
  Si imc < 25 entonces
    Retorne "normal"
  Si imc < 30 entonces
    Retorne "overweight"
  Retorne "obese"

Diseño con diagramas de flujo:

Procedimiento principal
Figure 1. Procedimiento principal
Procedimiento que clasifica personas
Figure 2. Procedimiento que clasifica personas
Funciones que validan la masa y la altura
Figure 3. Funciones que validan la masa y la altura
Función que encuentra el estado nutricional de un índice de masa corporal
Figure 4. Función que encuentra el estado nutricional de un índice de masa corporal

1.2. PseInt

PseInt es un intérprete de pseudocódigo que puede ejecutarlo, diagramarlo, y exportarlo a lenguajes de programación. Sólo soporta programas interactivos y no en lote. Ejemplo del índice de masa corporal en esta notación.

Listado 2. bmi.psc
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Proceso Clasificar_personas_por_índice_de_masa_corporal // snake_case
	Repetir // keywords
		Clasificar_una_persona_por_índice_de_masa_corporal
		Imprimir "¿Desea clasificar otra persona? [sN]"
		Leer continuar
	Hasta Que continuar <> "s" Y continuar <> "S" // < >
FinProceso // CamelCase

SubProceso Clasificar_una_persona_por_índice_de_masa_corporal
	Escribir "Masa (kg): "
	Leer masa
	Escribir "Altura (cm): "
	Leer altura_en_cm
	Imprimir masa, " ", altura_en_cm
	Si Es_la_masa_válida(masa) Y Es_la_altura_válida(altura_en_cm) Entonces
		Definir altura_en_m Como Real
		altura_en_m <- altura_en_cm / 100
		imc <- masa / (altura_en_m * altura_en_m)
		estado_nutricional <- Clasificar_indice_de_masa_corporal(imc)
		Imprimir imc, " ", estado_nutricional
	SiNo
		Imprimir " invalid data"
	Fin Si
FinSubProceso

Funcion resultado <- Es_la_masa_válida(masa)
	resultado <- masa > 0 Y masa <= 1000
FinFuncion

// altura_en_cm debe estar en centimetros
Funcion resultado <- Es_la_altura_válida(altura_en_cm)
	resultado <- altura_en_cm > 0 Y altura_en_cm <= 300
FinFuncion

Funcion estado_nutricional <- Clasificar_indice_de_masa_corporal(imc)
	Si imc < 18.5 Entonces
		estado_nutricional <- "underweight"
	SiNo
		Si imc < 25 entonces
			estado_nutricional <- "normal"	
		SiNo
			Si imc < 30 entonces
				estado_nutricional <- "overweight"
			SiNo
				estado_nutricional <-  "obese"
			FinSi
		FinSi
	FinSi
Fin Funcion

Funcion resultado <- f(x) // < -
	resultado <- x * (x - 1) / 2 // := <-
FinFuncion  // 2 * f(3)

2. Sistemas numéricos

2.1. Acumuladores

Las computadoras evolucionan de las calculadoras mecánicas. De la Pascalina se preserva el concepto de acumulador. Es un espacio de la memoria que almacena los dígitos de un número. A diferencia de los humanos, los números en la computadora no pueden tener una cantidad infinita de dígitos, por lo que se trata de una arimética de precisión fija. El siguiente ejemplo suma dos números en una cantidad de dígitos fija.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Fixed-point precision arithmetic
Algoritmo n_digits_adder10
	Leer cantidad_digitos
	// Leer los dos numeros como textos
	Leer numero1,numero2
	// Imprimir numero1 + numero2

	// Sacar los digitos de los dos numeros y ponerlos en arreglos
	Dimension arr1[cantidad_digitos]
	pos_arr <- cantidad_digitos
	Para pos_texto<-Longitud(numero1) Hasta 1 Con Paso -1 Hacer
		// Imprimir Sin Saltar Subcadena(numero1, pos, pos), ","
		digito <- ConvertirANumero(Subcadena(numero1,pos_texto,pos_texto))
		arr1[pos_arr] <- digito
		pos_arr <- pos_arr-1
	FinPara

	// Imprimir
	Para indice<-1 Hasta cantidad_digitos Hacer
		Escribir arr1[indice],',' Sin Saltar
	FinPara

	// TODO: Eliminar la redundancia de codigo
	Dimension arr2[cantidad_digitos]
	pos_arr <- cantidad_digitos
	Para pos_texto <- Longitud(numero2) Hasta 1 Con Paso -1 Hacer
		// Imprimir Sin Saltar Subcadena(numero1, pos, pos), ","
		digito <- ConvertirANumero(Subcadena(numero2,pos_texto,pos_texto))
		arr2[pos_arr] <- digito
		pos_arr <- pos_arr-1
	FinPara

	// Imprimir
	Escribir '' // Separar con un cambio de linea
	Para indice <- 1 Hasta cantidad_digitos Hacer
		Escribir arr2[indice],',' Sin Saltar
	FinPara

	// Hacer la suma
	Dimension resultado[cantidad_digitos]
	acarreo <- 0
	Para indice <- cantidad_digitos Hasta 1 Con Paso -1 Hacer
		suma <- arr1[indice] + arr2[indice] + acarreo
		resultado[indice] <- suma MOD 10
		acarreo <- trunc(suma/10)
	FinPara
	// resultado[1] <- acarreo

	// Imprimir
	Escribir '' // Separar con un cambio de linea
	Para indice <- 1 Hasta cantidad_digitos Hacer
		Escribir resultado[indice],',' Sin Saltar
	FinPara
FinAlgoritmo

Si el resultado de la suma ocupa más dígitos, los acumuladores anteriores los descartan, lo que provoca una povoca un fenómeno llamado desbordamiento (overflow). Eso nunca ocurre en la matemática humana. Para simular la matemática humana, se pueden crear acumuladores de más dígitos, siempre y cuando se disponga de suficiente memoria en la máquina, lo que genera una aritmética de precisión arbitraria.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Arbitrary precision arithmetic (software)
Algoritmo n_digits_adder10
	Leer cantidad_digitos
	// Leer los dos numeros como textos
	Leer numero1,numero2
	// Imprimir numero1 + numero2

	// Sacar los digitos de los dos numeros y ponerlos en arreglos
	Dimension arr1[cantidad_digitos]
	pos_arr <- cantidad_digitos
	Para pos_texto <- Longitud(numero1) Hasta 1 Con Paso -1 Hacer
		// Imprimir Sin Saltar Subcadena(numero1, pos, pos), ","
		digito <- ConvertirANumero(Subcadena(numero1,pos_texto,pos_texto))
		arr1[pos_arr] <- digito
		pos_arr <- pos_arr-1
	FinPara

	// Imprimir
	Para indice<-1 Hasta cantidad_digitos Hacer
		Escribir arr1[indice],',' Sin Saltar
	FinPara

	// TODO: Eliminar la redundancia de codigo
	Dimension arr2[cantidad_digitos]
	pos_arr <- cantidad_digitos
	Para pos_texto<-Longitud(numero2) Hasta 1 Con Paso -1 Hacer
		// Imprimir Sin Saltar Subcadena(numero1, pos, pos), ","
		digito <- ConvertirANumero(Subcadena(numero2,pos_texto,pos_texto))
		arr2[pos_arr] <- digito
		pos_arr <- pos_arr-1
	FinPara

	// Imprimir
	Escribir '' // Separar con un cambio de linea
	Para indice <- 1 Hasta cantidad_digitos Hacer
		Escribir arr2[indice],',' Sin Saltar
	FinPara

	// Hacer la suma
	Dimension resultado[cantidad_digitos + 1]
	acarreo <- 0
	Para indice <- cantidad_digitos Hasta 1 Con Paso -1 Hacer
		suma <- arr1[indice] + arr2[indice] + acarreo
		resultado[indice + 1] <- suma MOD 10
		acarreo <- trunc(suma/10)
	FinPara
	resultado[1] <- acarreo

	// Imprimir
	Escribir '' // Separar con un cambio de linea
	Para indice <- 1 Hasta cantidad_digitos + 1 Hacer
		Escribir resultado[indice],',' Sin Saltar
	FinPara
FinAlgoritmo

En las computadoras modernas, la aritmética de precisión arbitraria es provista no por el hardware sino por el software, lo que la hace en el orden de miles de veces más lenta. Un ejemplo es el lenguaje de programación Python, que la incorpora en el lenguaje para números enteros, pero no para números reales.

3. Python

3.1. Entrada y salida

Instrucciones de tipo 1 y 2: leer e imprimir

Listado 5. io.py
1
2
3
4
5
6
7
# 1. Read values
line = input("Your name: ")
# 2. Print/Write values
print('Hi ', line, '!', sep='', end='')
# print(line, end='')
# print('!')
print()

3.2. Conversiones de tipo

Python convierte un valor de un tipo de datos a otro usando la notación tipo(valor). Los tipos primitivos son:

  1. chr: carácter (ej.: una letra)

  2. str: texto (ej.: una o varias letras)

  3. int: entero de precisión fija o arbitraria, con o sin signo.

  4. float: doble precisión flotante (IEEE-754 de 64 bits)

Debe usarse try/except si un valor no puede convertirse de un tipo de datos a otro.

Listado 6. numbers.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
text = None
text = input('Number: ')
try:
    text = int(text)
    text = 2 * text
    print(text)
except ValueError:
    pass
    # print(text, 'is not a fixed number')

try:
    real = float(text)
    print(2 * real)
except ValueError:
    pass
    # print(text, 'is not a floating point number')

print(2 * text)
# 2*10 + 0 = 20
# 20 * 10 + 5 = 205

# Python data types:
# bool: {False, True}
# int: {fixed two-complement + arbitrary precision}
# float: IEEE 754 (double)
# str: Unicode text
# None:

3.3. Condicionales y ciclos

Los ciclos son como los condicionales, sólo que, los condicionales ejecutan la instrucción sólo una vez si la condición es verdadera. En cambio los ciclos se mantienen ejecutando el bloque de instrucciones mientras la condición sea verdadera.

Hay tres tipos de ciclos:

  1. Ciclos por contador cuando se sabe la cantidad de repeticiones de antemano.

  2. Ciclos por condición, cuando no se sabe la cantidad de repeticiones de antemano.

  3. Ciclos por colección, cuando se quiere recorrer todos los elementos de una colección (ejemplo, arreglos).

Listado 7. square1.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
try:
    while True:
        # Read the dimensions of the square
        size = int(input("Tamaño: "))
        if size == 0:
            break
        if size > 0 and size <= 20:
            # For each line
            for row in range(size):
                # Print a line
                for column in range(size):
                    print("*", end="")
                print()
        else:
            print("Tamaño debe estar entre 1 y 20")
except ValueError:
    print("Tamaño inválido")

3.4. Subrutinas (funciones)

Las subrutinas son bloques se código a los que se les da un nombre, y se pueden invocar desde varios lugares del programa. Permiten reutilizar código, modularizarlo, y hacerlo más legible.

Listado 8. square2.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def print_line(column_count):
    for column in range(column_count):
        print("*", end="")
    print()

def print_square(size):
    # For each line
    for row in range(size):
        # Print a line
        print_line(size)

def main():
    while True:
        try:
            # Read the dimensions of the square
            size = int(input("Tamaño: "))
            if size == 0:
                break
            if size > 0 and size <= 20:
                print_square(size)
            else:
                print("Tamaño debe estar entre 1 y 20")
        except ValueError:
            print("Tamaño inválido")

main()

3.5. Arreglos (vectores)

En Python se puede tener una colección de varios valores llamada arreglo o vector (que en terminología Python se llama lista). Un arreglo es como un conjunto de celdas numeradas de forma única. El número que identifica una celda se llama índice. En cada celda se puede guardar lo que se quiera, como un número, un texto, otro arreglo, o dejarla vacía.

Listado 9. arrays.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
nombres_1digito = ["cero", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve"]

# print(nombres_1digito[3])

while True:
    digito = int(input())  # "Digito: "
    if (digito >= 0 and digito < len(nombres_1digito)):
        print(digito, ': ', nombres_1digito[digito], sep='')
    else:
        break

Si no se hacen preguntas interactivas al usuario si no que se supone los datos vienen en la entrada con un formato preestablecido, se tiene un programa en lote. Tiene la ventaja de que se puede redireccionar la entrada y la salida para tomar los datos de un archivo en lugar del teclado.

Listado 10. arrays_batch.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
nombres_1digito = ["cero", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve"]

# print(nombres_1digito[3])

while True:
    digito = int(input())  # "Digito: "
    if (digito >= 0 and digito < len(nombres_1digito)):
        print(digito, ': ', nombres_1digito[digito], sep='')
    else:
        break

Supóngase que los datos se escriben en un archivo tests/input001.txt:

Listado 11. tests/input001.txt
1
2
3
4
5
6
7
8
9
8
8
8
2
3
4
5
0
91

Se puede invocar al programa en Python para que tome estos datos en la entrada estándar sin tener que escribirlos manualmente en el teclado. A esto se le llama redireccionar la entrada estándar con el operador < en la línea de comandos. Es válido tanto en Unix como MS-DOS:

$ python3 arrays.py < tests/input001.txt
8: ocho
8: ocho
8: ocho
2: dos
3: tres
4: cuatro
5: cinco
0: cero