Aprenda Programação: Estruturas Condicionais (ou Estruturas de Condição)
Créditos para a imagem: Imagem criada pelo autor usando o programa Spectacle.
Pré-Requisitos
Na introdução sobre ambientes de desenvolvimento, indiquei Python, Lua e JavaScript como boas escolhas de linguagens de programação para iniciantes. Posteriormente, comentei sobre GDScript como opção para pessoas que tenham interesse em programar jogos digitais ou simulações. Para as atividades de introdução a programação, você precisará de, no mínimo, um ambiente de desenvolvimento configurado em uma das linguagens anteriores.
Caso queira experimentar programação sem configurar um ambiente, você pode usar um dos editores online que criei:
Contudo, eles não possuem todos os recursos dos interpretadores para as linguagens. Assim, cedo ou tarde, você precisará configurar um ambiente de desenvolvimento. Caso precise configurar um, confira os recursos a seguir.
Assim, se você tem um Ambiente Integrado de Desenvolvimento (em inglês, Integrated Development Environment ou IDE) ou a combinação de editor de texto com interpretador, você está pronto para começar. Os exemplos assumem que você saiba executar código em sua linguagem escolhida, como apresentado nas páginas de configuração.
Caso queira usar outra linguagem, a introdução provê links para configuração de ambientes para as linguagens C, C++, Java, LISP, Prolog e SQL (com SQLite). Em muitas linguagens, basta seguir os modelos da seção de experimentação para adaptar sintaxe, comandos e funções dos trechos de código. C e C++ são exceções, por requerem o uso de ponteiros para acesso à memória.
Fluxo de Execução
Programas iniciam-se em um ponto de entrada e terminam em um ponto de saída. Todos os programas escritos até este momento iniciam-se de um ponto de entrada e terminam em mesmo ponto de saída, executando todas as instruções fornecidas entre os dois pontos na ordem definida.
Em programas complexos, isso nem sempre acontece. Na realidade, é bastante comum escolher uma dentre duas ou mais alternativas para executar o trecho de código mais apropriado de acordo com os valores atuais do programa. Em outras palavras, o estado de um programa pode afetar a execução.
O termo desvio (branch) refere-se à possibilidade de modificar a seqüência de instruções por meio de um salto (jump) para uma linha de código potencialmente diferente da próxima.
Saltos para desvios podem ser incondicionais (por exemplo, usando go to
) ou condicionais.
Um salto incondicional ocorre sempre; um salto condicional depende de uma condição definida no código-fonte. Com combinações de variáveis com operações aritméticas, relacionais e lógicas, é possível criar fluxos alternativos para a execução de um programa.
A partir deste momento, convém começar a imaginar o código-fonte de um programa como composto por blocos. Assim como uma edificação possui cômodos (como salas, cozinhas, corredores...), programas possuem blocos de código-fonte.
Para se deslocar de de ponto inicial até um ponto final em uma casa, percorre-se uma seqüência de cômodos. Nem sempre se visita todos os cômodos de uma casa; visita-se apenas os necessários para se atingir o destino.
Programas funcionam de forma semelhante. De acordo com os dados fornecidos, pode-se executar (ou não) diferentes partes do código-fonte definido. O termo fluxo de execução refere-se à ordem na qual a máquina executa as instruções de um programa.
Programação Estruturada
Programas, assim como edificações, possuem estruturas.
Em programação, um modelo (paradigma) tradicional de programação chama-se programação estruturada. Um resultado do teorema do programa estruturado é a possibilidade de construir quaisquer programas por meio de três padrões: seqüência, seleção e iteração.
Até este momento, construiu-se todo programa por meio de seqüências de instruções. Seqüências são executadas na ordem definida. Elas podem ser instruções (em nível mais baixo) ou subprogramas (em nível mais alto). Seqüências como subprogramas significam que é possível construir seqüências compostas por seleções e iterações.
Seleção permite escolher um dentre dois caminhos alternativos para seguir, baseando-se em um valor lógico para a decisão. Em outras palavras, pode-se criar condições usando-se operadores relacionais e/ou lógicos para definir a próxima instrução do programa. Dependendo do valor lógico, a próxima instrução executada pode ser definida por um salto, ao invés de ser a linha seguinte de código.
Iterações são repetições de instruções existentes. A iteração combina seleções com saltos para um bloco de instruções que se pode repetir zero ou mais vezes (ou uma ou mais vezes, dependendo da estruturação). A combinação permite repetir uma mesma seqüencia mediante uma condição. Enquanto a condição for verdadeira, repete-se o código. Quanto a condição tornar-se falsa, salta-se para uma seqüencia diferente de instruções. Caso a condição nunca se torne falsa, o programa repete-se infinitamente (algo que será chamado de laço (loop) infinito quando da introdução de estruturas de repetições).
Em linguagens de programação modernas, seleções são abstraídas por estruturas condicionais (ou estruturas de condição). Iterações são abstraídas por estruturas de repetição, também chamadas de laços (loops). Também é possível realizar iterações por meio de subrotinas recursivas.
Como iterações dependem de seleções, convém considerar estruturas condicionais primeiro.
Estruturas Condicionais
Em linguagens de programação modernas, seleções são comumente implementas usando estruturas condicionais. A estrutura abstrai o salto, permitindo à programadora ou ao programador pensar em alto nível enquanto programa.
Embora existam diferentes estruturas condicionais, duas são mais comuns em linguagens de programação modernas:
- Se, Então, Senão, que será doravante chamada de
se
ouif
; - Escolha... Caso, que será doravante chamada de
escolha
ouswitch
.
A disponibilidade de estruturas varia de linguagem para linguagem, embora praticamente toda linguagem forneça uma estrutura if
.
Caso o conteúdo das próximas seções pareça muito abstrato, as seções com fluxogramas e linguagens de programação visual podem ser úteis para uma visualização de estruturas condicionais.
Se X, Então Y
Talvez a estrutura de condição mais tradicional, se
ou if
possui o seguinte formato:
se (condição) então
início
// Bloco para executar caso a condição seja verdadeira
// ...
fim
// Código continua...
Caso condição
seja Verdadeiro
, o bloco de código compreendido entre início
e fim
é executado.
No exemplo, o bloco corresponde aos comentários das linhas 3 e 4.
Quando o bloco termina, o código continua na linha seguinte após fim
(linha 6, que, no caso, é vazia).
Por outro lado, caso condição
seja Falso
, o bloco de código é ignorado.
O programa continua imediatamente após o bloco (ou seja, na linha 6).
Para facilitar a leitura de instruções dentro do bloco, o espaçamento para criar um novo nível visual chama-se indentação. Ela pode ser feita com espaços ou com tabulações (tabs). Em algumas linguagens (como Python e GDScript), é obrigatório utilizar indentação correta para definir blocos. Em outras, como JavaScript e Lua, símbolos como chaves ou palavras reservadas definem blocos, tornando o uso de indentação opcional (embora eu recomende por facilitar a leitura).
Para um primeiro exemplo interativo, você pode usar o interpretador para JavaScript embutido em seu navegador (atalho para console/terminal: F12
).
let condicao = true
if (condicao)
{
console.log("Condição verdadeira, linha executada.")
}
console.log("Linha sempre executada.")
No código JavaScript, pode-se pensar em {
como início
e }
como fim
.
A saída será:
Condição verdadeira, linha executada.
Linha sempre executada.
Em seguida, modifique o código:
// Variável declarada no exemplo anterior.
condicao = false
if (condicao)
{
console.log("Condição verdadeira, linha executada.")
}
console.log("Linha sempre executada.")
A saída será:
Condição verdadeira, linha executada.
Linha sempre executada.
O uso de se
permite, portanto, executar ou ignorar uma linha de acordo com um valor lógico fornecido como condição.
No exemplo, a variável condicao
armazena o resultado.
Você poderia alterar a condição para qualquer outra que seja pertinente para a solução de seu problema.
Inclusive, ela não precisa estar em uma variável; basta que o resultado possa ser verificado como um valor do tipo lógico.
Por exemplo:
let nome = prompt("Qual é o seu nome?")
console.log("Olá, ", nome, "!")
if (nome === "Franco")
{
console.log("Meu nome também é Franco.")
}
Se X, Então Y, Senão Z
O que fazer quando se quiser tratar a condição contrária?
Uma opção é inverter o valor comparado com o operador não
.
se (não condição) então
início
// Bloco para executar caso a condição seja falsa.
// não Falso é verdadeiro, então o bloco seria executado.
// ...
fim
// Código continua...
Contudo, ela apresenta a mesma limitação de poder tratar apenas um caso.
Uma outra possibilidade seria combinar o código original com a primeira alternativa.
se (condição) então
início
// Bloco para executar caso a condição seja verdadeira
// ...
fim
se (não condição) então
início
// Bloco para executar caso a condição seja falsa.
// ...
fim
// Código continua...
Como tratar ambos os casos de uma condição é algo freqüente em programação, linguagens de programação comumente definem um complemento para se
chamado senão
.
Enquanto se
trata o caso em que o resultado seja Verdadeiro
, senão
permite tratar o caso em que o resultado seja Falso
.
se (condição) então
início se
// Bloco para executar caso a condição seja verdadeira
// ...
fim se
senão
início senão
// Bloco para executar caso a condição seja falsa.
// ...
fim senão
fim
// Código continua...
Agora, caso condição seja Verdadeiro
, executa-se o bloco de código compreendido entre início se
e fim se
(linhas 2--5).
Caso contrário, o programa executará o bloco de código compreendido entre início senão
e fim senão
(linhas 7--10).
Ambos os casos são mutuamente exclusivos, ou seja, a execução de um inibe a execução do outro.
Em outras palavras, apenas um dos blocos será executado para o resultado de uma dada condição.
let seu_nome = prompt("Qual é o seu nome?")
console.log("Olá, ", seu_nome, "!")
if (seu_nome === "Franco")
{
console.log("Meu nome também é Franco.")
}
else
{
console.log("Seu nome é interessante.")
console.log("Meu nome é Franco.")
}
console.log("Tchau, ", seu_nome, "!")
Estilos para Formatação de Código-Fonte: Chaves e Indentação
Em linguagens de programação como C, C++, Java e JavaScript, pode-se definir blocos para escopos de estruturas usando chaves.
Existem alguns estilos tradicionais para orientar a colocação de chaves e indentação em blocos. Para uma lista, pode-se consultar páginas como indentation style na Wikipedia.
Alguns dos mais tradicionais incluem:
- Allman:
if (true) { console.log("Allman") } else { console.log("...") }
- K&R:
if (true) { console.log("K&R") } else { console.log("...") }
- GNU:
if (true) { console.log("GNU") } else { console.log("...") }
- Whitesmiths:
if (true) { console.log("Whitesmiths") } else { console.log("...") }
- LISP:
if (true) {console.log("Whitesmiths")} else {console.log("...")}
Os nomes são dos autores, instituições ou linguagens de programação que popularizam o estilo. Para ser mais preciso, alguns autores (como K&R) omitem o uso de chaves em alguns casos, algo que eu pessoalmente não gosto e não recomende (os motivos serão explicados na próxima subseção).
Você pode escolher o estilo que preferir. O mais importante é manter a consistência para criar um padrão ao longo do projeto. Em outras palavras, evite misturar estilos.
Quando há opção de escolha, eu prefiro o estilo Allman como primeira opção; K&R como segunda. A exceção é para JavaScript. Em JavaScript, considero o estilo K&R preferível, pois existem casos em que o interpretador gera código diferente para chaves iniciado em uma nova linha. Ao invés de memorizar exceções, prefiro adotar um estilo consistente para o projeto.
Assim, os demais exemplos da página para JavaScript utilizarão o estilo K&R.
Omissão de Chaves para Expressão em Única Linha
Como adiantado, existem autores que omitem o uso de chaves para blocos em algumas situações. Isso ocorre porque, caso a estrutura condicional tenha uma expressão em uma única linha, pode-se omitir o uso de chaves.
if (true)
console.log("1")
if (true) {
console.log("1")
}
Ambos os usos anteriores são equivalentes. Caso a estrutura condicional tenha mais de uma expressão, deve-se usar chaves.
if (true) {
console.log("1")
console.log("2")
}
Caso contrário, a regra de uma única linha de código continua válida, gerando código potencialmente diferente do esperado.
// Problema.
if (false)
console.log("1")
console.log("Sempre executado.")
// O código anterior equivale a:
if (false) {
console.log("1")
}
console.log("Sempre executado.")
Caso se omita chaves, algumas situações podem ficar confusas e levar a erros, caso não se tome cuidado. Por exemplo:
if (true)
console.log("1")
if (true)
console.log("2")
else console.log("3")
console.log("4")
O exemplo anterior equivalente ao seguinte código:
if (true) {
console.log("1")
}
if (true) {
console.log("2")
} else {
console.log("3")
}
console.log("4")
Indentação incorreta pode levar a interpretações equivocadas do código. Portanto, em linguagens que utilizem chaves para blocos (como JavaScript), a indentação é irrelevante para definição da estrutura. O importante são as chaves (ou a omissão delas).
Particularmente, eu prefiro sempre usar chaves. Além de explicitar o bloco de código, o uso evitar algumas situações que podem ser confusas, como a anterior.
O próximo bloco apresenta outro exemplo que requer atenção caso se omita chaves.
if (true)
if (false)
console.log("1")
else console.log("2")
Em particular, todo senão
faz par com o se
mais próximo (ainda sem um respectivo senão
).
A regra para única linha continua valendo.
Assim, no exemplo anterior, o else
pareia o if
da linha 2.
Além disso, modificações de código sem chaves requerem atenção especial.
O que ocorre caso se adicione uma nova linha entre o if
e o else
?
if (true)
if (false)
console.log("1")
console.log("1.1")
else console.log("2")
Com sorte, o resultado seria erro de sintaxe. Com azar, em alguns casos, ele resultaria modificação não intencional de estrutura:
if (true) {
if (false) {
console.log("1")
}
console.log("1.1")
} else { console.log("2")
}
Portanto, eu prefiro sempre definir explicitamente blocos usando chaves.
Se Aninhado
É possível adicionar estruturas condicionais dentro de outras estruturas condicionais. Esse tipo de uso chama-se aninhamento.
let numero = parseInt(prompt("Digite um número inteiro"))
if (numero > 0) {
console.log("O número é positivo.")
if (numero < 10) { console.log("O número é menor que 10.") } else { if (numero === 10) { console.log("O número é igual a 10.") } else { console.log("O número é maior que 10.") } }} else {
if (numero < 0) {
console.log("O número é negativo.")
} else {
console.log("Zero.")
}
}
Existem situações em que o uso de aninhamentos define condições mutuamente exclusivas. As partes destacadas nas linhas 4--12 são exemplos de condições mutuamente exclusivas (um número possui o valor do próprio número).
Para facilitar a escrita e a leitura do código, algumas linguagens definem uma palavra-chave diferente para marcar o uso de aninhamento para condições mutuamente exclusivas, como elseif
ou elif
.
Em linguagens que não possuam (como JavaScript), ainda é possível modificar o código para torná-lo mais legível.
let numero = parseInt(prompt("Digite um número inteiro"))
if (numero > 0) {
console.log("O número é positivo.")
if (numero < 10) { console.log("O número é menor que 10.") } else if (numero === 10) { console.log("O número é igual a 10.") } else { console.log("O número é maior que 10.") }} else {
if (numero < 0) {
console.log("O número é negativo.")
} else {
console.log("Zero.")
}
}
Como o último else
faz par ao if
mais próximo, a implementação é equivalente para casos mutuamente exclusivos.
Se Ternário
Algumas linguagens de programação fornecem um operador para o comando se
chamado de se ternário
(ternary if) ou condicional ternário
(ternary conditional).
Ele normalmente é escrito na forma condição ? (resultadoParaVerdadeiro) : (resultadoParaFalso)
.
No operador, ?
corresponde à parte do então
, enquanto :
corresponde à parte do senão
.
Ambos são necessários.
Os resultados para ambas as partes devem ser uma expressão que retorna um valor como resultado. Por exemplo, para JavaScript:
let seu_nome_novamente = prompt("Qual é o seu nome?")
console.log("Olá, ", seu_nome_novamente, "!")
let mensagem = (seu_nome_novamente === "Franco")
? "Meu nome também é Franco."
: "Seu nome é interessante.\nMeu nome é Franco."
console.log(mensagem)
console.log("Tchau, ", seu_nome_novamente, "!")
Para facilitar a identificação, os resultados para cada parte estão em uma nova linha. O uso de parênteses na condição também é opcional, embora eu particularmente prefira usá-los, por julgar que torne o código mais fácil de ler. De qualquer forma, também seria válido escrever toda a expressão em uma única linha e sem os parênteses para a condição:
mensagem = seu_nome_novamente === "Franco" ? "Meu nome também é Franco." : "Seu nome é interessante.\nMeu nome é Franco."
console.log("Tchau, ", seu_nome_novamente, "!")
Também é possível aninhar o operador ternário.
let um_numero = parseInt(prompt("Escolha um número."))
let mensagem = (um_numero > 0)
? "O número é positivo."
: (um_numero < 0)
? "O número é negativo."
: "O número é zero."
console.log(mensagem)
Se versus Se Ternário
Você pode estar imaginando quando usar o comando se
e quando usar o se ternário
.
O comando se
sempre pode ser usado.
O operador se ternário
é útil para a escrita de código em estilo funcional (ao invés de procedural).
Neste momento, uma limitação do se ternário
é a obrigatoriedade do resultado ser uma única expressão.
Após a introdução de subrotinas, pode-se criar uma função para remover a limitação.
No mais, você pode escolher o que preferir ou o que tornar o código mais fácil de ler. Pessoalmente, eu evitaria o uso do operador ternário para expressões longas, complexas ou com vários níveis de aninhamento.
Escolha Y Caso X
Além de se
, algumas linguagens de programação fornecem uma estrutura de condição chamada escolha
.
A estrutura escolha
provê uma forma prática de selecionar um entre diversos valores, evitando-se a necessidade de aninhar múltiplas estruturas se
em seqüencia.
let numero = parseInt(prompt("Escolha um número inteiro."))
switch (numero) {
case 1:
console.log("Um.")
break
case 2:
console.log("Dois.")
break
case 7:
console.log("Sete.")
break
case 99:
console.log("Noventa e nove.")
break
default:
console.log("Outro número: ", numero)
break
}
default
(padrão
ou outro caso
) é a opção padrão para casos não contemplados anteriormente.
Caso seja omitida, o bloco termina sem processar os demais casos.
Caso existam valores não considerados, compiladores costumam emitir um aviso (warning) a respeito.
Em linguagens sem a estrutura escolha
, o bloco anterior poderia ser escrito como:
let numero = parseInt(prompt("Escolha um número inteiro."))
if (numero === 1) {
console.log("Um.")
} else if (numero === 2) {
console.log("Dois.")
} else if (numero === 7) {
console.log("Sete.")
} else if (numero === 99) {
console.log("Noventa e nove.")
} else {
console.log("Outro número: ", numero)
}
Para facilitar a correspondência entre a implementação usando escolha
e a usando se
, é possível modificar a indentação e criar escopos próprios para cada um dos casos.
Em particular, isso também permite criar variáveis locais para cada caso.
let numero = parseInt(prompt("Escolha um número inteiro."))
switch (numero) {
case 1: {
console.log("Um.")
break
}
case 2: {
console.log("Dois.")
break
}
case 7: {
console.log("Sete.")
break
}
case 99: {
console.log("Noventa e nove.")
break
}
default: {
console.log("Outro número: ", numero)
break
}
}
O funcionamento de escolha
tende a variar entre linguagens de programação.
Em algumas, como C, C++ e JavaScript, deve-se usar break
(interrompa
) para terminar o código para cada case
(caso
).
Caso o uso não seja feito, os casos seguintes serão executados até a identificação do primeiro break
ou o fim da estrutura.
let numero = 1
switch (numero) {
case 1:
console.log("Um.")
case 2:
console.log("Dois.")
case 7:
console.log("Sete.")
break
case 99:
console.log("Noventa e nove.")
break
default:
console.log("Outro número: ", numero)
break
}
No exemplo modificado, a omissão de break
para os casos 1
e 2
gerará respostas diferentes do código original.
Em casos em que o uso seja intencional, pode-se escrever código mais compacto definindo-se casos comuns primeiro.
Por exemplo, caso o objetivo do bloco anterior fosse escrever todos os nomes de números por extenso até o número 7, o código estaria correto.
Além disso, algumas linguagens de programação possuem restrições quanto ao tipo da variável usada em caso
.
Por exemplo, C e C++ permitem apenas o uso de valores numéricos (ou seja, não é possível usar cadeias de caracteres).
Assim, o código a seguir é válido em linguagens que permitem o uso de cadeias de caracteres em escolha
, como JavaScript, mas não é válido em linguagens como C.
let numero_por_extenso = "Um"
switch (numero_por_extenso.toLowerCase()) {
case "um":
console.log("1.")
break
case "dois":
console.log("2.")
break
case "sete":
console.log("7.")
break
case "noventa e nove":
console.log("99.")
break
default:
console.log("Outro número: ", numero)
break
}
Existem ainda linguagens que fornecem comandos para escolha
com mais funcionalidades, como intervalos de valores e reconhecimento de padrões.
Por vezes, um comando assim é chamado de match
ao invés de switch
, como match
para GDScript (documentação).
Estruturas Condicionais em Programação
Recursos visuais como fluxogramas e linguagens de programação visual podem ser úteis para visualizar estruturas de condição. Infelizmente, esses recursos não são acessíveis para todas as pessoas (pois requerem visão).
Fluxogramas: Flowgorithm
Em Flowgorithm, estruturas de condição estão disponíveis no bloco Alternativa (If).
A Alternativa possui duas partes: Verdadeiro (True), usado caso a condição tenha resultado Verdadeiro
.
Em outras palavras, Verdadeiro corresponde à parte do então
.
A parte Falso (False), para caso Falso
, corresponde à parte do senão
.
O código para cada possibilidade deve ser inserido no lado adequado.
A condição para o bloco é definida modificando-se o valor do bloco. Para isso, pode-se clicar duas vezes sobre o bloco Alternativa adicionado. A condição deve resultar em um valor lógico.
O trecho de código a seguir contém a transcrição do texto da imagem.
Principal
Lógico mostrarMensagem
mostrarMensagem = true
Alternativa mostrarMensagem
Falso
Verdadeiro
Saída "Olá! Meu nome é Franco."
Inteiro numero
numero = 1
Alternativa numero < 0
Falso
Alternativa numero > 0
Falso
Saída "Zero."
Verdadeiro
Saída "Número positivo."
Verdadeiro
Saída "Número negativo."
Alternativa numero == -1
Falso
Alternativa numero == 0
Falso
Alternativa numero == 1
Falso
Saída "Outro número."
Verdadeiro
Saída "Um.
Verdadeiro
Saída "Zero."
Verdadeiro
Saída "Menos um.
Fim
Um benefício do uso de fluxogramas é a maior facilidade para interpretar o programa. Segue-se a seta e escolhe-se o caminho da alternativa conforme a condição. No caso de Flowgorithm, o programa sempre segue de cima para baixo. Quando se atinge uma alternativa, o programa bifurca em duas opções: uma para caso a condição seja verdadeira, outra para quando ela é falsa. Quando o bloco termina, o programa volta a ter um único caminho, passível de novas bifurcações a toda nova alternativa.
Em particular, a execução do programa passo-a-passo permite visualizar como cada instrução é seguida. Caso você esteja usando Flowgorithm, eu recomendaria usá-la para observar a execução.
Linguagens de Programação Visual: Scratch
Estruturas de condição em Scratch estão disponíveis no menu lateral Controle (Control).
Scratch define apenas a estrutura se
para condições, em dois blocos distintos.
O primeiro chama-se se _
então (if _
then), enquanto o segundo fornece se _
então e senão (else).
A condição usada para se
deve ser um resultado lógico.
Para facilitar a leitura do código, adotou-se mostrar_mensagem
como uma cadeia de caracteres ao invés de um valor lógico (Scratch não fornece constantes para Verdadeiro
e Falso
).
O valor sim
exibe a mensagem; qualquer outro valor omite a mensagem.
O código em Scratch pode fornecer uma maneira de visualizar a estrutura do programa.
Pode-se notar como cada recurso de programação encaixa-se em outros para a escrita de programas.
Por exemplo, a condição para se
requer um resultado lógico, que pode ser criado a partir de um operador relacional ou de um operador lógico.
Além disso, cada possível caminho de um se
termina em um ponto especificado.
Após o término, o programa continua no bloco que sucede o se
.
Linguagens de Programação Textual: JavaScript, Python, Lua e GDScript
JavaScript foi usada nos exemplos iniciais desta página. Esta seção expande a sintaxe para estruturas de condição nas linguagens Python, Lua e GDScript.
JavaScript, Python, Lua e GDScript fornecem a estrutura condicional se
.
Em JavaScript, os parênteses fazem parte da estrutura.
Assim, é obrigatório escrever if (condicao)
.
Em Python, Lua e GDScript, eles são opcionais (embora eu prefira escrevê-los).
Assim, if condicao
e if (condicao)
são equivalentes nas três linguagens.
Apenas JavaScript e GDScript fornecem uma estrutura para escolha
.
Em JavaScript, o uso de parênteses é obrigatório (switch (valor)
); em GDScript, o uso é opcional.
Em Python e Lua, deve-se usar uma seqüencia de se
e senão
que seja mutuamente exclusiva.
Outra possibilidade é possível simular uma estrutura escolha
usando um dicionário (Python) e um tabela (Lua).
O valor para outro caso (default
) ocorre quando o valor não existir na estrutura de dados.
Contudo, como não se comentou ainda sobre estruturas de dados, os exemplos utilizarão se
s aninhados.
let mostrar_mensagem = true
if (mostrar_mensagem) {
console.log("Olá! Meu nome é Franco.")
}
let numero = 1
if (numero < 0) {
consolel.log("Número negativo.")
} else if (numero > 0) {
console.log("Número positivo.")
} else {
console.log("Zero.")
}
switch (numero) {
case -1:
console.log("Menos um.")
case 0:
console.log("Zero.")
break
case 1:
console.log("Um.")
break
default:
console.log("Outro número.")
break
}
mostrar_mensagem = True
# Uso de parênteses é opcional.
if mostrar_mensagem:
print("Olá! Meu nome é Franco.")
numero = 1
if (numero < 0):
print("Número negativo.")
elif (numero > 0):
print("Número positivo.")
else:
print("Zero.")
if (numero == -1):
print("Menos um.")
elif (numero == 0):
print("Zero.")
elif (numero == 1):
print("Um.")
else:
print("Outro número.")
local mostrar_mensagem = true
-- Uso de parênteses é opcional.
if mostrar_mensagem then
print("Olá! Meu nome é Franco.")
end
local numero = 1
if (numero < 0) then
print("Número negativo.")
elseif (numero > 0) then
print("Número positivo.")
else
print("Zero.")
end
if (numero == -1) then
print("Menos um.")
elseif (numero == 0) then
print("Zero.")
elseif (numero == 1) then
print("Um.")
else
print("Outro número.")
end
extends Node
func _ready():
var mostrar_mensagem = true
# Uso de parênteses é opcional.
if mostrar_mensagem:
print("Olá! Meu nome é Franco.")
var numero = 1
if (numero < 0):
print("Número negativo.")
elif (numero > 0):
print("Número positivo.")
else:
print("Zero.")
# Uso de parênteses é opcional.
match numero:
# Cada caso requer indentação.
-1:
print("Menos um.")
0:
print("Zero.")
1:
print("Um.")
# Outro caso ou default.
_:
print("Outro número.")
Nos exemplos, é importante notar como cada linguagem define blocos e escopos.
JavaScript: blocos definidos usando chaves. O bloco inicia-se em
{
(abre chaves) e termina em}
(fecha chaves). Indentação é opcional, embora recomendada;Lua: blocos definidos por palavras-chave. O bloco inicia-se em
then
e termina emend
. Para condições intermediárias, o próximo bloco começa emelseif
ouelse
. Indentação também é opcional, embora recomendada;Python e GDScript: blocos definidos por indentação. Para iniciar um novo bloco, deve-se aumentar a indentação em um nível. Para terminar um bloco, deve-se reduzir a indentação de um nível. A indentação, portanto, é obrigatória e parte da sintaxe da linguagem.
Por exemplo, para indentação usando 4 espaços:
- Primeiro nível: 0 espaços;
- Segundo nível: 4 espaços;
- Terceiro nível: 8 espaços...
E assim por diante. O número de espaços é de sua escolha, mas deve ser consistente. Por exemplo, para avançar do segundo para o terceiro nível, deve-se iniciar a próxima linha com 8 espaços ao invés de 4. Para retornar do terceiro para o segundo nível, a próxima linha de código deve começar com 4 espaços ao invés de 8.
Para indentação usando tabulações:
- Primeiro nível: 0 tabulações;
- Segundo nível: 1 tabulações;
- Terceiro nível: 2 tabulações...
E assim por diante.
Como comentado em editores de texto para programação, a tecla TAB
adicionará uma tabulação ou o número de espaços para o próximo nível.
A combinação Shift TAB
faz a operação contrária: remove uma tabulação ou o número de espaços necessário para retornar ao nível anterior.
Em alguns editores de texto, como GNU Emacs, TAB
define quase sempre a indentação correta para cada nível.
Para se alterar entre níveis, pode-se pressionar a tecla novamente.
Nos demais exemplos e próximos tópicos, eu utilizarei parênteses em estruturas de condição mesmo quando o uso for opcional. Eu prefiro usá-los por hábito. Também uso parênteses em atribuições de valores lógicos, por julgar mais fácil de ler. Caso você prefira digitar menos, não é necessário digitá-los em seus projetos para as linguagens Python, Lua e GDScript.
Exemplos
Estruturas condicionais são um dos recursos que tornam computadores máquinas mais versáteis e poderosas que calculadoras. Com condições, você pode definir processamentos arbitrários para diferentes classes de valores em sua aplicação, mudando o comportamento de acordo com o estado atual do programa.
Par ou Ímpar?
Em operações relacionais, apenas era possível mostrar Verdadeiro
ou Falso
como saída de comparações.
Com estruturas condicionais, você pode escolher mensagens mais apropriadas para cada possível resultado.
Um exemplo ilustrativo é escrever se um número fornecido como entrada é par ou se é ímpar.
Afinal, escrever 2 é par
ou 3 é ímpar
é mais fácil de ler e entender que 2 é par? True
ou 3 é par? False
.
let numero = parseInt(prompt("Digite um número inteiro: "))
let resto = numero % 2
if (resto === 0) {
console.log(numero, " é par.")
} else {
console.log(numero, " é ímpar.")
}
numero = int(input("Digite um número inteiro: "))
resto = numero % 2
if (resto == 0):
print(numero, " é par.")
else:
print(numero, " é ímpar.")
print("Digite um número inteiro: ")
local numero = io.read("*number")
local resto = numero % 2
if (resto == 0) then
print(numero, " é par.")
else
print(numero, " é ímpar.")
end
extends Node
func _ready():
# Número definido no código, pois GDScript não permite leitura via terminal.
var numero = 1
var resto = numero % 2
if (resto == 0):
print(numero, " é par.")
else:
print(numero, " é ímpar.")
É possível evitar a declaração da variável resto
utilizando diretamente (numero % 2) == 0
como condição.
O operador ==
deve ser trocado para ===
em JavaScript.
Validação Parcial de Dados
Quando se solicita a entrada de um valor de um determinado tipo a um usuário ou uma usuário, o que ocorre se a pessoa fornecer um valor de um tipo inesperado?
Por exemplo, texto como "Franco"
ao invés de um número como -123
?
A resposta é um erro. Com estruturas de condição, é possível determinar se a entrada fornecida é ou não é válida antes de tentar processá-la.
O programa a seguir utiliza se
para determinar se o valor lido é um número inteiro.
Caso seja, o programa escreve o dobro do valor e termina.
Caso contrário, o programa escreve uma mensagem de erro e termina.
let numero = parseInt(prompt("Digite um número: "))
if (!isNaN(numero)) {
console.log("2 * ", numero, " = ", 2 * numero)
} else {
console.log("O valor fornecido não é um número.")
}
try:
# int() / float()
numero = int(input("Digite um número: "))
print("2 * ", numero, " = ", 2 * numero)
except ValueError:
print("O valor fornecido não é um número.")
print("Digite um número: ")
local numero = io.read("*number")
if (numero ~= nil) then
print("2 * ", numero, " = ", 2 * numero)
else
print("O valor fornecido não é um número.")
end
extends Node
func _ready():
# Número definido no código, pois GDScript não permite leitura via terminal.
var valor = "Franco"
# is_valid_float() / is_valid_integer()
if (valor.is_valid_integer()):
var numero = int(valor)
print("2 * ", numero, " = ", 2 * numero)
else:
print("O valor fornecido não é um número.")
Como descobrir qual é o valor retornado por um comando ou função de leitura em caso de valor inválido? Consultando a documentação.
- JavaScript:
parseInt()
(documentação) eparseFloat()
(documentação) retornamNaN
(not a number, ou não é um número) em caso de erro. Pode-se usar a funçãoisNaN()
(documentação) para se testar se um valor é igual aNaN
; - Python: a linguagem utiliza exceções para indicar erros ao invés de valores de retorno.
int()
efloat()
emitem uma exceção chamadaValueError
(documentação). Como o material não abordou exceções: exceções são situações excepcionais que podem servir para indicar erros. O tratamento de exceções (em linguagem com suporte) é feito de forma parecida com estruturas de condição, mas usam uma estrutura especial chamadatry... catch
(outry... except
). A parte comtry
assume que nenhum erro ocorrerá. Caso ocorra um erro, ele é tratado na partecatch
(ouexcept
) para tipos determinados de exceções. No caso da conversão, ela chama-seValueError
; - Lua:
io.read()
utilizafile.read()
(documentação) usando como arquivo a entrada padrãostdin
. A documentação informa que, em caso de erro, a função retornanil
. - GDScript: a verificação pode usar os métodos
is_valid_integer()
(documentação) para números inteiros eis_valid_float()
(documentação) para números reais. Ambos retornamtrue
caso o texto represente um número, oufalse
, caso contrário.
Caso você esteja imaginado a razão da seção chamar-se "validação parcial", a razão é que não é possível solicitar a leitura de um novo valor e assegurar que ele seja válido usando apenas estruturas condicionais. Por exemplo, caso o novo valor fornecido também seja inválido, seria necessário duplicar o código para tentar novamente.
O problema é: quantas vezes o código seria replicado? Potencialmente infinitas.
Uma estratégia seria fixar um limite máximo para tentativas erradas (por exemplo, 3). Contudo, ela é limitada. Para uma solução mais robusta, pode-se usar estruturas de repetição para solicitar releituras até a entrada de um valor válido. Estruturas de repetição serão um dos próximos tópicos do material.
Classificação de Triângulos Quanto às Medidas de Lados
Dependendo da medida de seus lados, um triângulo pode ser:
- Equilátero, se a medida de todos os lados forem iguais;
- Isósceles, se a medida de dois lados forem iguais;
- Escaleno, se a medida de todos os lados forem diferentes.
Existem diferentes formas de implementar a solução do problema. Antes de consultar uma possível solução abaixo, tente pensar na sua. Quando se está aprendendo, a melhor solução é a sua.
Evidentemente, uma solução só é solução se estiver correta. Para testar sua solução, você pode considerar os seguintes triângulos com lados , e como casos de teste:
- , , : equilátero;
- , , : isósceles;
- , , : isósceles;
- , , : isósceles;
- , , : escaleno;
- , , : não é triângulo (todos os lados devem ter medidas positivas).
Testar seus programas é algo fundamental. Um programa compilar ou executar até o final não é garantia de solução correta (só é possível afirmar que ele esteja sintaticamente correto). Assim, um bom ponto de partida é resolver o problema manualmente para criar casos de testes, com entradas específicas e respostas esperadas. Um programa que passe por todos os casos de teste não está, necessariamente, correto, mas a falha de qualquer caso sugere a existência de problemas caso o resultado obtido não corresponda ao esperado.
let lado_a = parseFloat(prompt("A = "))
let lado_b = parseFloat(prompt("B = "))
let lado_c = parseFloat(prompt("C = "))
if ((lado_a <= 0.0) || (lado_b <= 0.0) || (lado_c <= 0.0)) {
console.log("Medidas inválidas para um ou mais lados.")
} else {
if (lado_a === lado_b) {
if (lado_b === lado_c) {
console.log("Triângulo equilátero.")
} else {
console.log("Triângulo isósceles.")
}
} else if ((lado_a === lado_c) || (lado_b === lado_c)) {
console.log("Triângulo isósceles.")
} else {
console.log("Triângulo escaleno.")
}
}
lado_a = float(input("A = "))
lado_b = float(input("B = "))
lado_c = float(input("C = "))
if ((lado_a <= 0.0) or (lado_b <= 0.0) or (lado_c <= 0.0)):
print("Medidas inválidas para um ou mais lados.")
else:
if (lado_a == lado_b):
if (lado_b == lado_c):
print("Triângulo equilátero.")
else:
print("Triângulo isósceles.")
elif ((lado_a == lado_c) or (lado_b == lado_c)):
print("Triângulo isósceles.")
else:
print("Triângulo escaleno.")
print("A = ")
local lado_a = io.read("*number")
print("B = ")
local lado_b = io.read("*number")
print("C = ")
local lado_c = io.read("*number")
if ((lado_a <= 0.0) or (lado_b <= 0.0) or (lado_c <= 0.0)) then
print("Medidas inválidas para um ou mais lados.")
else
if (lado_a == lado_b) then
if (lado_b == lado_c) then
print("Triângulo equilátero.")
else
print("Triângulo isósceles.")
end
elseif ((lado_a == lado_c) or (lado_b == lado_c)) then
print("Triângulo isósceles.")
else
print("Triângulo escaleno.")
end
end
extends Node
func _ready():
var lado_a = 3
var lado_b = 4
var lado_c = 5
if ((lado_a <= 0.0) or (lado_b <= 0.0) or (lado_c <= 0.0)):
print("Medidas inválidas para um ou mais lados.")
else:
if (lado_a == lado_b):
if (lado_b == lado_c):
print("Triângulo equilátero.")
else:
print("Triângulo isósceles.")
elif ((lado_a == lado_c) or (lado_b == lado_c)):
print("Triângulo isósceles.")
else:
print("Triângulo escaleno.")
Para um exemplo adicional, a implementação verifica se todos os lados fornecidos são positivos. Caso queira aprimorar a solução, você pode considerar a desigualdade triangular para verificar se os lados fornecidos podem, de fato, formar um triângulo.
Além disso, é possível reorganizar a solução para tornar o código um pouco mais compacto. Você consegue indentificar como?
Novos Itens para Seu Inventário
Ferramentas:
- Casos de teste.
Habilidades:
- Criação de condições;
- Definição de desvios;
- Verificação de entradas.
Conceitos:
- Desvios;
- Saltos (condicionais e incondicionais);
- Fluxo de execução;
- Programação estruturada;
- Seqüência;
- Seleção;
- Iteração;
- Estruturas condicionais.
Recursos de programação:
- Estilos de formatação e indentação de código-fonte;
- Comando
se
; - Comando
escolha
; - Operador
se ternário
; - Anihamento de condições.
Pratique
Com estruturas condicionais, você pode melhorar a saída de sua soluções para os exercícios de operações relacionais e de operações lógicas.
Agora também é possível resolver problemas mais complexos. Uma parte importante do aprendizado de programação é descobrir como combinar as peças disponíveis para resolver problemas. Todos os exercícios a seguir são possíveis de resolver com os conceitos apresentados até este momento, embora você terá que pensar e usar a criatividade para alguns.
Leia o valor de um nome. Compare com o valor de seu nome. Se os nomes forem iguais, escreva uma mensagem.
Leia o valor de dois números. Se os números forem diferentes, escreva qual o menor e qual o maior valor. Caso contrário, escreva que os números são iguais.
Leia três números. Escreva-os em ordem crescente de valores (do menor para o maior). Em seguida, escreva os valores em ordem decrescente (do maior para o menor).
Escreva um menu de opções usando estruturas condicionais. O menu pode relacionar números com opções. Por exemplo:
- Escrever "Franco";
- Escrever "Olá!"
- Escrever "Tchau".
Leia o valor de uma número. Por exemplo, se a pessoa fornecer o valor
1
, o programa deve escreverFranco
.Você também pode usar letras ou palavras como o valor de acesso (por exemplo,
a
,b
ec
, ounome
,saudacao
edespedida
).Crie uma calculadora com operações aritméticas básicas (soma, subtração, multiplicação e divisão). A calculadora deve ler dois valores e um operador, para, em seguida, efetuar operação correspondente e escrever o resultado. Cuidado com divisões por zero! Dica: a escolha de operação pode usar um menu como o do exercício anterior.
Leia três valores de ângulos internos de triângulo. A soma dos ângulos fornecidos é igual a 180°? Escreva uma mensagem de acordo.
Leia três valores de ângulos internos para um triângulo. Classifique o triângulo quanto aos ângulos.
- Se todos os ângulos internos forem menor que 90°, o triângulo é agudo (acutângulo);
- Se um dos ângulos internos for igual a 90°, o triângulo é retângulo;
- Se um dos ângulos internos for maior que 90°, o triângulo é obtuso (obtusângulo).
Deve-se notar que apenas um dos ângulos pode ter valor igual ou maior que 90°. Caso contrário, a soma dos ângulos internos excederá 180°.
Leia o nome de um mês. Informe quantos dias ele tem. Você pode escolher um número de dias para fevereiro, ou informar as duas possibilidades (28 dias ou 29 dias em anos bissextos).
Considere uma coluna da tabela periódica dos elementos químicos. Por exemplo, pode-se considerar o grupo 1 (metais alcalinos), composto por lítio (Li), sódio (Na), potássio (K), rubídio (Rb), césio (Cs), e frâncio (Fr). Leia uma sigla e informe se ela pertence ao grupo. Caso pertença, escreva o nome do elemento correspondente. Por exemplo, a entrada
Fr
resultaria no elementoFrâncio
.No jogo Pedra, Papel e Tesoura, duas pessoas escolhem um dentre os três valores anteriores como suas jogadas. Se os valores escolhidos forem iguais, o jogo resulta em empate. Para as demais combinações:
- Pedra vence tesoura;
- Tesoura vence papel;
- Papel vence pedra.
Leia duas jogadas e escreva o resultado obtido.
O jogo implementado não será muito justo, pois a segunda pessoa poderia ler a jogada da primeira. Você consegue pensar em uma forma de esconder a primeira jogada antes da leitura do segundo valor?
Uma forma melhor de modificar a solução consiste sortear uma jogada, algo que pode ser feito mediante o uso de números pseudoaleatórios, que serão abordados em conjunto com estruturas de repetição.
Próximos Passos
Estruturas condicionais fazem de computadores ferramentas mais poderosas que meras calculadoras, por permitirem definir desvios e fluxos alternativos para a execução de um programa.
Com estruturas condicionais, torna-se possível resolver problemas mais complexos. Com problemas mais complexos, a tendência é que o tamanho dos programas (em linhas de código) também comece a aumentar. Até este momento, toda vez que se desejou realizar um mesmo processamento, precisou-se duplicar o código. Duplicar código não é uma boa prática de programação.
Em geral, o ideal é criar código que possa ser usado com vários valores, sempre que necessário. O próximo tópico permitirá a criação de blocos de código reusáveis, chamados de subrotinas, um termo que inclui funções, procedimentos e métodos.
Você já usou algumas subrotinas em seus programas (por exemplo, para conversão de valores, ou mesmo para entrada e saída dados em algumas linguagens). Agora você poderá começar a criar suas próprias, assim como suas próprias bibliotecas.
- Introdução;
- Ponto de entrada e estrutura de programa;
- Saída (para console ou terminal);
- Tipos de dados;
- Variáveis e constantes;
- Entrada (para console ou terminal);
- Aritmética e Matemática básica;
- Operações relacionais e comparações;
- Operações lógicas e Álgebra Booleana;
- Estruturas de condição (ou condicionais ou de seleção);
- Subrotinas: funções e procedimentos;
- Estruturas de repetição (ou laços ou loops);
- Vetores (arrays), cadeias de caracteres (strings), coleções (collections) e estruturas de dados;
- Registros (structs ou records);
- Arquivos e serialização (serialization ou marshalling);
- Bibliotecas;
- Entrada em linha de comando;
- Operações bit-a-bit (bitwise operations);
- Testes e depuração.