APS2 - Revisão de códigos
Contato
- Jean Paulo Martins (jeanmartins utfpr edu br)
- Sala 105, Bloco S (UTFPR - Campus Pato Branco)
Comparação-plágio: abrir em outra página
Avaliação dos códigos-fonte
Sobre a complexidade da implementação Sobre o uso consistente de formatação
Avalie a complexidade do código abaixo para listas de encadeamento simples e duplo!
void list_print(list* v) {
int cont = 0;
node* aux = v->head;
while(cont++<list_size(v))
{
printf("%d \n", aux->value);
aux = aux->next;
}
}
Com uma simples modificação baixamos a complexidade $O(n^2)$ para $O(n)$, ver abaixo:
void list_print(list* v) {
int size = list_size(v);
node* aux = v->head;
while(size-- > 0) {
printf("%d \n", aux->value);
aux = aux->next;
}
}
Simplificação de código
Observe se o uso de bibliotecas adicionais tornariam o código-fonte mais legível (fácil de compreender)
int list_empty(list* v) {
if(v->head == NULL)
return 1;
else
return 0;
}
O tipo de dados Booleanos é sempre mais indicado para escrita de códigos que envolvam resultados lógicos (verdadeiro/falso). Pois evidenciam o significado do retorno. Todas linguagens de programação “modernas” possuem esse tipo de dados.
#include <stdbool.h>
bool list_empty(list* v) {
return v->head == NULL;
}
Formatação dos comentários
Comentários são úteis e devem ser utilizados, nos ajudam a compreender melhor o código e a relembrar posteriormente. No entanto, eles fazem parte do seu código-fonte e devem ser escritos/formatados corretamente.
int** new_matrix(int m, int n)
{
int i,j;
int **Matrix=(int**)malloc(m*sizeof(int*));//nesse passo eu aloquei um vetor de ponteiros.
for(i=0;i<m;i++)//percorre as linhas.
{
Matrix[i]=(int*)malloc(n*sizeof(int));//aloca um vetor de inteiros para a coluna.
}
return Matrix;//Retorna o ponteiro para a matriz alocada.
}
A legibilidade é afetada pelo excesso de espaços e pelo fato dos comentários terem sido inseridos após o fim da linha. Se o comentário é extenso o indicado é escrevê-lo antes da operação.
O espaçamento entre os operadores também deixa o código mais organizado e fácil de ler.
Evite declarar índices no ínicio da função, isso pode ser feito na própria definição do loop.
int** new_matrix(int m, int n) {
//nesse passo eu aloquei um vetor de ponteiros.
int **Matrix = (int**) malloc (m * sizeof(int*));
for(int i = 0; i < m; i++) {
//aloca um vetor de inteiros para a coluna.
Matrix[i] = (int*) malloc(n * sizeof(int));
}
//Retorna o ponteiro para a matriz alocada.
return Matrix;
}
Comentários para funções
void list_push_front(list* v, Type value)
{//Insere no inicio.
list_insert(v,value,0);
}
Ao comentarmos a definição de uma função é usual utilizarmos comentários de múltiplas linhas e mais descritivos.
/*
Insere um novo nó de conteúdo `value` no início da lista.
*/
void list_push_front(list* v, Type value) {
list_insert(v,value,0);
}
Indentação
Seja consistente com sua indentação. Se utiliza 4 espaços, 8 espaços ou tabulação, isso não é importante. Porém, utilize sempre o mesmo padrão em todo o seu arquivo.
Observe o tipo de retorno da sua função, se ela sempre retorna NULL, não há porque retornar algo.
int**free_matrix (int m, int n, int **v)
{
int i;
if (v == NULL) return (NULL);
if (m < 1 || n < 1) {
printf (" erro");
return (v);
}
for (i=0; i<m; i++)
free (v[i]);
free (v);
return (NULL);
}
void free_matrix (int m, int n, int **v) {
if (v != NULL) {
for (int i = 0; i < m; i++)
free (v[i]);
free (v);
}
}
Espaços fazem parte da linguagem e devem ser utilizados apropriadamente
node* iterate_to(list*v,int pos){
assert(pos>0&&pos<=list_size(v));
int indice = 0;
node*ptr=v->head;
while(indice++!=pos-1)
ptr=ptr->next;
return ptr;
}
O código acima se torna muito mais organizado e legível com a formatação apropriada (observe o assert
)
node* iterate_to(list*v,int pos) {
assert(pos > 0 && pos <= list_size(v));
int indice = 0;
node*ptr=v->head;
while(indice++ != pos-1)
ptr=ptr->next;
return ptr;
}
Valores lógicos raramente precisam ser explicitados em comparações
Type list_pop_front(list* v){
assert(list_empty(v)!=true);
//...
Em casos como esse, o true
pode ser ignorado sem prejuízo à interpretação do texto.
Type list_pop_front(list* v) {
assert(list_empty(v));
//...
O mesmo acontece no trecho abaixo:
if(list_empty(v) == true){
Que se mantém claro se escrito como “Se a lista v está vazia”
if(list_empty(v)) {
Separe/modularize funcionalidades de uso recorrente!
A funcionalidade iterate_to
foi definida para que fique claro a lógica que envolve inserção/remoção de nós. Sem que essa lógica se confunda com a lógica de programação para percorrer a lista.
Type list_erase(list* v, int i){
Type valor;
if(i<=0)
{
list_pop_front(v);
}
else if(i>=(list_size(v)-1))
{
list_pop_back(v);
}
else
{
int j=0;
node* a=v->head;
node* e;
while(j!=(i-1))
{
a=a->next;
j++;
}
e=a->next;
valor=e->value;
a->next=e->next;
free(e);
}
return(valor);
}
Vejamos a simplificação do código acima adaptado para utilizar iterate_to
.
Type list_erase(list* v, int i) {
Type valor;
if(i < = 0)
list_pop_front(v);
else if(i >= list_size(v) - 1)
list_pop_back(v);
else {
node* a = iterate_to(v, i);
node* e = a->next;
valor = e->value;
a->next = e->next;
free(e);
}
return(valor);
}
Para simplificação do código, evite redundâncias
int list_size(list* v)
{
int i=0;
if(v->head!=NULL)
{
node* b=v->head;
for(i=1;b->next!=NULL;i++)
{
b=b->next;
}
}
return i;
}
A função acima funcionará normalmente se removermos o if
inicial.
int list_size(list* v) {
int size = 0;
node* b = v->head;
for(; b != NULL; size++)
b = b->next;
return size;
}