5  Comparações, o comando if e recursão

Antes de falar em desvio (if), vamos ver um novo tipo de variável que foi introduzido de forma natural. O tipo booleando, ou seja uma variável que pode valer true (verdadeiro) ou false (falso). O seu uso está intimamente ligado ao if.

Observem os seguintes exemplos:

2 + 2 == 4
true
3 != 8
true
23 < 24
true
42 <= 44
true
42 < 2
false

Vale chamar a atenção, como em linguagens de programação o = é usado para atribuições, para comparações se usa o ==. Da mesma forma o != é usado como diferente. Esses operadores, em conjunto com o <, <=, > e >= nos permitem comparar valores.

Sobre as variáveis booleanas vale também observar o seu tipo. Uma explicação mais aprofundada sobre como essas variáveis funcionam será fornecida quando abordarmos os operadores condicionais:

typeof(2 == 3)
Bool

Finalmente, também podemos negar variáveis booleanas para inverter o seu valor:

!true
!false
true

Nessa aula, vamos aprender um novo comando. O desvio condicional, através dele é possível alterar o fluxo de execução de um programa. Até o momento não tínhamos comentado isso explicitamente, mas a ordem de execução de instruções segue a ordem em que elas estão. Vejamos o exemplo abaixo:

println("Oi")
println("um")
println("dois")
Oi
um
dois

A ordem de impressão será Oi, um e dois.

Da mesma forma não temos problema ao executar o código abaixo.

denominador = 0
denominador += 2
30 / denominador
15.0

Apesar da variável denominador começar inicialmente com 0, antes de se fazer a divisão, ela estará valendo 2.

Como é de se esperar nem sempre queremos que essa ordem seja respeitada. Observe o seguinte exemplo:

pandemia = true
println("Vou sair de casa?")
if pandemia == true
   println("Só vou sair de casa se for essencial")
end
Vou sair de casa?
Só vou sair de casa se for essencial

O exemplo acima é claro, se uma condição for verdadeira, o código que está no escopo do if (isso é entre a condição e o end) será executado.

Um outro exemplo:

denominador = 1
if denominador != 0
   println("sei fazer a divisão se não for por zero")
   println("o resultado da divisão de 30 por ", denominador, " é igual a ", 30/denominador)
end
sei fazer a divisão se não for por zero
o resultado da divisão de 30 por 1 é igual a 30.0

Situações muito comuns em computação devem ser favorecidas pela linguagem, nesse caso do if, é muito comum termos duas ou mais situações. Nesse sentido em Julia podemos também ter alternativas como abaixo:

 pandemia = true
 println("Vou sair de casa?")
 if pandemia == true
    println("Só vou sair de casa se for essencial")
 else
    println("Balada liberada!!")
 end
Vou sair de casa?
Só vou sair de casa se for essencial

No caso de termos mais de uma altenativa, não basta termos só uma condição, nesse caso temos que usar elseif.

pandemia = true
tenhoqueestudar = true
println("Vou sair de casa?")
if pandemia == true
  println("Só vou sair de casa se for essencial")
elseif tenhoqueestudar == true
  println("Melhor ficar em casa")
else
  println("Balada liberada")
end
Vou sair de casa?
Só vou sair de casa se for essencial

Conhecendo o if, agora, escreva uma função que recebe os coeficientes, a, b e c de uma equação de segundo grau e imprime as suas raízes reais.

Sim, a forma de se aprender a programar é programando.

Vamos agora a parte mais importante do curso, lembrando que até o momento aprendemos: - valores - varíaveis e alguns dos seus tipos - alguma funções já prontas como div(), typeof(), parse(), string(), println(), sin(), etc - como fazer as nossas funções com a palavra reservada function e que termina por end - lembrando que a função pode ou não devolver algo através do return - lembrando também que uma função pode chamar outra função - como mudar o fluxo de execução normal com o if, elseif

5.1 Agora sim: Funções que se chamam

Agora podemos, ir ao tópico principal da aula.

Observe a seguinte função imprime().

function imprime()
    println("Mensagem positiva")
    imprime()
end
imprime (generic function with 1 method)

Ao ser chamada, o que acontece? O computador fará chamadas seguidas a função, imprimindo a mensagem, até o momento que ocorra uma limitação de memória. Logo, fazer chamadas onde uma função se chama, sem controle não é uma boa ideia.

Por outro lado, podemos pensar em uma forma de chamada controlada, onde a própria função decide o momento de parar de se chamar. Para isso, vamos pegar uma função matemática bem conhecida, o fatorial.

Sabemos que 5! = 5.4.3.2.1. Mais, ainda dado um número n, sabemos que n! = n.(n - 1)! Continuando, temos que (n - 1)! = (n - 1).(n - 2)! e assim por diante. Para reproduzir isso no computador precisamos saber quando parar. Para isso, podemos usar que o fatorial de zero é 1, ou 0! = 1. Logo já temos a primeira parte da função:

function fatorial(n)
 if n == 0
   return 1
 else
   # o que vamos colocar aqui?
end

No código acima, temos o critério de parada, ou seja quando n for igual a zero, a resposta será 1. Mas, e se n não for zero. Nesse caso, temos que seguir a fórmula da recursão ou seja n.(n - 1)!. Como (n - 1)! pode ser escrito como fatorial(n - 1). Ficamos com a expressão n * fatorial(n - 1).

function fatorial(n)
 # Critério de parada: quando n é igual a 0, a recursão termina
 if n == 0
   return 1
 else
   return n * fatorial(n - 1) # Chamada recursiva
 end
end
fatorial(5)
120

Vamos a um segundo exemplo, a contagem regressiva. Mais uma vez, quando se chega a zero, podemos considerar que a contagem terminou. Além disso, a cada número, o próximo passo é o número menos 1.

function contagem(n)
   if n < 0
       println("Bum!")
   else
       print(n, " ")
       contagem(n - 1)
   end
end
contagem(5)
5 4 3 2 1 0 Bum!

Essa estrutura é bem poderosa, pois permite que operações sejam executadas um número controlado de vezes. Voltando ao countdown, imagine que ao invés de imprimir uma mensagem quiséssemos fazer uma conta com o que será devolvido.

function soma(n)
 if n > 0
   return n + soma(n - 1)
 else
   return 0
 end
end

soma(11)
66

Essa estrutura é bastante poderosa e pode ser usada para o cálculo de produto, nesse caso, a mudança é bem pequena.

Da mesma forma segue um exemplo para o cálculo dos n primeiros elementos da soma hârmonica.

function somaharmonica(atual, n)
 # Caso base: se 'atual' é maior ou igual a 'n'
 if atual >= n
   # Retorna o recíproco de 'atual' (último termo da soma)
   return 1.0 / atual
 else
   # Caso recursivo: soma o recíproco de 'atual' e chama a função para o próximo número
   return 1.0 / atual + somaharmonica(atual + 1, n)
 end
end

somaharmonica(1, 10)
2.9289682539682538