DEV Community

loading...

Diferentes Formas de Expressar em Código

Eduardo Klosowski
Mestre em computação aplicada, programador web com conhecimento de DevOps
Originally published at eduardoklosowski.github.io Updated on ・5 min read

Existem diferentes formas de se programar algo, mesmo quando usa-se a mesma linguagem de programação. Embora esses códigos possam ser considerados equivalentes, uma vez que apresentam o mesmo resultado, eles tem suas particularidades, podendo facilitar ou dificultar sua leitura e manutenção. Usando como exemplo a impressão dos valores de uma lista (ou array) no terminal utilizando a linguagem Rust, pretendo mostrar essas diferenças na prática, de forma que também possa ser abstraído para outras lógicas e linguagens.

Problema proposto

Dado uma lista:

let lista = [1, 6, 3, 8, 4, 3];
Enter fullscreen mode Exit fullscreen mode

Deseja-se imprimir no terminal o índice e valor de cada elemento, conforme a baixo:

0 => 1
1 => 6
2 => 3
3 => 8
4 => 4
5 => 3
Enter fullscreen mode Exit fullscreen mode

Uma forma de se resolver esse problema é iterando sobre os elementos da lista, imprimindo os índices e valores.

Código 1: Acesso direto

Uma primeira forma de resolver esse problema é acessando cada valor diretamente no código. Exemplo:

println!("{} => {}", 0, lista[0]);
println!("{} => {}", 1, lista[1]);
println!("{} => {}", 2, lista[2]);
println!("{} => {}", 3, lista[3]);
println!("{} => {}", 4, lista[4]);
println!("{} => {}", 5, lista[5]);
Enter fullscreen mode Exit fullscreen mode

Esse código é simples e direto, tendo como resultado da sua execução o resultado desejado. Porém apresenta dois pontos principais: ele é fixo para 6 elementos, sendo necessário sua alteração caso a quantidade de elementos da lista seja alterado; também existe uma repetição no código, sendo necessário alterar todos os println!(...) caso deseja-se mudar a saída.

Código 2: while

É possível escrever o código de forma que ele se adapte a quantidade de elementos da lista, sendo necessário uma estrutura de repetição para isso. Exemplo:

let mut i = 0;
while i < lista.len() {
    println!("{} => {}", i, lista[i]);
    i += 1;
}
Enter fullscreen mode Exit fullscreen mode

Esse código utiliza a estrutura de repetição while para melhorar os dois pontos destacados no código anterior, verificando o tamanho da lista em tempo de execução, e evitando a repetição do println!(...). Porém isso vem com o custo adicional de uma variável e alguns ciclos de processamento adicionais para fazer o controle da estrutura de repetição (acessar o tamanho da lista e compará-lo a variável de controle).

Código 3: for

Outra forma possível é a utilização da estrutura de repetição for. Exemplo:

for i in 0..lista.len() {
    println!("{} => {}", i, lista[i]);
}
Enter fullscreen mode Exit fullscreen mode

Esse código, assim como o anterior, necessita de uma variável para o controle da estrutura de repetição. Entretanto, o controle desta variável fica a cargo da linguagem e não do programador, não sendo necessário incrementá-lo manualmente, e fica mais claro no código como a estrutura de repetição está sendo controlada. Um ponto negativo é que não é possível alterar essa variável diretamente, o que possibilitaria imprimir os valores em outra ordem, com alguma lógica mais elaborada, como seria possível no Código 2.

Código 4: Iterando com for

Em Rust é possível iterar diretamente os valores de uma lista utilizando o for. Exemplo:

for &v in lista.iter() {
    println!("_ => {}", v);
}
Enter fullscreen mode Exit fullscreen mode

Embora esse seja o código mais simples usando uma estrutura de repetição, e fica claro no código de que a interação está ocorrendo em cima dos valores da lista, não é possível mostrar o índice do valor no println!(...).

Código 5: Iterando com for e .enumerate()

Para permitir que o índice seja mostrado, é possível enumerar os valores da lista. Desta forma, em vez de iterar nos elementos da lista diretamente, o for itera em tuplas com o valor dado pelo .enumerate() e o elemento da lista. Exemplo:

for (i, &v) in lista.iter().enumerate() {
    println!("{} => {}", i, v);
}
Enter fullscreen mode Exit fullscreen mode

Esse código se assemelha ao anterior em simplificação, porém permitindo que o índice seja impresso no terminal. Embora ele seja bem parecido com o Código 3, seu for deixa claro que a estrutura de repetição está iterando sobre os valores da lista, enquanto o for do Código 3 apenas diz que está sendo iterado sobre seus índices, necessitando verificar o bloco de código do for para entender no que o índice está sendo usado, e necessitando acessar manualmente os valores da lista, o que poderia dificultar a alteração do nome da variável da lista, por exemplo, uma vez que o mesmo precisaria ser alterado em diversos lugares.

Código 6: .for_each()

Rust também permite que o código seja feito utilizando funções em vez de uma estrutura de repetição (embora a estrutura de repetição seja usada internamente por essas funções). Para esse caso foi utilizado a função .for_each() do iterador, que permite chamar uma função passada por argumento para cada valor da lista. Exemplo:

lista
    .iter()
    .enumerate()
    .for_each(|(i, &v)| println!("{} => {}", i, v));
Enter fullscreen mode Exit fullscreen mode

Esse código está dividido em várias linhas apenas para facilitar sua leitura, podendo estar em uma única linha. O compilador do Rust também consegue otimizar esse código deixando-o mais performático que a versão utilizando o for. Porém a versão com for deixa explícito no código a iteração dos elementos da lista, o que poderia facilitar a sua leitura. Nesse código também não é possível utilizar comandos como continue e break para controlar a estrutura de repetição, necessitando utilizar outras funções do iterador para comportamentos distintos.

Considerações

Com exceção do Código 4, todos os outros têm o mesmo resultado no terminal, sendo opções de código viáveis de resolução do problema proposto. Cada código possui suas diferenças, variando em legibilidade e flexibilidade. Pensando em percorrer os elementos da lista para resolver o problema, essa lógica pode ser expressa de diferentes formas, como visto nos diferentes códigos apresentados, assim como em português é possível transmitir uma mesma ideia com diferentes frases.

Abstraindo para um sistema, existem diferentes formas de resolver as pequenas partes do sistema, e muitas vezes essas pequenas partes podem apresentar similaridades. Quando essas partes são resolvidas utilizando a mesma técnica, isso pode facilitar a leitura e manutenção do código, uma vez que fica mais fácil de pressupor o que o código está fazendo conforme o programador se habitua com ele. Assim como, ao se terminar uma alteração, é possível fazer uma análise, verificando se não existe uma melhor forma de expressar a lógica já implementada, deixando o código mais organizado, o que facilitaria a leitura do mesmo.

Discussion (1)

Collapse
tonhocodes profile image
tonhocodes

excelente artigo Eduardo, bons pontos, bem escrito. Parabéns.