Trocar Dígitos de Um Número

por Fabio A. Mazzarino

Já vi essa questão em uma entrevista de emprego. E confesso que pra desapontamento do entrevistador eu resolvi primeiro do jeito que ele não esperava e depois do jeito que ele esperava. A questão é a seguinte: Dado um número natural troque todas as ocorrências de um dado dígito, por um outra dado dígito. Ou seja, dado o int 1234138, troque os dígitos 3 por dígitos 5, resultando no int 1254158. Uma função assim:

int replace(int original, int from, int to);

Resolução Esperada

A resolução esperada, obviamente é utilização de matemática, e conhecimento de padrão da linguagem. Assim o esperado é que o candidato consiga utilizar potenciação para adicionar e remover o dígido individualmente.

Dessa forma o resultado seria:

#include <ctype.h>
#include <libgen.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

int isnatural(char* s) {
    while(*s) {
        if (!isdigit(*s++))
            return 0;
    }
    return 1;
}

int replace(int original, int from, int to) {
    // sanity
    if (original < 0 || from < 0 || from > 9 || to < 0 || to > 9) 
        return -1;

    // 32bits ints -> 10 digits
    int result = 0;
    for (int ctdigit = 10; ctdigit--; ) {
        int digit = original % (int)pow(10, ctdigit + 1) / pow(10, ctdigit); 
        if (digit == from)
            digit = to;
        result += digit * pow(10, ctdigit);
    }
    return result;
}

int main(int argc, char* argv[]) {
    // arguments
    if (argc < 4) {
        fprintf(stderr, "%s: insuficient arguments\n", basename(argv[0]));
        fprintf(stderr, "Usage: %s <ORIGINAL> <DIGIT-FROM> <DIGIT-TO>\n\n", basename(argv[0]));
        return 1;
    }
    // arguments sanity
    if (!isnatural(argv[1]) || !isnatural(argv[2]) || !isnatural(argv[3])) {
        fprintf(stderr, "%s: invalid arguments, use only natural numbers\n", basename(argv[0]));
        fprintf(stderr, "Usage: %s <ORIGINAL> <DIGIT-FROM> <DIGIT-TO>\n\n", basename(argv[0]));
        return 1;
    }

    int numresult = replace(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
    printf("%d\n", numresult);
    return 0;
}

Testando na prática:

$ gcc -x c -o numericreplace numericreplace.c -lm
$ ./numericreplace 1234138 3 5
1254158
$

Resolução Alternativa

Essa resolução causou certo desapontamento ao entrevistador, e ele mesmo disse que não esperava tal solução. Porém a resolução alternativa é mais eficiente e mais simples que a esperada.

Ao invés de apelar pra matemática, eu simplesmente utilizei duas funções simples, sprintf e atoi. Primeiro eu converti de int para string, fiz a substituição de maneira bem rápida, e em seguida voltei a string pra int. Mais simples, mais rápido, e com menos código.

A solução ficou assim:

#include <ctype.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>

int isnatural(char* s) {
    while(*s) {
        if (!isdigit(*s++))
            return 0;
    }
    return 1;
}

int replace(int original, int from, int to) {
    // sanity
    if (original < 0 || from < 0 || from > 9 || to < 0 || to > 9) 
        return -1;

    char szoriginal[12] = "";
    char cfrom = '0' + from;
    char cto = '0' + to;

    sprintf(szoriginal, "%d", original);
    char* s = szoriginal;
    while(*s) {
        if (*s == cfrom)
            *s = cto;
        s++;
    }
    return atoi(szoriginal);
}

int main(int argc, char* argv[]) {
    // arguments
    if (argc < 4) {
        fprintf(stderr, "%s: insuficient arguments\n", basename(argv[0]));
        fprintf(stderr, "Usage: %s <ORIGINAL> <DIGIT-FROM> <DIGIT-TO>\n\n", basename(argv[0]));
        return 1;
    }
    // arguments sanity
    if (!isnatural(argv[1]) || !isnatural(argv[2]) || !isnatural(argv[3])) {
        fprintf(stderr, "%s: invalid arguments, use only natural numbers\n", basename(argv[0]));
        fprintf(stderr, "Usage: %s <ORIGINAL> <DIGIT-FROM> <DIGIT-TO>\n\n", basename(argv[0]));
        return 1;
    }

    int strresult = replace(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
    printf("%d\n", strresult);
    
    return 0;
}

E na prática:

$ gcc -x c -o stringreplace stringreplace.c
$ ./stringreplace 1234138 3 5
1254158
$

Profissionalmente

No caso da entrevista a solução falou a meu favor, o entrevistador afirmou que eu pensei fora da caixa para apresentar uma solução mais performática.

Mas no dia-a-dia profissional, o desenvolvedor tem que estar atento para não se ater 100% à solução esperada. O mais importante é o código correto, simples e fácil de manter. Uutilizar pequenas distorções para obter o mesmo resultado pode trazer soluções inesperadas e inovadoras.

Saber disso é muito importante, principalmente quando o cliente, ou o analista, gosta de definir como o código deve ser implementado, essa resposta é prerrogativa do programador, ou do arquiteto do código, em raríssimos casos do analista ou mesmo do cliente.

Sugestões dos Leitores

Também recebemos sugestões dos leitores. No caso abaixo o leitor Thiago Adams mandou uma solução diferente, utilizando aritmética. Veja a solução abaixo:

#include <assert.h>
#include <limits.h>

unsigned int replace(int original, int old_digit, int new_digit) {
    // pre requisitos
    assert(original >= 0);
    assert(old_digit >= 0 && old_digit <= 10);
    assert(new_digit >= 0 && new_digit <= 10);

    if (original < 10)   {
        /* caso de um digito */
        return  (original == old_digit) ? new_digit : original;
    }
   
    unsigned int answer = 0;
    unsigned int factor = 1;
    while (original > 0) {
        unsigned int r = original % 10;
        if (r == old_digit)
            r = new_digit;
        answer = r * factor + answer;
        original = original / 10;
        factor = factor * 10;
    }
    return answer;
}

int main() {
    assert(replace(0, 0, 1) == 1);
    assert(replace(10, 0, 1) == 11);
    assert(replace(2147483647, 7, 8) == 2148483648);
    assert(replace(123, 3, 1) == 121);
    assert(replace(123, 2, 5) == 153);
    assert(replace(123, 1, 2) == 223);
    assert(replace(1233, 3, 1) == 1211);
}