Executando verificação de segurança...
3

C e Pensamento Computacional - Week 01 CS50

Um pouco sobre o CS50

O CS50 é um curso introdutório de Ciência da Computação da Universidade de Harvard. Ele é dividido em semanas, começando pela semana 0 com o Scratch, e vai até a semana 10, que aborda conceitos de Inteligência Artificial.

O curso é ministrado por David J. Malan, professor formado e doutor em Ciência da Computação pela própria Harvard. Embora o conteúdo seja atualizado anualmente, o objetivo principal do CS50 não é apresentar as tecnologias mais populares do momento, mas sim ensinar os fundamentos essenciais para quem deseja trabalhar com desenvolvimento de software.

Ao longo do curso, passamos por:

  • Scratch, uma forma visual e intuitiva de aprender lógica computacional;
    
  • C, para aprender os fundamentos da programação de baixo nível, como arrays, algoritmos, memória e estruturas de dados;
    
  • Python, SQL, HTML/CSS/JavaScript e Flask — um framework web em Python
    

C

Embora exista debate sobre o C ser uma linguagem de alto ou baixo nível, essa discussão foge tanto da minha competência quanto do foco do curso. O que realmente importa é que aprender C nos obriga a lidar com aspectos fundamentais da computação, o que proporciona uma compreensão mais profunda da programação, pois estamos lidando diretamente com a memória da máquina. A forma como declaramos as variáveis também importa, se não o código nem compila.

Ao iniciarmos a aula da semana 1, somos introduzidos a alguns conceitos importantes da linguagem C, vamos listar eles aqui.

Estrutura de um programa em C

  1. Todo programa começa pela função main()
  • Exemplo básico:
#include <stdio.h>

int main(void) {
    printf("Hello, world!\n");
    return 0;
}
  1. Compilação
  • O código em C é compilado em etapas:
  • Preprocessamento, Compilação, Assembly, Linkagem. Ao final, o código-fonte é transformado em um executável. Vou dar um breve resumo de cada uma das etapas:
  1. Pré-processamento
  • Responsável por interpretar diretivas como #include, #define, #ifdef
  • Inclui bibliotecas, substitui macros, e remove comentários
  1. Compilação
  • Traduz o código pré-processado para Assembly(código de máquina simbólico)
  • Verifica sintaxe, tipos, declarações e gera instruções para a CPU
  1. Montagem
  • Converte o código Assembly em código de máquina binário (formato objeto)
  • Cada .c vira um .o (object file)
  1. Linkagem
  • Junta vários arquivos objeto e bibliotecas para formar o programa final executável
  • Resolve símbolos (como a posição do printf), lida com endereços de memória e liga funções externas

Por isso é necessário adicionar um compilador a sua máquina quando se programa em C, C++. Atualmente eu tenho preferência pelo GCC, entretando existem outros disponíveis para experimentar. O compilador também serve como uma maneira de comunicar falhas no seu código, indicando linhas em que estão estes erros, podendo ser de sintaxe, léxico, etc(provavelmente um ";").

Os exercícios propostos para a semana 01

Ao final de cada aula, são disponibilizados exercícios para praticar os conceitos apresentados. Acredito que para alguém com mais experiência na área e uma lógica forte, não terá dificuldades em resolver as questões(existem algumas cabreiras, mas ainda não chegamos lá). É preciso lembrar também que são questões feitas para alunos de Havard, então talvez não sejam o "simples" que se espera de um curso da Udemy, ou até mesmo em algumas faculdades do país.

Mario (less)

Pessoalmente, eu sempre opto por resolver o exercício mais simples, e manter o mais dificil como uma revisão que utilizarei eventualmente. Dito isso, vamos ao exercício: é proposto que desenhemos com "#" as pirâmides do jogo Super Mario Bros., aquele amontoado de quadrados, sabe? Minha solução ficou dessa forma:

    #include <cs50.h>
#include <stdio.h>

void print_row(int spaces, int bricks);

int main(void)
{

    // The user defines the number of bricks

    int n;

    do
    {
        n = get_int("Height: ");
    }
    while(n < 1 || n > 8);

    for (int i = 0; i < n; i++)
    {
        int spaces = n - i - 1;
        int bricks = i + 1;
        print_row(spaces, bricks);
    }
}

void print_row(int spaces, int bricks)
{

    // Create Spaces

    for (int i = 0; i < spaces; i++)
    {
        printf(" ");
    }

    // Create bricks '#'

    for (int i = 0; i < bricks; i++)
    {
        printf("#");
    }
    printf("\n");
}

Por padrão ao curso, eu opto por manter meus comentários em inglês.

  1. Cabeçalhos:
