Avançar para o conteúdo
Imagem com logotipo, contendo link para a página inicial
  • United Stated of America flag, representing the option for the English language.
  • Bandeira do Brasil, simbolizando a opção pelo idioma Português do Brasil.

Aprenda Programação: Estruturas Condicionais (ou Estruturas de Condição)

Exemplos de uso de subrotinas em Python, Lua, GDScript e JavaScript.

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:

  1. Se, Então, Senão, que será doravante chamada de se ou if;
  2. Escolha... Caso, que será doravante chamada de escolha ou switch.

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:

  1. Allman:
    if (true)
    {
        console.log("Allman")
    }
    else
    {
        console.log("...")
    }
  2. K&R:
    if (true) {
        console.log("K&R")
    } else {
        console.log("...")
    }
  3. GNU:
    if (true)
      {
        console.log("GNU")
      }
    else
      {
        console.log("...")
      }
  4. Whitesmiths:
    if (true)
        {
        console.log("Whitesmiths")
        }
    else
        {
        console.log("...")
        }
  5. 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.

Exemplo uso de estruturas condicionais em Flowgorithm com interface em Português.

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.

Exemplo uso de estruturas condicionais em Scratch em página em Português.

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 ses 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 em end. Para condições intermediárias, o próximo bloco começa em elseif ou else. 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) e parseFloat() (documentação) retornam NaN (not a number, ou não é um número) em caso de erro. Pode-se usar a função isNaN() (documentação) para se testar se um valor é igual a NaN;
  • Python: a linguagem utiliza exceções para indicar erros ao invés de valores de retorno. int() e float() emitem uma exceção chamada ValueError (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 chamada try... catch (ou try... except). A parte com try assume que nenhum erro ocorrerá. Caso ocorra um erro, ele é tratado na parte catch (ou except) para tipos determinados de exceções. No caso da conversão, ela chama-se ValueError;
  • Lua: io.read() utiliza file.read() (documentação) usando como arquivo a entrada padrão stdin. A documentação informa que, em caso de erro, a função retorna nil.
  • GDScript: a verificação pode usar os métodos is_valid_integer() (documentação) para números inteiros e is_valid_float() (documentação) para números reais. Ambos retornam true caso o texto represente um número, ou false, 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.

  1. Leia o valor de um nome. Compare com o valor de seu nome. Se os nomes forem iguais, escreva uma mensagem.

  2. 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.

  3. 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).

  4. Escreva um menu de opções usando estruturas condicionais. O menu pode relacionar números com opções. Por exemplo:

    1. Escrever "Franco";
    2. Escrever "Olá!"
    3. Escrever "Tchau".

    Leia o valor de uma número. Por exemplo, se a pessoa fornecer o valor 1, o programa deve escrever Franco.

    Você também pode usar letras ou palavras como o valor de acesso (por exemplo, a, b e c, ou nome, saudacao e despedida).

  5. 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.

  6. Leia três valores de ângulos internos de triângulo. A soma dos ângulos fornecidos é igual a 180°? Escreva uma mensagem de acordo.

  7. 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°.

  8. 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).

  9. 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 elemento Frâncio.

  10. 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.

  1. Introdução;
  2. Ponto de entrada e estrutura de programa;
  3. Saída (para console ou terminal);
  4. Tipos de dados;
  5. Variáveis e constantes;
  6. Entrada (para console ou terminal);
  7. Aritmética e Matemática básica;
  8. Operações relacionais e comparações;
  9. Operações lógicas e Álgebra Booleana;
  10. Estruturas de condição (ou condicionais ou de seleção);
  11. Subrotinas: funções e procedimentos;
  12. Estruturas de repetição (ou laços ou loops);
  13. Vetores (arrays), cadeias de caracteres (strings), coleções (collections) e estruturas de dados;
  14. Registros (structs ou records);
  15. Arquivos e serialização (serialization ou marshalling);
  16. Bibliotecas;
  17. Entrada em linha de comando;
  18. Operações bit-a-bit (bitwise operations);
  19. Testes e depuração.
  • Informática
  • Programação
  • Iniciante
  • Pensamento Computacional
  • Aprenda a Programar
  • Python
  • Lua
  • Javascript
  • Godot
  • Gdscript
  • Scratch
  • Flowgorithm