Fors, ifs, whiles

Julia Para Economistas

Nessa página nós vamos tratar de controle de fluxo. Esses são comandos essenciais para construir algoritmos númericos e centrais em qualquer código. Vamos tratar dos três mais comuns: if, for e while

Como ler essa seção

Esse capítulo segue a seguinte estrutura: eu apresento os comandos secamente no começo de cada seção: o objetivo é que aqueles que já sabem o que ifs, fors e whiles fazem possam entender a sintaxe do Julia. Ainda assim, eu sugiro uma leitura, ainda que diagonal. Depois, eu discuto o que cada comando faz com detalhes. A última seção apresenta alguns exemplos, e são de interesse geral.

If

A sintaxe para o If é:

if condition
  alguma coisa
else
  outra coisa
end

If s são blocos que permitem que você teste uma condição e execute um comando condicional àquele condição ser atendida. Caso contrário (else) outro comando pode ser executado. Um exemplo simples é testar se um número é positivo:

if numero > 0
    println("Positivo")
  else
    println("Negativo")
  end

Veja que se você não definir antes a variável numero, o Julia vai dar um erro porque a variável não está definida. Por sinal, isso também introduz a função println, que "imprime" (escreve) coisas direto no console, pulando uma linha.

Podemos estabelecer numero = 3 e a rotina acima deve retornar um positivo.

Obviamente, neste exato momento, como 0=00 = 0, o comando vai testar como falso e vai retornar que zero é negativo! Nós temos duas soluções possíveis:

  1. Trocar o >> por >=. Agora testamos se 0 é maior ou igual que zero; se sim, ele retorna "positivo". Caso contrário, negativo.

  2. Nós podemos querer que, quando o número for zero, ele retorne "zero"

No segundo caso nós teríamos que adicionar mais um teste. Para adicionar um else seguido de um novo teste lógico, nós usamos o elseif, tudo junto. Modificando o exemplo, nós teríamos:

if numero > 0
    println("Positivo")
  elseif numero == 0
    println("Zero")
  else
    println("Negativo")
  end

Nós poderíamos amarrar isso em uma função para permitir o teste ser realziado para qualquer número repetidas vezes. Faremos isso mais para frente.

For

A sintaxe para o for é:

for i in 1:numero
  operações
end

Ou:

for i in vetor
  operações
end

O forpermite repetir algum conjunto de operações um número de vezes - por isso ele é conhecido como um loop. Um exemplo bobo e simples é tirar a raiz quadrada de todos os número de um vetor de 1 a 10:

bb = zeros(10)
  for i in 1:10
    bb[i] = sqrt(i)
  end

A cada etapa i, o Julia tira a raiz de i sqrt(i) e associado o valor a posição i do vetor bb. Veja que uma solução muito mais simples para o mesmo problema seria fazer bb = sqrt.(1:10), usando a discussão anterior de vetorizar funções e gerar sequências.

Nosso exemplo acima é extremamente simples para ser ilustrativo: o for é realmente útil, especialmente em simulações.

While

(O While exige o uso da keyword global, então leia a seção mesmo que en passant)

O While é, como o for, um loop. A diferença é que enquanto o for repete a mesma operação nn vezes, o while repete uma operação até uma certa condição passar a ser falsa. Poderíamos repetir o mesmo exemplo acima usando o while:

i = 1

bb = zeros(10)

while i <= 10
  bb[i] = sqrt(i)
  global i = i + 1
end
Veja que isso involve várias linhas de código a mais que o for. A utilidade do while vai ficar clara mais abaixo. Vamos focar primeiro na estrutura do problema.

Primeiro, temos que declarar o valor da variável que indexa o loop antes do while. Na declaração do while, colocamos a condição que deve ser atentida para o programa se repetir - nesse caso, i deve ser menor ou igual a 10.

No final do loop, precisamos acrescentar + 1 na variável i, coisa que o for faz "automaticamente" sem a gente ver. Esquecer esse passo é uma ótima maneira de fazer um programa que nunca para de rodar (teste rodar o while sem essa linha). Veja que antes do i eu usei um global. Isso se deve a maneira como o Julia interpreta as coisas: variáveis dentro de um loop são "locais" (pertencem ao loop) exceto se denotadas como global OU se foram previamente declaradas. Por exemplo, fazer:

for i in 1:10
   a = i
 end

 a
Deve retornar um erro dizendo que a variável a não existe. Entretanto,

for i in 1:10
   global a = i
 end

 a
Deve retornar 10. Algumas linguagens, como o R, não fazem essa diferenciação, o que pode ser positivo (reduz a quantidade de coisas que entram no código) ou negativo (um loop dentro de outro loop dentro de um terceiro loop muda uma variável que você deu o mesmo nome duas vezes)1. O que acontece quando nós definimos variáveis dentro de loops que por sua vez são passados para loops dentro deste loop? A próxima seção trata disso.

Não parece óbivo porque usar o while quando isso requer pelo menos duas linhas de código a mais (além das altas chances de você esquecer a etapa da soma 1). A justificativa é muito simples: em muitos algoritmos queremos repetir a operação até uma certa condição ser satisfeita. Por exemplo, poderíamos buscar o equilíbrio de um mercado da seguinte maneira: chute um preço inicial e compute a demanda e a oferta. Se a oferta for maior que a demanda, reduza o preço em x. Caso contrário, aumente em x. Faça isso até a diferença entre oferta e demanda ser pequena. Este passo final é facilmente implementável em um loop: while diff > 0.000000001 faria o truque.

Loops dentro de Loops e variáveis globais*

Uma pergunta honesta é como o Julia lida com loops que tem outros loops dentro - especialmente se for um while dentro de outro. Nós temos que declarar os dois índices como globais?

Não. O loop de dentro não precisa ser declarado como uma variável global. O seguinte exemplo funciona:

j = 1

while j <= 100
  i = 1
  while i <= 100
    operações
    i = i + 1
  end
  global j = j + 1
end

Veja que, na linha i= i+1, se tivessemos usado global, o Julia nos devolveria um erro acusando que i não é uma variável global. De fato, ela está definida dentro do while, e por isso não é global.

1O R tem uma maneira própria de lidar com esse tipo de coisa, via ambientes. Isso impede que dois objetos com o mesmo nome dentro de dois pacotes diferentes entrem em colisão.