Suplemento de Aula

2.0 Estrutura lógica dos algoritmos

​​​​​​​Fonte: https://devschannel.com/


  1. 2.0 Estrutura lógica dos algoritmos
  2. Fluxogramas
    1. Conhecendo o draw.io
    2. Primeiros fluxogramas
  3. Mini sumario
  4. Declaração e atribuição
  5. Identificadores
    1. Restrições
    2. Guias de estilo e Convenção de Nomes (naming conventions)
  6. Tipo de dado
  7. Tipos de dados escalares
    1. Atribuição de valores
      1. Valores numéricos
      2. Valores lógicos
      3. Chars
  8. Tipos de dados compostos
    1. Array
      1. Vetor
      2. Matrizes
      3. Índices
      4. Convenções
    2. Strings
  9. Tipagem
    1. Inferência de tipo
    2. Tipagem estática
    3. Tipagem dinâmica
    4. Conclusão
  10. Constantes
    1. Declaração
    2. Constantes anônimas
  11. Mini sumario
  12. Operadores de atribuição
  13. Operadores aritméticos
  14. Precedência de operadores aritméticos
  15. Operadores relacionais
  16. Operadores lógicos
    1. O operador e
    2. O operador ou
    3. O operador xor
    4. O operador nao
    5. Tabela verdade
    6. Precedência de operadores lógicos
  17. Operadores de caracteres
    1. O que você aprendeu:
  18. Material Extra

Propriedades de um Algoritmo

Fluxogramas

Além do pseudocódigo, há um outro recurso que pode ser usado para representar algoritmos: o fluxograma. Ele é um tipo especial de diagrama, usado para ilustrar, através de formas e símbolos, as instruções que compõem um algoritmo. Cada tipo de instrução é associada à uma forma diferente. As instruções são conectadas usando flechas, que indicam o fluxo do algoritmo.

