
Existem vários tipos de programa: linha de comandos, aplicação gráfica, "apps" para smartphone, sites interativos e controladores de máquinas (sistemas embebidos) mas os princípios da programação aplicam-se da mesma forma a todos os tipos.
Os programas estão escritos em língua binária, uma língua que o computador entende perfeitamente mas que para nós, humanos, é ininteligível.
Para facilitar, existem linguagens de programação que estão a meio caminho entre a nossa língua nativa (ex: português) e a língua binária. Se a linguagem está mais próxima da língua nativa, é uma linguagem de alto nível. Caso contrário, será uma linguagem de baixo nível.
As linguagens de alto nível como o Python e o Kotlin permitem um desenvolvimento rápido mas são relativamente lentas a executar. Se queremos programas que executem rapidamente, devemos usar linguagens intermédias ou de baixo nível como o C e o Assembly.
Um algoritmo é o conjunto de passos para atingir um objetivo.
Para o algoritmo ser válido, a ordem dos passos deve ser respeitada, não pode haver ambiguidade nas instruções e tem que terminar em tempo útil
Os algoritmos podem ser representados de forma gráfica usando fluxogramas
Todos os fluxogramas podem ser desenhados usando apenas 4 figuras básicas: acção; início/fim; decisão e entrada/saída
As acções são normalmente atribuições de valores a variáveis. Todos os programas usam variáveis para ir guardando valores que mais tarde vão precisar.
Os programas recebem dados através do teclado (entrada de dados) e mostram dados através do écran (saída de dados)
As entradas e saídas de dados são do ponto de vista do computador. Para tal usam-se caixas do tipo trapézio nos fluxogramas.
Todas as linguagens de programação têm uma sintaxe própria: um conjunto de regras que indicam quais as instruções válidas e como é que se combinam as diferentes instruções de forma correta.
Isto é semelhante ao que acontece nas línguas dos vários países (ex: português, inglês, ...)
A semântica indica qual o significado das frases. Uma frase pode estar sintaticamente correta mas não fazer sentido - nesse caso dizemos que tem um erro de semântica.
Cada linguagem define as suas regras de sintaxe mas a semântica é responsabilidade do programador
As ferramentas de programação ajudam a identificar os erros de sintaxe (tal como o corretor ortográfico no Word)
Os erros de semântica não são identificados pelas ferramentas, só se revelam quando executamos o programa e nos deparamos com um destes comportamentos: crash; ciclo infinito ou resultado errado.
É uma boa prática começar por desenhar o fluxograma que resolve um certo problema, antes da implementação propriamente dita.
Neste módulo descreve-se o fluxograma para o programa que converte milhas em quilómetros.
No âmbito deste curso, os nomes das variáveis usadas em fluxogramas devem obedecer a certas regras: devem estar escritas em maiúsculas e sublinhadas, sem carateres acentuados e não devem conter espaços (usar o carater '_' em vez do espaço). Mas a regra mais importante é que devem ser auto-explicativos.
Outra regra importante é que os textos devem estar sempre entre aspas, para os distinguir dos nomes das variáveis. Para combinar texto fixo com variáveis, pode-se usar a vírgula (ex: "O número é ", NUMERO)
Neste fase, converter um fluxograma para uma linguagem de programação é simples - cada caixa do fluxograma corresponde a uma linha de código no programa. Mas primeiro é necessário perceber como é que essas linhas dão origem a um programa que consigamos executar.
Para isso, existem 2 ferramentas: compiladores e interpretadores
Um compilador transforma código fonte (as tais linhas de programação) num ficheiro executável
Um interpretador executa diretamente o código fonte, sem o transformar
Algumas linguagens usam um modelo híbrido em que combinam um compilador e um interpretador. Com isso conseguem "o melhor dos dois mundos", juntando as vantagens de uns e de outros.
A sintaxe de uma linguagem define as regras para escrever corretamente nessa linguagem
As linguagens de programação são constituídas por 4 componentes.
O primeiro componente são as palavras reservadas - palavras pré-definidas pela linguagem, como as palavras do dicionário
O segundo componente são os símbolos, como por exemplo o ponto, o mais e o menos, etc.
O terceiro componente são os identificadores, palavras inventadas pelo programador normalmente associadas a variáveis ou funções
O quarto componente são os valores - dados explícitos manipulados pelo programa como por exemplo 3, "olá", etc.
Normalmente existe uma ligação direta entre identificadores e valores. Por exemplo, o identificador idade pode estar associado aos valores 20, 23, 30, etc. consoante a idade da pessoa.
A memória RAM de um computador pode ser medida da seguintes forma:
bit
byte - 8 bits
kilobyte - 1000 bytes
megabyte - 1000 kilobytes
gigabyte - 1000 megabytes
Um programa tem que reservar espaço em memória para as suas variáveis. Essa operação designa-se de declaração de variáveis.
Quando se declara uma variável, tem que se indicar o seu tipo - é esse tipo que vai definir quando espaço é que essa variável irá ocupar em memória.
Cada linguagem define os seus próprios tipos. Por exemplo, em Kotlin existem os tipos:
Byte, Short, Int, Long - números inteiros (ex: 3)
Float, Double - números com casas decimais (ex: 3.57)
Char - caracteres (ex: 'p' ou '$')
Boolean - guarda apenas os valores true ou false
Errata
Onde se lê Megabtytes, deve ser Megabytes
Para declarar variáveis, usa-se a seguinte instrução:
var identificador: tipo
Não é possível colocar um valor numa variável cujo tipo não seja compatível com esse valor. Por exemplo, não é possível colocar o valor 50000 numa variável do tipo Short, pois o valor máximo que "cabe" nessa variável é 21767
Isso foi o que aconteceu no famoso crash do youtube, causado pelo vídeo do gangnam style. A variável que guardava o número de visualizações não estava preparada para o número gigante de visualizações que o vídeo do gangnam style estava a ter - o valor era superior ao máximo permitido pelo tipo da variável.
A declaração de variáveis permite reservar espaço em memória para as variáveis. Mas com que valores ficam inicializadas essas variáveis?
Podem:
Ficar com lixo - valores que "sobraram" dos programas que estavam a correr antes
Ficar com valores por omissões - valores que são automaticamente atribuídos pela linguagem
Ser obrigadas a ser inicializadas com um valor
O Kotlin adopta a terceira opção. Ou seja, é obrigatório inicializar a variável no momento da declaração:
var identificador: tipo = valor
Quando é possível adivinhar o tipo de uma variável a partir do valor com que é inicializada, podemos usar tipos implícitos. Nesses casos, não precisamos de indicar o tipo pois o Kotlin consegue perceber qual é automaticamente, como neste caso
var letra = 'c'
No caso dos números inteiros, o tipo implícito será sempre Int
No caso dos números com casas decimais, o tipo implícito será sempre Double
Os próprios valores têm tipo implícito mas é possível alterar o tipo implícito através de alguns modificadores:
Um 'L' no final do número indica que é Long
Um 'f' no final do número indica que é Float
Se estiver entre plicas é um Char mas se estiver entre aspas é uma String
O tipo String representa um conjunto de caracteres, ou seja, um texto.
Declarar uma variável significa reservar espaço em memória
Atribuir um valor a uma variável significa colocar um valor no espaço que foi reservado em memória
Num programa, posso declarar uma variável uma única vez, mas posso depois fazer várias atribuições (no fundo, ir mudando o valor da variável)
Regras a lembrar na utilização de variáveis:
Não é possível declarar a mesma variável mais do que uma vez
Não é possível atribuir um valor incompatível com o tipo da variável
Posso "forçar" o tipo de uma variável usando tipos explícitos como neste caso:
var numero1 : Short = 30
Há situação que não se podem forçar, mas podem-se usar primitivas de conversão para converter valores para tipos diferentes, como por exemplo:
var numero = 34.56.toShort() // a conversão vai descartar as casas decimais
Expressões aritméticas são fórmulas que produzem valores numéricos. Podem usar variáveis, valores e todos os operadores matemáticos.
Quando os elementos de uma expressão aritmética são todos do mesmo tipo, o resultado será desse tipo.
Quando os elementos são de tipos diferentes, tem que haver uma conversão prévia dos elementos, de forma a que tenham todos o mesmo tipo. Essa conversão é sempre feita para o tipo "maior" que existe na expressão.
Na maioria das linguagens de programação, os números com casas decimais são representados em vírgula flutuante
Chama-se vírgula flutuante porque a vírgula "flutua" ou seja pode mudar de posição. Por exemplo 1,23 e 12,3 podem ser representados pelo mesmo tipo e ocupam a mesma memória. Se usássemos vírgula fixa, esta estaria sempre na mesma posição, o que quer dizer que teríamos que acrescentar zeros à direita para a colocarmos na posição pretendida.
Há no entanto uma desvantagem na representação em vírgula flutuante: os números são guardados numa combinação de fracções em formato binário e isso significa que podem haver erros aritméticos quando fazemos operações com estes números. Mas em geral, isso não é um problema.
O tipo Char permite guardar caracteres como 'c' ou 'P'. No entanto os caracteres não são diretamente guardados em memória. Na realidade o que é guardado é um código numérico que representa esse caracter. Esses códigos são universais e estão descritos na tabela ASCII.
A tabela ASCII não é suficiente para representar todos os caracteres do mundo. Por exemplo, os caracteres chineses ou árabes não estão nessa tabela.
Surge então uma tabela muito mais extensa chamada Unicode. Esta tabela tem mais de 50000 caracteres.
Os valores em Unicode são representados assim: '\uXXXX' em que XXXX é o código Unicode.
Entrada de dados acontece quando entram dados no computador, por exemplo quando escrevemos num teclado.
Saída de dados acontece quando saem dados do computador, por exemplo quando aparecem mensagens no écran.
Nos fluxogramas, estas instruções são representadas através de trapézios.
Em Kotlin, a saída de dados para o écran faz-se através das instruções print e println .
Para se poder misturar texto fixo com variáveis, nas mensagens que enviamos para o écran, usa-se o $. Por exemplo:
println("O meu nome é $nome")
Quando queremos misturar variáveis com texto e não é claro o que é variável e o que é texto, pode-se delimitar a variável com chavetas:
println("O meu nome é ${nome}!")
A instrução readLine() lê uma linha do teclado. Lê todos os caracteres que o utliizador introduz até premir o enter. Por isso, retorna um valor do tipo String.
Se precisarmos de ler números, temos que usar as primitivas de conversão. Por exemplo, para ler um número inteiro:
var numero = readLine()!!.toInt()
Os dois pontos de exclamação são necessários para indicar ao Kotlin que estamos dispostos a assumir que o readLine() não retornará null. A utilização do null será explicada mais à frente.
Na versão 1.6 do Kotlin, foi introduzida a instrução readln() que funciona como alternativa ao readLine mas não obriga a usar os dois pontos de exclamação
O programa que vamos desenvolver nesta parte permite calcular o número de dias do mês. Consegue lidar com os casos mais simples (Janeiro, Março, Abril, etc.), com o caso de Fevereiro (para o qual pede adicionalmente o ano) e com os casos inválidos (o utilizador não introduz um mês válido)
Os meses têm dias diferentes (alguns 30, outros 31 e o Fevereiro que pode ter 28 ou 29) para tentar acertar o calendário com a órbita da Terra à volta do Sol.
A Terra demora exatamente 365,242 dias a dar uma volta completa ao Sol. Se dividirmos esse número por 12 (meses), não vamos ter um número inteiro, vamos ter um número que é aproximadamente 30,43.
Os Romanos começaram por criar um calendário em que os meses alternavam entre os 29 dias e os 31 dias. Ao fim do ano, totalizavam 360 dias, logo ainda lhes faltavam 5,242 dias para alinhar com o tempo de rotação à volta do Sol.
Após uma visita ao Egipto, evoluíram o calendário para muito próximo do atual - a única diferença era que Fevereiro tinha sempre 28 dias. Nesse caso, o ano passava a totalizar 365 dias. Ficava a faltar os 0,242...
Surge então a ideia dos anos bissextos, para que de 4 em 4 anos se compensasse este desfasamento.
Para que o desfasamento fosse realmente compensado, surge o detalhe desconhecido por muita gente que os anos 100, 200, 300, .. não são bissextos. Na realidade, a regra do ano bissexto é: todos os anos que sejam múltiplos de 4 mas que não sejam múltiplos de 100. A menos que sejam múltiplos de 400, aí também são bissextos!
À partida, esperaríamos que os programas se começassem a executar na primeira linha do ficheiro. Mas nem todas as linguagens funcionam dessa forma
Por exemplo, em Java e em C, existe um "marcador" chamado main que indica a linha onde o programa deve começar a executar.
Em Kotlin, podem-se usar ambos os modos:
Se o ficheiro tiver a extensão .kts, começa logo na primeira linha
Se o ficheiro tiver a extensão .kt, começa na linha a seguir ao main
Para melhorar a legibilidade do código, devemos indentá-lo - uma formação equivalente aos tópicos e sub-tópicos do Word.
Devem-se criar blocos de código (pedaços de código entre chavetas) que, no fundo, são equivalentes aos parágrafos da língua portuguesa.
A regra é:
Quando se abre uma chaveta, as linhas seguintes devem ser indentadas (empurradas para a direita)
Quando se fecha uma chaveta, volta-se à posição inicial
Para os programas terem um comportamento dinâmico, em que uma vezes fazem uma coisa outra vezes fazem outra, é necessário usarmos decisões ou condições.
Uma decisão é sempre uma pergunta, que só pode ter duas respostas possíveis: SIM ou NÃO
Estas decisões são representadas nos fluxogramas através de losangos
Todos os anos aparecem novas ferramentas e linguagens de programação mas os princípios fundamentais acabam por se manter. Isto é importante pois sem as bases fundamentais da programação, terás um esforço tremendo para te manteres atualizado.
Mais do que saber técnicas específicas, vais aprender a pensar como um programador. Saber olhar para os problemas de forma a serem resolvidos por um programa. Saber como fazer mas acima de tudo saber o porquê de ser feito dessa forma. Isso será feito analisando 4 programas exemplo, cuidadosamente escolhidos para servirem de guia a um leque abrangente de conceitos:
Conversor de milhas para kms
Calculador de número de dias de um mês (incluindo Fevereiro, anos bissextos, etc..)
Jogo "Estou a pensar num número, adivinha qual"
Criar gráficos de barras a partir de um ficheiro excel
Tudo isto será ensinado recorrendo a fluxogramas e à linguagem Kotlin.
Escolhi a linguagem Kotlin pois, sendo 100% compatível com Java, é muito simples de aprender.
A metodologia de ensino é a mesma que aplico nas minhas aulas na Universidade: todas as matérias são intercaladas com pequenos exercícios, para obrigar o estudante a raciocinar sobre aquilo que acabou de ouvir. No caso deste curso, os vídeos são curtos (2 a 5 minutos em média) e terminam sempre com um exercício. Que depois é resolvido em vídeo, com explicação detalhada para que não hajam dúvidas antes de se avançar.
Acima de tudo, meu principal objetivo é que o(a) aluno(a) adquira (ou aumente!) o gosto pela programação. Porque quando se gosta, é muito mais fácil aprender!