Pesadillas con If/else

baltasarq

Baltasar García Perez-Schofield

Posted on September 18, 2024

Pesadillas con If/else

if/else representa decisiones

De cuando en cuando, "salta" en internet código como el siguiente.

Conversiones de caracteres a número y viceversa

char chrFromInt(int digit)
{
    char toret;

    if ( digit == 0 ) {
        toret = '0';
    }
    else
    if ( digit == 1 ) {
        toret = '1';
    }
    else
    if ( digit == 2 ) {
        toret = '2';
    }
    else
    if ( digit == 3 ) {
        toret = '3';
    }
    else
    if ( digit == 4 ) {
        toret = '4';
    }
    else
    if ( digit == 5 ) {
        toret = '5';
    }
    else
    if ( digit == 6 ) {
        toret = '6';
    }
    else
    if ( digit == 7 ) {
        toret = '7';
    }
    else
    if ( digit == 8 ) {
        toret = '8';
    }
    else
    if ( digit == 9 ) {
        toret = '9';
    } else {
        fprintf( stderr, "ERROR: digit no es 0-9: %d\n", digit );
        exit( -1 );
    }

    return toret;
}
Enter fullscreen mode Exit fullscreen mode

Este auténtico anti-patrón (antipattern), puede tomar varias formas con distintos requerimientos. La variante más común, sin embargo, sustituye if/else por switch.

char chrFromInt(int digit)
{
    char toret;

    switch( digit ) {
                case 0:
            toret = '0';
            break;
        case 1:
            toret = '1';
            break;
        case 2:
            toret = '2';
            break;
        case 3:
            toret = '3';
            break;
        case 4:
            toret = '4';
            break;
        case 5:
            toret = '5';
            break;
        case 6:
            toret = '6';
            break;
        case 7:
            toret = '7';
            break;
        case 8:
            toret = '8';
            break;
        case 9:
            toret = '9';
            break;
        default:
            fprintf( stderr, "ERROR: digit no es 0-9: %d\n", digit );
                        exit( -1 );
    }    

    return toret;
}
Enter fullscreen mode Exit fullscreen mode

El código anterior sería C. En Python, la función anterior sería algo como:

def chr_from_int(digit):
    if digit == 0:
        toret = '0'
    elif digit == 1:
        toret = '1'
    elif digit == 2:
        toret = '2'
    elif digit == 3:
        toret = '3'
    elif digit == 4:
        toret = '4'
    elif digit == 5:
        toret = '5'
    elif digit == 6:
        toret = '6'
    elif digit == 7:
        toret = '7'
    elif digit == 8:
        toret = '8'
    elif digit == 9:
        toret = '9'
    else:
        raise Error("digit no es 0-9, sino " + str(digit))

    return toret
Enter fullscreen mode Exit fullscreen mode

Y la versión utilizando match (recientemente añadido al lenguaje), sería:

def chr_from_int(digit):
    match digit:
        case 0:
            toret = '0'
        case 1:
            toret = '1'
        case 2:
            toret = '2'
        case 3:
            toret = '3'
        case 4:
            toret = '4'
        case 5:
            toret = '5'
        case 6:
            toret = '6'
        case 7:
            toret = '7'
        case 8:
            toret = '8'
        case 9:
            toret = '9'
        case _:
            throw Error("digit no es 0-9, sino " + str(digit))

    return toret
Enter fullscreen mode Exit fullscreen mode

El uso de esta función sería bastante simple (código C):

for(int i = 0; i < 12; ++i) {
    printf( "%c\n", intFromDigit( i ) );
}
Enter fullscreen mode Exit fullscreen mode

Y la salida sería la siguiente:

0
1
2
3
4
5
6
7
8
9
Runtime error
ERROR: digit no es 0-9: 11
Enter fullscreen mode Exit fullscreen mode

¿Y qué es lo que podemos hacer en este caso? Como siempre, podemos investigar más profundamente. ¿Por qué hacerlo? Porque escribiendo este programa, estamos repitiendo código. Siempre que repetimos código, significa que estamos en un problema.

Todas las codificaciones de caracteres (UTF-16, UTF-8, ISO-8859-1...) se apoyan en el código ASCII para representar los caracteres básicos, como se puede ver en la siguiente tabla.

Tabla de caracteres ASCII imprimibles.

Si nos fijamos, los caracteres están representados en el código ASCII en orden alfabético, así como los dígitos lo están de menor a mayor. Esto es verdaderamente importante; no que el carácter 'a' sea el código nº 97, o el '0' el 48. A pesar de que el código ASCII está tan extendido que encontrar un hardware que no lo use directa o indirectamente, sea difícil, aún puede suceder. Así que es mejor utilizar las características del propio lenguaje. Por ejemplo, en C, 'a' devuelve directamente el código numérico asociado (es decir, 97 en ASCII), al igual que ord('a') hace lo mismo en Python (y chr(97) lo contrario).