Quando visita a bibliografia, você pode encontrar diversas formas de representação de algoritmos na forma de fluxogramas. Entretanto, existe uma norma padrão para seu desenvolvimento, a Norma ISO 5707/1985 que normatiza o padrão para Fluxogramas e Pseudocódigo ([Revista Programar v. 21|http://www.slideshare.net/filipebes/revista-programar-21]).


Para criar fluxogramas, existem várias ferramentas, tanto softwares para desktop, como também ferramentas online, que rodam no navegador. Nessa disciplina, vamos trabalhar com a ferramenta do site draw.io.


Conhecendo o draw.io

Entre no draw.io. Se você estiver entrando nele pela primeira vez, você será perguntado onde você quer salvar os seus diagramas. Há várias opções, sendo que uma delas é a Device, que salva os diagramas no seu HD.

Perceba também como a ferramenta te dá a opção de escolher o seu idioma: basta clicar no ícone do globo e escolher o seu idioma na lista que aparecer.


Depois disso, você é perguntado se quer criar um novo diagrama ou abrir um diagrama existente:

Essa pergunta também é feita mesmo se esse não for o seu primeiro acesso no site. Opte por criar um novo diagrama. Você pode começar com um diagrama em branco ou com um que tenha alguns elementos já definidos. Escolha o diagrama em branco e clique em Criar.


Já pela interface gráfica do sistema, de cara já dá pra perceber que ele tenta imitar um software de desktop em alguns pontos. Há um menu que é bem semelhante à menus de desktop e uma barra de ferramentas, que também é bem frequente em softwares de desktop e pouco frequente em sistemas web.


O sistema tenta imitar softwares de desktop em outros aspectos também: nos atalhos de teclado. Ele é cheio de atalhos de teclado para várias tarefas e é claro que os mais usados não poderiam faltar: Ctrl + C / Ctrl + V, para copiar e colar; Ctrl + X, para recortar; Ctrl + Z / Ctrl + Shift + Z para desfazer e refazer.


Há algumas opções interessantes no submenu Ajuda. Elas podem te ajudar a entender melhor como usar o draw.io para criar os seus fluxogramas. Lembrando que ele é uma ferramenta que serve para criar diagramas de diversos tipos, de diversas áreas de conhecimento, não apenas na área de desenvolvimento de software.


Eu destaco também algumas opções do submenu Arquivo: tem opções pra salvar o diagrama que você fez, abrir diagramas que você salvou, imprimir o diagrama e muitas outras opções.


Outro submenu bem interessante é o Ordenar: ele tem opções pra rotacionar elementos, agrupar e desagrupar elementos e também alinhar elementos tanto horizontal quanto verticalmente.


Muitas opções da barra de ferramentas fazem a mesma coisa que algumas opções do menu. Entre os elementos da barra de ferramentas, eu destaco a opção de zoom. Ela tem vários valores pré-definidos e tem uma opção bem interessante, de ajustar à largura da página, que é muito interessante e também está presente em vários softwares de desktop, como o leitor de PDF Adobe Reader.


Veja que, abaixo da barra de ferramentas, há três colunas: a primeira mostra algumas formas e símbolos que podem ser adicionados à coluna do meio, que seria uma espécie de área de trabalho do sistema. Na coluna da direita, podemos configurar algumas coisas, tais como:

  • Habilitar ou desabilitar a grelha.
  • Habilitar ou desabilitar a guia. A guia é útil para alinhar os elementos do fluxograma.
  • Definir o tamanho do papel e a orientação dele (retrato/paisagem).


Primeiros fluxogramas

Agora que você já teve uma pequena introdução ao draw.io, vou mostrar nessa seção como você pode criar seus primeiros fluxogramas nessa ferramenta. Antes de criar um fluxograma, eu vou explicar alguns símbolos e formas básicos:

  • Input: paralelogramo inclinado para a esquerda.
  • Processamento: retângulo.
  • Output: paralelogramo inclinado para a direita.
  • Flechas: conectam instruções.
  • Início/Fim: elipse/pílula (Terminator).


Vamos ver como adicionar cada um desses símbolos e formas. Primeiramente, vamos começar pelo processamento, que é o mais simples. Para adicioná-lo, basta clicar no primeiro elemento da aba “Geral“, que é um retângulo:

Depois do clique, o elemento vai aparecer na Área de Trabalho. Aí, basta arrastar e soltar o elemento para mudar a posição dele. Coloque o elemento na posição que você quiser e adicione o segundo elemento, que é a forma de Output, representada pelo paralelogramo inclinado para a direita:

Note que ao selecionar um elemento, como o paralelogramo que você acabou de criar, aparecem três abas do lado direito: Estilo, Texto e Ordenar. Elas servem para alterar várias propriedades do elemento. Selecione a aba Ordenar. Clique no botão Horizontal, que está dentro da seção Virar. Esse botão faz com que o paralelogramo passe a ficar inclinado para a esquerda, que é como um elemento de input é representado. Veja na imagem abaixo:

A forma de início/fim é representada por uma elipse. A aba Geral até tem uma, mas a elipse que é usada em fluxogramas é bem mais achatada do que essa. Por sorte, o draw.io tem uma aba específica para elementos de fluxogramas. Clique nela e adicione o elemento Terminator:

Esse elemento será usado para criar as formas de início e fim do algoritmo. Você pode ter notado que o draw.io disponibilizou uma elipse mais arredondada para representar o início do algoritmo (elemento Start). Só que, na prática, os fluxogramas costumam usar essa elipse mais achatada para representar o início e o fim do algoritmo, e é o que faremos também.


Falta apenas mostrar como criar as flechas. É bem simples. Ao selecionar um elemento, aparecem flechas azuis nos lados do elemento. Simplesmente clique na flecha mais próxima do elemento que você quer conectar. Veja na imagem abaixo:

Tome cuidado para não deixar os elementos muito distantes um do outro, senão eles não serão conectados e a conexão será feita com um elemento novo. Sempre quando não há um elemento próximo da flecha, é criado um novo elemento igual ao que está selecionado e o elemento selecionado é conectado com o elemento recém-criado.


Tem outros símbolos de fluxogramas, mas eles serão explicados depois. Esses que foram citados são suficientes pra que você crie seu primeiro fluxograma.


No caso dos pseudocódigos do Portugol Studio, é necessário declarar as variáveis do algoritmo que você está representando. Porém, isso é desnecessário nos fluxogramas.


Vamos representar em um fluxograma um algoritmo que lê um número do usuário, eleva ele ao cubo e retorna o número elevado ao cubo para o usuário. Siga os passos abaixo para criar o fluxograma:

  • Coloque uma forma de início do algoritmo.
  • Coloque uma forma de input em uma variável numero.
  • Coloque uma forma de processamento com a variável cubo recebendo o número elevado ao cubo.
  • Coloque uma forma de output com o texto “imprime cubo” (forma resumida pra facilitar).
  • Coloque uma forma de fim do algoritmo (basta copiar a forma de início)
  • Conecte as instruções.


Após terminar o fluxograma, ele deve ficar mais ou menos assim:

Às vezes, alguns elementos que são de pouca importância para o algoritmo são omitidos para simplificar a representação do fluxograma, já que ela ocupa muito espaço com facilidade, por ser uma representação gráfica.


No caso do algoritmo que acabamos de discutir, é normal que seja escrito um texto pedindo para o usuário digitar um número, para que ele saiba que precisamos que ele digite um número e estamos esperando por isso. Porém, isso não tem nenhuma importância para a lógica do algoritmo. É algo que é incluído apenas para que o usuário saiba o que ele precisa fazer. Por isso, não costuma ser incluído em fluxogramas e também não é incluído em alguns pseudocódigos, pelo mesmo motivo.


Esse foi só um exemplo básico de criação de fluxograma. Quando você for criar seus próprios fluxogramas, se você também usar o draw.io, use e abuse da cópia dos elementos, porque ela agiliza bastante a criação dos fluxogramas. Aliás, os aplicativos do Microsoft 365 (Office) também possuem uma caixa de ferramentas com os recursos para desenvolvimento de fluxogramas conforme você vê na figura a seguir:

Além de criar fluxogramas no computador, você também pode criá-los desenhando em um caderno. Qual dos métodos é melhor? Depende de você. Se você se sente mais confortável desenhando e acha mais produtivo, então é a melhor opção para você.


A mesma coisa vale para os pseudocódigos. Você pode escrever seus pseudocódigos em um caderno ou no computador. Você pode escolher qualquer um dos dois. O que realmente importa é representar corretamente o seu algoritmo.


Além disso, pseudocódigos e fluxogramas são duas maneiras de representar algoritmos. O que escolher, mais uma vez, depende do que você se sentir mais confortável e do que você achar que transmite com mais clareza o algoritmo que você quer descreve. No entanto, a maioria dos desenvolvedores prefere pseudocódigos porque criá-los é mais rápido do que criar fluxogramas e ocupa bem menos espaço.


Tipos de Dados

Mini sumario

  1. Declaração e atribuição
  2. Identificadores
  3. Tipos de dados
  4. Tipos de dados escalares
  5. Tipos de dados compostos
  6. Tipagem
  7. Constantes

Declaração e atribuição

Em muitas linguagens de programação, antes que você possa usar qualquer variável, você deve declará-la. No entanto, isso não é necessário em todas as linguagens, e a cada dia mais aumenta o número de linguagens onde isso não é necessário.

Uma declaração é uma instrução que fornece um identificador para uma variável, precedido em muitas linguagens, mas não em todas, pelo tipo da variável. É o tipo que determina o conjunto de valores que a variável pode assumir. O identificador é simplesmente o nome da variável.

Exemplo de declarações em Portugol Studio:

real altura
real peso = 74.4
inteiro prestacoes_financiamento_casa, animais_estimacao

Note que esse exemplo tem 3 tipos de declarações:

  • Declaração de uma variável sem valor (o valor da variável é atribuído posteriormente).
  • Declaração de uma variável com valor (ou seja, declaração e atribuição ao mesmo tempo, num processo chamado de inicialização).
  • Declaração de múltiplas variáveis (todas sem valor, porém eu poderia ter declarado as duas variáveis com valor, se eu quisesse).

Essa é a sintaxe de declarações de variáveis do Portugol Studio. Linguagens de programação tem suas próprias sintaxes, mas geralmente também tem esses mesmos conceitos, porém com diferenças de sintaxe. Veja exemplos de declarações similares em C++:

double altura;
double peso = 74.4;
int prestacoesFinanciamentoCasa = 300, animaisEstimacao = 2;

Agora, veja exemplos de declarações similares em PHP:

$altura;
$peso = 74.4;
$prestacoesFinanciamentoCasa = 300;
$animaisEstimacao = 2;

Veja como os exemplos do PHP não tem declarações de múltiplas variáveis com atribuição de valores diferentes a cada variável. Essa funcionalidade não é suportada no PHP. Viu como cada linguagem tem suas particularidades?

Identificadores

O identificador de uma variável nada mais é do que o seu nome.

Restrições

Cada linguagem de programação tem as suas próprias regras para criar identificadores. Geralmente, as linguagens costumam ter palavras-chaves reservadas, que não podem ser usadas como nomes de variáveis, porque elas são parte da sintaxe da linguagem. Alguns exemplos de palavras-chaves reservadas são nomes de tipos de dados e nomes de estruturas de controle.

Diferentemente das palavras do mundo real, na programação não se pode separar as palavras que fazem parte do identificador de uma variável com espaço. Ao invés disso, usa-se outras maneiras para indicar o fim de uma palavra e o começo de outra no identificador de uma variável. Algumas dessas maneiras são:

  • Usar o underline (snake_case). Exemplo: inteiro jogos_perdidos_selecao = 10.
  • Usar uma letra maiúscula no começo de cada palavra, exceto a primeira (camelCase). Exemplo: inteiro jogosGanhosSelecao = 20.

Além disso, existem outras restrições aos identificadores de variáveis. A documentação do Portugol Studio não cita essas restrições, mas eu fiz alguns testes e vi que não é possível começar um nome de variável com um dígito, mas é possível iniciá-la com um underline ou uma letra.

De uma maneira geral, letras, dígitos e underlines geralmente são permitidos no identificador de uma variável, mas dígitos não costumam são permitidos na primeira letra. Isso não é válido para todas as linguagens, mas muitas delas seguem essas regras.

Os identificadores não permitem que se use letras com acentos, cedilhas, tremas, crases ou caracteres desse tipo. Exemplo:

real aceleracao = 7.1 // aceleracao ao invés de aceleração

Guias de estilo e Convenção de Nomes (naming conventions)

Na maioria das linguagens, os identificadores são case-sensitive (sensíveis à caixa), então  nomeJogador é diferente de NOMEJOGADOR e de nomejogador. No Portugol Studio também é assim. Além disso, as linguagens costumam permitir a colocação de um underline no identificador de uma variável como já foi falado. Exatamente por causa dessa liberdade enorme de nomear as variáveis, existem algumas naming conventions (convenções de nomenclatura) para nomear variáveis. Para quem não sabe, naming conventions são uma coisa muito ampla, são convenções para se nomear alguma coisa, que pode ser de diversas áreas de conhecimento diferentes.

Algumas naming conventions são snake_case e camelCase, que foram brevemente citadas na seção anterior. A popularidade de cada naming convention varia de acordo com cada linguagem. Muitas linguagens tem guias de estilo (style guides), que definem regras para a programação na linguagem e estabelecem quais naming conventions devem ser seguidas e quando elas devem ser aplicadas. Isso mesmo: um guia de estilo de uma linguagem pode seguir mais de uma naming convention. Ele pode, por exemplo, usar camelCase para nomear variáveis e StudlyCaps para nomear classes (não se preocupe se você não sabe o que são classes, você aprenderá quando trabalhar com uma linguagem de programação que tenha classes).

Seguir guias de estilos é bom, porque aumenta a consistência entre o modo de programar de diferentes programadores da mesma linguagem. Quando se trabalha com outros programadores, tudo fica mais fácil quando todos seguem o mesmo guia de estilos.

Há linguagens onde os guias de estilos são mais adotados e populares do que outras. Quando você for programar em alguma linguagem, independentemente de qual seja, eu recomendo que você sempre procure saber se há guias de estilo da sua linguagem. Muitas vezes, pode haver mais de um, então sugiro que você procure saber qual é o mais adotado.

Enfim, tenha uma coisa em mente: seguir um guia de estilo vai ajudar a tornar os seus programas mais fáceis de ler e entender.

Tipo de dado

O tipo de dado de uma variável está associado ao conjunto de valores que a variável pode assumir. Exemplo: o tipo inteiro está associado a um conjunto de valores inteiros, que é limitado pelo número de bytes que podem ser usados para representar o valor.

O tipo de dado pode ser classificado em várias categorias, de acordo com as características deles. Por exemplo, eles podem ser escalar, que são aqueles que são representados por um único valor (pense no conceito de grandeza escalar da Física, que explica que grandeza escalar é aquela que contém apenas um valor), ou composto, que podem ser representados por múltiplos valores.

Tipos de dados escalares

Os tipos de dados escalares, que também são chamados de básicos ou de primitivos, se encaixam em três categorias: numérica (gue engloba números, sejam eles inteiros ou reais), lógica/booleana (que guarda true/false, ou seja verdadeiro/falso) e caractere (que guarda um caractere, que pode ser uma letra, um número, um símbolo, etc…).

A categoria lógica pode também ser considerada uma subcategoria da categoria numérica, já que na verdade, ela usa números. Mesmo assim, alguns, como eu, deixam ela em uma categoria separada. Isso também não está errado, já que o propósito dela é diferente do propósito da categoria numérica e ela é bem mais restrita quanto aos valores permitidos.

É importante lembrar que essas são categorias. Por serem categorias, elas podem englobar vários tipos de dados. Por exemplo, dentro da categoria numérica, a maioria das linguagens de programação tem tipos de dados diferentes para armazenar números inteiros e números reais. Muitas até têm subtipos para armazenar números reais com precisões diferentes (geralmente são os subtipos float e double). A categoria lógica é mais restrita e as linguagens contém apenas um tipo dentro dela.

Veja como é a declaração de variáveis com tipos de dados escalares no Portugol Studio:

inteiro x = 3
real y = 2.5
logico lampadaAcesa = falso
caracter c = 'K'

Atribuição de valores

Valores numéricos

Valores numéricos são escritos sem símbolos especiais. Nada de cifrão, mesmo se a variável que você estiver utilizando guardar algum preço de alguma coisa ou o lucro de uma empresa qualquer. Da mesma forma, se a sua variável representar alguma grandeza, como velocidade, você não deve colocar m/s após o número que representar o valor da variável.

Números inteiros devem ser escritos apenas com os dígitos dele. Números reais devem ser escritos com um ponto separando a parte inteira da parte decimal. Os detalhes extras (nos exemplos que eu citei, o $ e o m/s) devem ser adicionados quando você for imprimir esses valores para o usuário. Eles não fazem parte do valor da variável.

Exemplo de atribuição de valores numéricos em Portugol:

inteiro x = 3
real y = 7.4

A única situação em que um número, seja inteiro ou real, é escrito com alguma coisa a mais do que o número em si é quando se deseja expressar o número em uma notação diferente do padrão, que é a decimal. Essas notações diferentes não são suportadas por todas as linguagens. O Portugol suporta:

inteiro x = 0x7a84fb // número inteiro expresso em notação hexadecimal

Valores lógicos

Podem assumir apenas dois valores: verdadeiro ou falso. Veja o exemplo abaixo:

logico lampada_acesa = verdadeiro

Chars

Quando você atribuir um char a uma variável, coloque aspas simples. Um char representa apenas um caractere. Um caractere não precisa ser uma letra. Pode ser um dígito ou algum outro caractere, como uma vírgula. Exemplos:

caractere c = 'K'
caractere d = '9'

Tipos de dados compostos

Tipos de dados compostos são mais amplos do que os simples e armazenam vários valores. Eles podem ser arrays, strings, tipos definidos pelo usuário, dentre outros. Vamos ver apenas arrays e strings. Os demais tipos compostos variam de acordo com a linguagem, portanto não devem ser abordados aqui, senão os tutoriais ficariam presos a uma linguagem X ou Y, o que não é o propósito dos tutoriais.

Algumas linguagens (tais como PHP) permitem que o número de elementos que uma array pode armazenar seja alterado durante a execução e outras não permitem essa alteração (tais como C++).

Array

Uma array é um tipo de dado que armazena vários valores. Exemplos: uma lista de fornecedores de uma empresa ou uma lista com o número de vendas de um determinado produto em cada mês. Em algumas linguagens, esses valores precisam ser do mesmo tipo, como C++, e em outras, esses valores podem ser de tipos diferentes, como PHP.

Vetor

Uma array pode ter várias dimensões. Uma array unidimensional é chamada de vetor. Exemplos de declarações de vetores no Portugol Studio:

inteiro fotos[10]
inteiro vitorias_equipes[] = {3, 1, 2, 1, 1}
real precos_produtos[3] = {10.30, 3.45, 2.10}

No Portugol Studio, um vetor é declarado da seguinte forma: primeiro, informa-se o tipo da variável, seguido pelo identificador dela. Depois, coloca-se colchetes, que é o que indica que a variável é uma array. Se você quiser indicar o tamanho do vetor, basta informá-lo nos colchetes. O tamanho do vetor é obrigatório se os elementos do vetor não forem inicializados.

Se os elementos do vetor forem informados, o tamanho se torna opcional, porque ele pode ser inferido. Quando os elementos do vetor são definidos, eles devem ser colocados entre chaves e separados por vírgulas.

Matrizes

Como eu falei, arrays podem ter várias dimensões. Uma array unidimensional é chamada de vetor. Já uma array bidimensional é chamada de matriz. Pense que você pode querer armazenar a altura e o peso de um grupo de 10 pessoas. Fazer isso em vetores não seria bom. Ficaria bagunçado, estranho. O mais recomendado seria usar uma matriz (considerando apenas essas duas opções).

Seguindo o exemplo de usar uma matriz para armazenar a altura e o peso das 10 pessoas, a primeira dimensão da matriz poderia ter 10 elementos para armazenar os dados das 10 pessoas, e a segunda dimensão poderia ter 2 elementos, para armazenar o peso e a altura de cada uma das 10 pessoas. Ou seja, cada um dos elementos da primeira dimensão da array vai poder guardar dois dados, em cada uma das duas posições da segunda dimensão.

Exemplos de declarações de matrizes:

real dados_pessoas[3][2]
real dados_pessoas[3][2] = {{1.77, 78.0}, {1.54, 52.0}, {1.65, 57.0}}
real dados_pessoas[][] = {{1.77, 78.0}, {1.54, 52.0}, {1.65, 57.0}}

Veja que a declaração de uma matriz segue a mesma lógica da declaração de um vetor. Basicamente, se adiciona um colchete a mais, para indicar a segunda dimensão. E, na definição dos elementos da matriz, cada dimensão é definida dentro de suas próprias chaves. E as chaves são separadas por vírgulas. São chaves dentro de chaves. Note também que os tamanhos das dimensões da matriz podem ser inferidos, como acontece na terceira declaração.

Índices

Depois que uma array foi declarada e você definiu os elementos dela, você pode usar os elementos da array da mesma forma que você usuaria variáveis simples. Ou seja, você pode atribuir valores a elementos da array, imprimi-las, etc.

Cada elemento de uma array é diferenciado dos outros por um índice único, que é usado para acessar o elemento da array. Na maioria das linguagens, o índice é obrigatoriamente numérico (em outras, como PHP, também pode ser uma string).

Em linguagens onde o índice deve ser numérico, ele quase sempre indica a posição de um item particular dentro de uma matriz, pois os itens quase sempre começam do 0 e aumentam de 1 em 1. Uma das exceções é Pascal. Porém, em quase todas as linguagens, você começará com o índice 0. Isso é o mais comum.

Veja como definir e consultar elementos de vetores e matrizes:

inteiro vitorias_equipes[] = {3, 1, 2, 1, 1}
real dados_pessoas[3][2] = {{1.77, 78.0}, {1.54, 52.0}, {1.65, 57.0}}
escreva(vitorias_equipes[0])
vitorias_equipes[1] = 2
escreva(vitorias_equipes[1])
dados_pessoas[1][0] = 1.52
escreva(dados_pessoas[1][0])

Perceba como a sintaxe para definir e para acessar o valor de um elemento de uma array é a mesma. Se for um vetor, apenas informe o nome da array e coloque o índice entre colchetes. Se for uma matriz, adicione o índice da segunda dimensão também.

Convenções

Quando se nomeia arrays, programadores seguem as mesmas regras que eles seguem quando nomeiam variáveis. Muitos usam algumas convenções ao nomear arrays, como apenas usar o plural, (exemplo: velocidades_carros), ou usar uma palavra que implica um grupo, tais como lista_compras ou grupo_estudantes. Esse primeiro deve ser evitado, porque ele pode gerar confusão, já que lista é o nome de uma estrutura de dados.

Strings

Enquanto o tipo caractere serve para armazenar apenas um caractere, o tipo string armazena um conjunto de caracteres. Por isso, ele pode ser usado para armazenar desde uma palavra ou uma frase até o conteúdo de um arquivo de texto completo.

No Portugol Studio, o tipo string é chamado de cadeia (porque remete à cadeia de caracteres):

cadeia nome = "Lorraine"

Perceba que é bem semelhante às declarações dos tipos de dados escalares. A diferença é que o valor é um texto especificado entre aspas. A diferença é portanto conceitual: o tipo string é composto porque ele é uma cadeia de caracteres.

É importante ressaltar que uma string também pode conter apenas dígitos. Ou também exclamações. Ainda assim seria uma string. Uma string é uma cadeia de caracteres, e não de letras.

Tipagem

Assim como alguns outros temas cobertos nos tutoriais, esse não é um tema tradicional de Lógica de Programação, mas o autor resolveu colocá-lo porque julgou que falar sobre tipagem vai lhe ajudar quando você aprender a sua primeira linguagem de programação.

Inferência de tipo

O que é inferir? É deduzir. Então, a inferência de tipo nada mais é do que a dedução do tipo de uma variável. Então, em linguagens onde há inferência de tipo, o tipo da variável não é informado. Exemplos: PHP e Python. Exemplos onde isso não acontece: C++ e Java.

Exemplos de inferência de tipo em PHP:

$salario = 8000; // infere-se o tipo int
$nome = "Max"; // infere-se o tipo string

Já em C++, o tipo não é inferido:

int salario = 8000;
string nome = "Max";

Tipagem estática

Em linguagens com tipagem estática, o tipo de uma variável não se modifica. Por isso, essa tipagem é chamada de estática.

Geralmente, linguagens com tipagem estática requerem que o programador informe o tipo da variável quando ele declara as variáveis do programa. Mas isso nem sempre é necessário, pois uma linguagem pode ter tipagem estática e inferência de tipo ao mesmo tempo. Exemplos de linguagens com tipagem estática são C e C++.

Tipagem dinâmica

É o contrário da tipagem estática. Na tipagem dinâmica, o tipo de uma variável pode ser modificado durante o programa. Em linguagens que suportam esse tipo, não se indica o tipo da variável na declaração dela. Ao invés disso, usa-se apenas o identificador da variável e algum prefixo antes dela em alguns casos, como um $ no caso do PHP. Nele, eu posso inicializar uma variável com um número inteiro e depois mudar o valor dela para um texto tranquilamente:

$x = 30;
$x = "texto";

Isso é tipagem dinâmica. Em linguagens com tipagem estática, isso não é permitido.

Conclusão

Tipagem é um tema que gera muitas discussões nas comunidades de programadores. O objetivo foi introduzir o tema, explicando os conceitos de inferência de tipo e tipagem estática e dinâmica. Tem mais coisa para falar sobre tipagem, como tipagem fraca, tipagem forte e duck typing, mas não seria legal abordar nesses tutoriais. É melhor você ter uma experiência com uma linguagem de programação antes de entender esses conceitos.

Constantes

Além de variáveis, a maioria das linguagens de programação permite que você crie constantes. A única diferença entre uma constante e uma variável é que a constante não pode ter o valor dela alterado e geralmente precisa ter o valor dela definido no momento da sua declaração.

Casos de uso de constantes são quando você quer usar constantes matemáticas em um programa (como pi) ou quando você quer usar qualquer coisa que você sabe que não vai mudar durante o programa. Declarando essas coisas como constantes, há uma segurança a mais, porque se você tentar mudar o valor de uma constante, um erro vai ocorrer.

Em muitos guias de estilo, constantes seguem naming conventions diferentes de variáveis. Se o identificador de uma constante for uma palavra composta, geralmente as naming conventions determinam que seja usado o underline para separar as palavras e também é comum determinar que todas as letras da constante sejam maiúsculas. Basicamente, um snake_case com letras maiúsculas.

Declaração

No Portugol Studio, a declaração de uma constante é bem parecida com a declaração de uma variável. Basta colocar const no início:

const real PI = 3.14
const cadeia NOME_ABNT = "Associação Brasileira de Normas Técnicas"

Constantes anônimas

Existem também as constantes anônimas. Considere o pseudocódigo abaixo:

inteiro x = 9
real y = 8.2

Considere os números 9 e 8.2 de forma isolada. Esses números são chamados de constantes numéricas, ou constantes literais numéricas. Estou falando dos números isoladamente, não das variáveis às quais eles são atribuídos. O número isolado é uma constante literal, porque o valor dele é sempre o mesmo.

O mesmo ocorre com strings. Considere o pseudocódigo abaixo:

cadeia cpf = "999.999.999-99"

Da mesma forma que um valor numérico isolado é uma constante numérica, uma string olhada de forma isolada, esquecendo qualquer variável que possa estar ligada à ela, é uma constante de string.

Tanto a constante numérica como a constante de string são chamadas de constantes anônimas. Elas são chamadas assim porque não tem identificadores, mesmo que elas sejam atribuídas a uma variável. Olhadas de forma isolada, elas nunca contém identificadores.

Operadores e Expressões

Mini sumario

  1. Operadores de atribuição
  2. Operadores aritméticos
  3. Precedência de operadores aritméticos
  4. Operadores relacionais
  5. Operadores lógicos
  6. Operadores de caracteres

Operadores podem ser classificados de várias formas. Uma delas é de acordo com o número de operandos que eles têm. Na programação, geralmente há 3 tipos: unário, binário e ternário. Os mais comuns, com certeza, são os operadores binários.

Operadores de atribuição

Os operadores de atribuição são operadores binários. O operando da esquerda é o nome de uma variável e o operando da direita é um valor literal ou outra variável ou uma expressão. O operador da atribuição sempre opera da direita para a esquerda, o que quer dizer que a associatividade dele é da direita para a esquerda. Isso quer dizer que o valor da expressão à direita do operador de atribuição é avaliado primeiro, então o resultado é atribuído ao operando na esquerda. O operando na esquerda de um operador de atribuição deve ser um nome de uma variável.

Veja abaixo alguns exemplos de atribuições válidas:

x = 7
x = 4 * 5
x = y
x = y + 8

Veja abaixo alguns exemplos de atribuições inválidas:

7 * 3 = x
x - 8 = 10
x / 4 = y

Em cada um dos casos acima, o valor à esquerda do operador de atribuição não é uma variável. Por isso, as instruções são inválidas. Perceba a diferença entre expressões matemáticas e atribuições em linguagens de programação: na Matemática, as expressões acima seriam válidas. Já na programação, elas são inválidas. A programação tem uma base muito forte na Matemática, mas isso não quer dizer que ela é igual à Matemática. Há coisas que são válidas na Matemática, mas são inválidas na programação, assim como há coisas que são válidas na programação que são inválidas na Matemática.

Além do operador de atribuição composto apenas pelo =, quase todas as linguagens suportam operadores de atribuição que combinam o = com operadores aritméticos. Os que costumam ser suportados são os seguintes:

x += 3 -> x = x + 3
x -= 3 -> x = x - 3
x *= 3 -> x = x * 3
x /= 3 -> x = x / 3
x %= 3 -> x = x % 3

O Portugol suporta todos esses operadores. Os quatro primeiros são usados para combinar a atribuição com adição, subtração, multiplicação e divisão, respectivamente. O último combina o operador de módulo com a atribuição comum. O operador de módulo retorna o resto da divisão do primeiro operando pelo segundo. Assim, se x for 8, a atribuição x %= 3 é igual a 2, pois o resto da divisão de 8 por 3 é 2.

Operadores aritméticos

Todos os operadores aritméticos são binários. Eles já foram discutidos brevemente na seção de Operadores de atribuição. Cinco operadores representam as quatro operações básicas da matemática e a operação de módulo, que retorna o resto da divisão do primeiro operando pelo segundo. Veja exemplos de cada uma dessas operações:

  • Adiçãox = 5 + 6
  • Subtraçãox = 7 - 4
  • Multiplicaçãox = 5 * 3
  • Divisãox = 10 / 5
  • Módulox = 12 % 5

Algumas linguagens suportam operadores aritméticos adicionais, como por exemplo, de exponenciação. Geralmente, é usado um ^ para essa operação.

Precedência de operadores aritméticos

É possível combinar operações aritméticas. Quando isso é feito, cada operador segue regras de precedência que ditam a ordem na qual as operações serão realizadas. O operador com maior nível de precedência tem a operação dele realizada primeiro.

Na prática, nas linguagens de programação, a precedência de operadores envolve outros operadores, além dos parênteses e dos operadores aritméticos, mas esses são operadores mais gerais, que são comuns às linguagens de programação. Se eu falar sobre outros operadores, aí eu vou começar a entrar em operadores mais específicos e também em conceitos mais avançados, que não são o objetivo desses tutoriais, que são voltados especialmente para iniciantes.

Veja abaixo o nível de precedência dos operadores aritméticos e dos parênteses, em ordem decrescente:

  • () se existirem parênteses aninhados, os mais internos são avaliados primeiro
  • *, / e %
  • + e -

Caso a precedência de operadores seja igual, é usada a associatividade para determinar qual operação é realizada primeiro. Veja alguns exemplos de expressões para entender, na prática, como a precedência e a associatividade funcionam:

  • x = 3 + 5 / 2: temos duas operações aqui: adição e divisão. Como o nível de precedência dos dois é diferente, é realizada primeiro a operação cujo operador tem maior nível de precedência. No caso, é a divisão. Considerando que x é inteiro, 5 / 2 resultará em 2. Ficamos agora com 3 + 2. Agora, só há uma operação, então basta efetuá-la.
  • x = (3 + 5) / 2: Nesse exemplo, continuamos apenas com adição e divisão, só que como a adição está entre parênteses, ela é realizada antes da divisão, porque os parênteses tem maior nível de precedência do que a divisão. Então, ficamos com 8 / 2, que resultará em 4. Veja como o uso dos parênteses alterou o resultado final da expressão.
  • x = 3 + 5 – 6 / 2: Nesse exemplo, temos agora uma adição, uma subtração e uma divisão. Não temos parênteses. Temos operadores com níveis de precedência diferentes. A divisão é o operador com maior nível de precedência, então ela é realizada primeiro. Ficamos com 3 + 5 – 3. Agora, temos adição e subtração. Duas operações com o mesmo nível de precedência. Por isso, é usada a associatividade dos operadores, que nesse caso é da esquerda para a direita. Então, a adição é realizada primeiro, e depois a subtração. Ficamos então com 8 – 3, que é igual a 5.
  • x = (2 + 3 * (4 / 2) + (2 * 4)) % 2: Essa é uma expressão mais complexa, com bem mais operações e que usa parênteses em níveis diferentes. Como ela seria interpretada? Temos aqui adição, multiplicação, divisão, módulo e parênteses. Precedências diferentes? Sim. O que tem maior precedência? Os parênteses. Temos parênteses em níveis diferentes. Dois são mais internos e um é mais externo. Qual é avaliado primeiro? Um dos internos, porque os parênteses internos são avaliados antes dos mais externos. Qual dos internos é avaliado primeiro? Para decidir isso, é usada a associatividade, que é da esquerda para a direita. Assim, é avaliado primeiro o (4 / 2). Ficamos com (2 + 3 * 2 + (2 * 4)) % 2. Em seguida, os outros parênteses são avaliados: (2 + 3 * 2 + 8) % 2. Agora, temos ainda parênteses, mas dentro dele temos três operações: duas adições e uma multiplicação. A multiplicação é feita primeiro, porque tem maior precedência: (2 + 6 + 8) % 2. Depois, é feita a primeira adição da esquerda para a direita, por causa da associatividade: (8 + 8) % 2. Depois, é feita a adição que sobrou nos parênteses: 16 % 2. Finalmente, é feito o módulo, que resulta em 0.

Como você viu, os diferentes níveis de precedência dos operadores podem levar à resultados diferentes. Por isso, quando você estiver programando, se você tiver dúvidas, aconselho a usar os parênteses ou assimilar bem a tabela com os níveis de precedência dos operadores da sua linguagem de programação.

Se você assumir que operadores vão ter um nível de precedência diferente do nível de precedência real (ou seja, você se equivocar quanto à precedência de um operador), o seu programa vai ter um erro lógico que, dependendo do porte do seu programa, pode ser bem difícil de se detectar. Exemplo: vamos supor que você queira calcular a média entre três números e faça da seguinte forma: media = a + b + c / 3. Aí, o que vai acontecer? A divisão vai acontecer primeiro, e depois as adições, resultando em um resultado totalmente diferente do correto. Um erro lógico. A solução seria colocar as adições entre parênteses, para que elas sejam realizadas primeiro: media = (a + b + c) / 3.

Operadores relacionais

Operadores relacionais só podem ter como resultado um valor booleano, que pode assumir apenas os valores verdadeiro ou falso.

Esse nome, booleano, é uma homenagem ao matemático britânico George Boole, que no século XIX, expressou seleções lógicas com símbolos algébricos comuns. Mais tarde, essas seleções lógicas foram chamadas de lógica booleana e adotadas na computação. Cada decisão que um computador toma envolve avaliar uma expressão usando essa lógica booleana.

Os operadores de comparação mais comuns (todos binários), que estão presentes na maioria das linguagens de programação, são:

SignificadoSintaxe
igual==
diferente!=
maior que>
menor que<
maior ou igual a>=
menor ou igual a<=
Tabela de Operadores Relacionais

É importante ressaltar que algumas linguagens, como o PHP, também usam outros tipos de operadores de comparação.

Os símbolos acima podem variar de acordo com as linguagens de programação. E é muito comum que em pseudocódigos e fluxogramas sejam usados outros símbolos para os operadores de igualdade e diferença. No caso do operador de igualdade, é comum usar apenas um =. No caso do operador de diferença, algumas variações são o <> e o /=.

Veja abaixo um exemplo onde todos os operadores de comparação citados são usados:

inteiro x = 2

escreva(x == 2, "\n")
escreva(x != 2, "\n")
escreva(x > 2, "\n")
escreva(x < 2, "\n")
escreva(x >= 2, "\n")
escreva(x <= 2, "\n")

se (x > 0 ) {
    escreva("x é positivo")
}
senao se (x < 0) {
    escreva("x é negativo")
}
senao {
    escreva("x é nulo")
}
escreva("\n")

Veja que os operadores de comparação podem ser usados tanto dentro de estruturas de controle, como fora delas, em alguns casos. Eles retornam uma valor lógico. Assim, eles podem ser usados em qualquer lugar que aceitar um valor lógico.

Operadores lógicos

Operadores lógicos servem para conectar duas expressões ou para negar uma expressão. Os operadores lógicos mais usados são o e, o ou e o nao. Há também o operador xor.

O operador e

O operador e (and nas linguagens de programação) serve para conectar duas expressões, de modo que as duas precisam ser verdadeiras para que a expressão seja verdadeira.

Exemplo 1: você poderia querer verificar quais empresas com até 3 anos tem faturamento anual superior a R$ 3.000.000.

Você poderia expressar isso em pseudocódigo da seguinte forma:

se (idadeEmpresa <= 3 e faturamentoAnualEmpresa > 3000000) {
    escreva("Essa empresa é nova, mas possui faturamento superior a R$ 3.000.000.\n")
}

Exemplo 2: você poderia querer verificar se um aluno teve nota no ENEM superior a 700, nota em Matemática maior que 800 e nota em Redação de pelo menos 650:

se (notaEnem > 700 e notaMatematica > 800 e notaRedacao >= 650) {
    escreva("Aprovado!\n")
}

Note que três expressões foram unidas. Por isso, foram usados dois operadores e. Se fosse em uma linguagem de programação real, logicamente, não seria possível usar o e. Ao invés disso, geralmente se permite usar o and ou o &&. Exemplo em C++:

if (notaEnem > 700 and notaMatematica > 800 && notaRedacao >= 650) {
    ...
}

Como o e só é verdadeiro quando as duas expressões que ele conecta são avaliadas como verdadeiras, então se a primeira expressão for falsa, a segunda nem é avaliada. Por isso, uma dica de performance é colocar a expressão que tem mais chance de ser falsa primeiro.

O operador ou

O operador ou também é binário assim como o e, só que ele precisa que pelo menos uma das duas expressões que ele liga seja verdadeira, e não que ambas sejam necessariamente verdadeiras, assim como o e. Mas se as duas expressões forem verdadeiras, o operador vai retornar verdadeiro também. Ele só vai retornar falso quando as duas expressões forem falsas. O ou é representado como or ou || em muitas linguagens, como C++.

Veja abaixo um exemplo em C++ que verifica se uma equipe ganhou o último Campeonato Brasileiro ou a última Copa do Brasil:

if (campeaoUltimoCampBras == true || campeaoUltimaCopaBras == true) {
    ...
}

Essa expressão só vai retornar falso se a equipe não tiver ganho o Campeonato Brasileiro nem a Copa do Brasil. Veja um exemplo em pseudocódigo:

se (campeaoUltimoCampBras == verdadeiro ou campeaoUltimaCopaBras == verdadeiro) {
    ...
}

Também dá pra mesclar os operadores e e ou:

se (campeaoUltimoCampBras == verdadeiro ou campeaoUltimaCopaBras == verdadeiro e dividas == 0) {
    ...
}

Nesse caso, temos dois operadores lógicos diferentes. Qual será avaliado primeiro? Será que há alguma precedência de operadores lógicos? Sim, há precedência. Vou falar disso mais tarde.

Como o ou é verdadeiro se uma das duas expressões for verdadeira, se a primeira expressão for verdadeira, a segunda nem é avaliada. Então, uma dica de performance é colocar como primeiro operando aquele que tem mais chances de retornar verdadeiro.

O operador xor

xor é bem parecido com o ou. A única diferença é que pra que o resultado dele seja verdadeiro, só uma das expressões deve ser verdadeira. Na prática, ele é verdadeiro quando uma das expressões é verdadeira e a outra é falsa, pois ele precisa que pelo menos uma seja verdadeira, mas não ambas. Ele não é suportado no Portugol, nem no C++. Veja um exemplo em PHP:

if ($campeaoUltimoCampBras == true xor $campeaoUltimaCopaBras == true) {
    ...
}

O operador nao

Ao contrário dos operadores e e ou, o nao é um operador unário, o que quer dizer que ele contém apenas um operando. Ele é muito simples: ele serve apenas para negar uma expressão, invertendo o valor lógico dela. Em Portugol, ele é simbolizado pelo nao. Exemplo em pseudocódigo:

inteiro x = -2
se (nao (x > 0)) {
    escreva("x é nulo ou negativo")
}

Você também pode usar esse operador em uma atribuição, para inverter o valor de uma variável lógica:

logico maiorIdade = falso
inteiro idade = 20
se (idade >= 18) {
    maiorIdade = nao maiorIdade
}
escreva(maiorIdade)

Em outras linguagens, como C++, é possível usar o !, que é a versão mais usada, ou o not:

if (!(x > 0)) {
    ...
}

Tabela verdade

A tabela verdade é uma tabela que coloca expressões ou subexpressões nas colunas da primeira linha, e o valor true (ou verdadeiro) ou false (ou falso) ou ainda T (ou V) ou F (essas versões mais curtas são mais usadas) em cada uma das colunas das linhas seguintes, para determinar a veracidade ou falsidade do que está sendo avaliado.

A tabela verdade pode ajudar bastante a entender os operadores que nós vimos, porque ela mostra quando cada um é verdadeiro e quando é falso. Segue a tabela verdade com os operadores que vimos, para duas expressões hipotéticas, x e y:

xynot xnot yx and yx or yx xor y
VVFFVVF
VFFVFVV
FVVFFVV
FFVVFFF
Tabela verdade

Precedência de operadores lógicos

Os operadores lógicos e e ou tem precedências diferentes. Assim, quando eles estão juntos em uma mesma condição, é executado primeiro o que tem maior precedência, que é o e, a menos que parênteses sejam usados para evitar isso. Veja um exemplo em Portugol que demonstra isso:

inteiro idade = 15
logico epocaEleicao = falso
se (idade < 16 ou idade > 70 e epocaEleicao == verdadeiro) {
    escreva("É época de eleição, mas você não pode ou não precisa votar.")
}

Esse algoritmo tem um erro lógico. Como o e é executado primeiro, apesar de estar após o ou, por ter precedência maior que ele, então se a pessoa tiver menos de 16 anos, não importa se é época de eleição ou não, ele vai exibir a mensagem. Isso não é o desejado. Para exibir a mensagem, é necessário que seja época de eleição e que a pessoa não possa ou não seja obrigada a votar. Para corrigir, é necessário usar parênteses para forçar a execução do operador ou antes do operador e:

se ((idade < 16 ou idade > 70) e epocaEleicao == verdadeiro) {

Se a precedência dos operadores fosse igual, seria usada a associatividade para determinar quais operadores seriam avaliados primeiro.

Operadores de caracteres

Vou falar sobre o operador de caractere +. Ele é um operador de concatenação de strings (que são cadeias de caracteres), quando é usado com dois valores (variáveis ou constantes) que são um caractere ou uma cadeia de caracteres. Em algumas linguagens, esse operador é representado por outro símbolo. Por exemplo, no PHP, ele é representado por um ponto. O Portugol suporta o operador de concatenação. Veja um exemplo:

cadeia nome = "Mariano"
cadeia sobrenome = "da Rocha"
cadeia nomeCompleto = nome + " " + sobrenome
escreva(nomeCompleto)

Traga suas perguntas e dúvidas para o fórum e compartilhe com seus colegas ou verifique na seção perguntas e respostas se sua pergunta já não se encontra respondida lá.

O que você aprendeu:

– Fluxogramas (Norma ISO 5707/1985 e Dra.io)
– Tipos de Dados
– Operadores e Expressões (Atribuição, Aritméticas, Relacionais e Lógicas)

Material Extra

https://youtube.com/watch?v=4ZTsqUv4HGM%3Ffeature%3Doembed
Concepção Histórica Geral de Fluxogramas

Verified by MonsterInsights