#include <cs50.h> //Biblioteca padrão do curso, ele abstrai alguns comandos, principalmente de entrada, como o get_int();
#include <stdio.h> //Biblioteca padrão de C
  1. main()
// Solicita ao usuário uma altura entre 1 e 8, repetindo enquanto for inválida.
int n;

do
{
    n = get_int("Height: ");
}
while(n < 1 || n > 8);
  1. Loop de construção da pirâmide
// i representa o andar atual da pirâmide
for (int i = 0; i < n; i++)
{
    int spaces = n - i - 1;
    int bricks = i + 1;
    // spaces: número de espaços antes dos blocos. Diminui a cada linha.
    // bricks: número de blocos #, que aumenta a cada linha.
    // Chama a função print_row(spaces, bricks) para imprimir a linha correta.
    print_row(spaces, bricks);
}
  1. Funçao print_row(int spaces, int bricks)
// Imprime os espaços em branco no início da linha.
for (int i = 0; i < spaces; i++)
{
    printf(" ");
}
// Imprime os blocos # após os espaços.
for (int i = 0; i < bricks; i++)
{
    printf("#");
}
// Finaliza a linha.
printf("\n");

Cash

A proposta é simples: dado um valor em centavos, calcular quantas moedas de 25¢, 10¢, 5¢ e 1¢ são necessárias para dar o troco com o menor número de moedas possível. Sinceramente o que mais aprendi aqui foi sobre o nomeclatura das moedas que eles usam (CS50 também é cultura). A lógica por trás é de um algoritmo ganancioso: Sempre usamos a maior moeda possível em cada etapa.

Vamos ao código.

#include <cs50.h>
#include <stdio.h>

int calculate_quarters(int cents);
int calculate_dimes(int cents);
int calculate_nickels(int cents);
int calculate_pennies(int cents);
int sum_cents(int cents);

int main(void)
{
    // Prompt to ask how many change i need to give;
    int cents;
    do
    {
        cents = get_int("Change owed: ");
    }

    while (cents < 0);

    // Sum and exibes the necessary coins
    int total_coins = sum_cents(cents);
    printf("Total coins used: %d\n", total_coins);
}

int calculate_quarters(int cents)
{
    // Calculate how many quarters you should give the costumer
    int quarters = 0;
    while (cents >= 25)
    {
        quarters++;
        cents = cents - 25;
    }
    return quarters;
}

int calculate_dimes(int cents)
{
    // Calculate how many dimes you should give the costumer
    int dimes = 0;
    while (cents >= 10)
    {
        dimes++;
        cents = cents - 10;
    }
    return dimes;
}

int calculate_nickels(int cents)
{
    // Calculate how many nickels you should give the costumer
    int nickels = 0;
    while (cents >= 5)
    {
        nickels++;
        cents = cents - 5;
    }
    return nickels;
}

int calculate_pennies(int cents)
{
    // Calculate how many pennies you should give the costumer
    int pennies = 0;
    while (cents >= 1)
    {
        pennies++;
        cents = cents - 1;
    }
    return pennies;
}

int sum_cents(int cents)
{
    // Declares the values
    int quarters = calculate_quarters(cents);
    cents -= quarters * 25;

    int dimes = calculate_dimes(cents);
    cents -= dimes * 10;

    int nickels = calculate_nickels(cents);
    cents -= nickels * 5;

    int pennies = calculate_pennies(cents);

    // Sum the numbers of quarters, dimes, nickels, and pennies used
    int total_coins = quarters + dimes + nickels + pennies;

    return total_coins;
}

Sei que não é a maneira mais otimizada de solucionar este problema. Eu poderia ter usado divisão inteira e %, no entanto decidi priorizar a lógica e a estrutura do código, quase uma descrição lógica do passo a passo.

Considerações Finais

Gosto da didática do David, e como os exercícios são feitos para utilizar tudo aquilo que lhe foi ensinado durante a aula. Sei que poderia ter usado a biblioteca <math.h>, no entanto optei por resolver tudo na unha para me acostumar ao estilo do curso. Volto semana que vem com minhas considerações sobre a Week 2 - Arrays.

Sobre o SICP

Na minha postagem anterior, eu mencionei estudar o SICP também. Ainda o farei, no entanto a vida acontece e a vida pessoal tem exigido muito da minha atenção, então estarei optando para acompanhar este material após terminar o CS50.

Agradeço a atenção de cada um que parou para ler um pouco do que escrevi, entendo que minha didática não possa estar tão clara, e alguns conceitos podem ter sido explicados de forma incorreta, para isso estou aberto a críticas e direções. Até semana que vem.

Carregando publicação patrocinada...