terça-feira, 2 de agosto de 2011

Laços


Ao longo deste blog, que já deve contar com mais de 10 artigos, falei algumas vezes sobre laços de repetição. Mas que diabos seria isto? Pense num laço, em questões de montaria. O cavaleiro faz um laço de corda para envolver qualquer que seja o objeto mirado, ou seja, figurativamente, uma prisão, armadilha ou cativeiro. Em programação, temos mais ou menos esta ideia para laços. Um local onde o código fica aprisionado, rodando dependentemente de alguma condição imposta. Por repetição se entende que o código delimitado será executado tantas vezes quanto forem definidas. Ou seja, ele ficará lá repetindo até chegar a uma condição estabelecida.
Temos três maneiras formais de se delimitar laços de repetição. Existem outras, que falarei ao final deste artigo, mas não são consideradas verdadeiros laços de repetição, e sim, estruturas de dados que simulam este artifício. O primeiro e mais elementar, é o laço Enquanto. Ele simplesmente conta com uma condição que, ao ser respeitada, os comandos que estão dentro do bloco serão executados. Quando a condição não for respeitada, passa-se o laço e a execução vai diretamente para a linha posterior ao fechamento do bloco, deixando o laço para trás. É bem útil, mas geralmente exige mais que uma linha de código no bloco. Vamos fazer um exercício que perdure para todos os comandos deste artigo: calcular o Fatorial de um número. Utilizarei as linguagens Java e VB.Net, apenas para notar as diferenças entre elas.
public static double Fatorial(int n){
 int c = n;
 while(c>1){
  c--;
  n *= c;
 }
 return n;
}
Public Function Fatorial(ByVal n As Integer) As Double
 Dim c = n 'não precisamos definir o tipo, quando já igualamos a outra variável.
 While c > 1
  c = c - 1
  n = n * c
 End While
 Return n
End Function
Bem simples, não é? Declara uma variável "c" que é igual ao número informado. Enquanto c for maior que 1 (ao se multiplicar por 0, o resultado se anula), c é diminuído em 1 e o n pega o valor do resultado dele mesmo multiplicado por c (x *= y significa x = x * y). Perceba que o retorno não se repete junto com o laço, pois está fora do escopo (parte do código que está dentro de um bloco delimitado por chaves, que não pode ser acessado por outras partes do código). Um bug notável neste código (bug = defeito, falha, erro) aparece quando o número fornecido for 0. O resultado retornado é 0. Sabemos que nenhum fatorial dá 0, porque zero não tem número natural antes de si mesmo, portanto, o fatorial de 0 é 1, pois nenhum número multiplicado por outro dá 1, a não ser ele mesmo. Uma solução seria colocar uma condição, como:
if(n == 0) return 1;
//lembrando que não precisamos abrir o bloco de chaves quando só há uma instrução. Isto serve também para laços.
Outra forma, bem semelhante, mas sutilmente diferente no conceito é o laço Faça - Enquanto:
public static double Fatorial(int n){
 if(n == 0) return 1; //debugador
 int c = n;
 do{
  c--;
  n *= c;
 }while(c>1)
 return n;
}
Public Function Fatorial(ByVal n As Integer) As Double
 If n = 0 Then
  Return 1
 End If 'sim, temos que abrir e fechar o bloco em VB.Net :(
 Dim c = n
 Do
  c = c - 1
  n = n * c
 While n > 1
 Return n
End Function
Este laço tem a diferença de que, a verificação, ao invés de ser feita na volta do código, é feita no final, podendo representar uma melhoria (embora não significativa) na performance, sem ter que contar com o desvio para a volta do laço.
Existe mais um tipo de laço que consegue reunir muitas características dos outros dois, além de ser mais funcional e contar com um inicializador, uma comparação e um incrementador próprio. É o laço For:
public static double Fatorial(int n){
 if(n == 0) return 1; //debugador
 for(int c = n;c>1;c--) //sem abrir bloco, pois só há uma instrução
  n *= c;
 return n;
}
Public Function Fatorial(ByVal n As Integer) As Double
 If n = 0 Then
  Return 1
 End If 'sim, temos que abrir e fechar o bloco em VB.Net :(
 For c = n To 2 Step -1
  n = n * c
 Next
 Return n