Así que es cuestión de sumar al número que nos pasan, el valor de '0'.

char chrFromInt(int digit)
{
    if ( digit < 0
      || digit > 9 )
    {
        fprintf( stderr, "ERROR: digit no es 0-9: %d\n", digit );
        exit( -1 );
    }

    return '0' + digit;
}
Enter fullscreen mode Exit fullscreen mode

En el caso de Python, como vimos en los ejemplos anteriores, también se puede repetir código. Pero el soporte de Python de estructuras de datos complejas (como diccionarios o listas), con un uso muy sencillo, nos puede llevar a abusar de ellas. Un ejemplo se puede ver a continuación.

def chr_from_int(digit: int) -> str:
    sust = [0: '0', 1: '1', 2: '2', 3: '3', '4': '5', 6: '6', 7: '7', 8: '8', 9: '9']

    toret = sust.get(digit)

    if not toret:
        raise Error("digit no es 0-9, sino " + str(digit))

    return toret
Enter fullscreen mode Exit fullscreen mode

Es un poco mejor, porque no repetimos código, pero... ¿Cuáles son las claves de ese diccionario sust? El rango 0 a 9 sería el acceso normal de cualquier lista, así que en realidad se puede simplificar a:

def chr_from_int(digit: int) -> str:
    sust = ['0', '1', '2', '3', '4', '6', '7', '8', '9']

    if (digit < 0
     or digit > 9):
        throw Error("digit no es 0-9, sino " + str(digit))

    return sust[digit]
Enter fullscreen mode Exit fullscreen mode

De nuevo está mejor que lo que teníamos antes, pero... lo único que tenemos es una lista con los dígitos ordenados del 0 al 9 en formato de carácter... ya sabemos que... se puede hacer mucho mejor. No necesitamos de ninguna estructura de datos, ocupando memoria.

Así, en Python:

def chr_from_int(digit: int) -> str:
    if (digit < 0
     or digit > 9):
        throw Error("digit no es 0-9, sino " + str(digit))

    return chr(ord('0') + digit)
Enter fullscreen mode Exit fullscreen mode

Leet speech

Podríamos querer convertir un texto cualquiera a leet speech, una forma de escribir que sería algo así como el "discurso" de los hackers.

La conversión del alfabeto natural a leet, consiste básicamente en sustituir los caracteres por dígitos (además de algunos otros caracteres). Está conversión está representada en la siguiente tabla.

Carácter Dígito
a 4
b 8
c (
d 6
e 3
f |
g C-
h #
i 1
j ]
k |<
l |_
m [V]
n /V
o 0
p |7
q 9
r I2
s 5
t 7
u
v \/
w \N
x ><
y `/
z 2

Para que quede claro, queremos una función que, al llegarle un texto como "Baltasar", devuelva otro texto: "84|_7454I2".

Si utilizamos Python, podemos llegar a este código, parecido al que teníamos anteriormente.

def leet_from_str(s: str) -> str:
    sust = {
        'a': "4",
        'b': "8",
        'c': "(",
        'd': "C-",
        'e': "3",
        'f': '|',
        'g': "C-",
        'h': "#",
        'i': "1",
        'j': "]",
        'k': "|<",
        'l': "|_",
        'm': "[V]",
        'n': "/V",
        'o': "0",
        'p': "|7",
        'q': "9",
        'r': "|2",
        's': "5",
        't': "7",
        'u': "|_|",
        'v': "\\/",
        'w': "\\N",
        'x': "><",
        'y': "'/",
        'z': "2",
    }

    toret = ""
    for ch in s:
        ch2= sust.get(ch)

        if not ch2:
            raise Error("ch no es a-z, sino "     + str(digit))

        toret += ch2

    return toret
Enter fullscreen mode Exit fullscreen mode

Con este código, print(leet_from_str("Baltasar")), nos devuelve 84|_7454|2, como se esperaba.

De acuerdo, pero C no tiene soporte para diccionarios en su librería estándar. ¿Qué podemos hacer en un caso así?

Eso lo veremos en el siguiente capítulo...

💖 💪 🙅 🚩
baltasarq
Baltasar García Perez-Schofield

Posted on September 18, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Arrays paralelos
spanish Arrays paralelos

September 23, 2024

Pesadillas con If/else
spanish Pesadillas con If/else

September 18, 2024