End Function
Este laço é praticamente uma função inteira dentro de outra. Conta com um escopo de declaração de variável, sendo que a variável declarada dentro dele (int c = n) só pode ser usada DENTRO DELE. Se for tentar usar o c fora do laço, vai dar erro de compilação, pois o c simplesmente não vai existir. A comparação é igual a do while em Java, mas em VB, há a diferença de que o For é um contador, e não um comparador. Você declara uma variável e ele vai incrementando ou decrementando até (To) o valor especificado. Para definir se queremos incrementar ou decrementar a variável, colocamos o passo, que no Java é representado pelo "c--" (pode ser c++ [sendo assim, podendo ser ocultado] ou então c+=2, qualquer numero para incrementar / decrementar) e no VB é representado pelo Step (que pode ser positivo ou negativo. Quando queremos Step 1, podemos ocultar o Step que o VB reconhece automaticamente o passo 1).
Temos duas formas de fazer algo parecido com os laços de repetição, embora não sejam reais laços. Temos o desvio incondicional e uma verdadeira obra-prima da programação, a recursão, que nada mais é do que um aproveitamento de regras matemáticas.
Primeiramente com o Desvio Incondicional (não existe em Java):
Public Function FatorialDI(ByVal n As Integer) As Double
 If n = 0 Then
  Return 1
 End If 'sim, temos que abrir e fechar o bloco em VB.Net :(
 c = n
 inicio:
  n = n * c
  c = c - 1
 If(c > 2) GoTo inicio
 Return n
End Function
O grande truque é o GoTo, que existe em C, C++, C#, VB, e em muitas outras linguagens, mas não em Java :(
Ele define um rótulo (neste caso, "inicio", mas poderia ser qualquer palavra) que pode ser acessado por meio da palavra-chave GoTo, ou seja, desviando o fluxo natural do código para onde o programador quiser, quando ele quiser, ou seja, incondicionalmente. Pode-se acrescentar uma condição como foi feita neste trecho, mas o comando GoTo realmente, não possui condição, assim como os laços de repetição.
Agora, a chave de ouro para este artigo. A recursão utiliza uma regra matemática um tanto confusa para quem observa de primeira, mas muito útil e economiza várias linhas de código, apesar de forçar o trabalho mental do programador. Esta regra utiliza a chamada de uma função dentro dela mesma, ou seja, o resultado retornado pela função é usada dentro dela mesma, gerando uma pseudo-repetição que, se não for propriamente controlada, pode durar infinitamente. A recursão não utiliza nenhuma palavra-chave nova, apenas é um conceito utilizado por programadores muito seguros no que fazem. Mas bem útil.
public static double FatorialR(int n){
 if(n <= 1) return 1; //debugador
 return n * FatorialR(n--);
}
Public Function FatorialR(ByVal n As Integer) As Double
 If n <= 1 Then
  Return 1
 End If 'sim, temos que abrir e fechar o bloco em VB.Net :(
 Return n * FatorialR(n-1)
End Function
Para entender este código, leva um tempinho. Primeiramente, mudei o debugador para se adequar às condições da recursão. Agora, se n for menor ou igual a 1 (ou 0 ou 1) vai retornar 1. Se passar desta condição, ele retorna o valor de n multiplicado pelo retorno da própria função com argumento de n menos um! Ou seja, o n vai ser cada vez menor, até bater de frente com o debugador, que vai retornar 1. Simplificando, o return iria ficar assim: return n * n-1 * n-2 * n-3 ... * 1 Justamente porque ele percorre a função e encontra sua própria chamada, aí tem que executar a função de novo, até encontrar uma condição que faça a repetição parar, para não cair em looping infinito! Como eu falei, bem confusa, mas bem útil. Viram como o código fica mais enxuto, com menos linhas? Apenas duas linhas na função inteira em Java e quatro em VB! Agora você pensa que não dá pra diminuir mais ainda as linhas? Dá pra fazer ficar com apenas UMA linha em ambas as linguagens:
public static double FatorialR(int n){
 return (n <= 1) ? 1 : n * FatorialR(n--);
}
Public Function FatorialR(ByVal n As Integer) As Double
 Return IIf(n <= 1, 1, n * FatorialR(n-1));
End Function
Este é o chamado operador ternário (em Java) que faz uma pergunta (valor_booleano ?) e retorna o primeiro argumento se o valor for verdadeiro ou o segundo se for falso. Sintaxe:

(<expressao_booleano> ? <valor_verdadeiro> : <valor_falso>)

Em VB, há uma função que faz essa decisão por nós, que tem uma sintaxe parecida e retorna ou o valor verdadeiro ou o valor falso, a IIf:

IIf(<expressao_booleano>, <valor_verdadeiro>, <valor_falso>)

Bem, é isso, gente. Acabei escrevendo bem mais do que eu planejava. Até a próxima!

Nenhum comentário:

Postar um comentário

Codifique tua mensagem aí: