29 de julho de 2021

O problema de Monty Hall e como estatística não é algo intuitivo

Quando estudamos estatística é muito comum esbarrarmos no problema de Monty Hall. Monty Hall era um programa de TV onde o apresentador oferecia três portas para uma pessoa, atrás de uma delas havia um prêmio e das outras duas alguma coisa desagradável para dar o tom de humor do programa. Aqui no Brasil tinhamos o nosso equivalente apresentado pelo Sérgio Malandra, a Porta dos Desesperados. Aqui temos uma explicação do problema com sua solução:

Apesar da explicação do vídeo e de todas as outras que eu já vi, ainda pode ser um problema muito difícil de abstrair, então para testar a hipótese eu criei um script em R (linguagem de análise estatística que eu estudava na época) para simular o problema conforme as condições acima e realmente os resultados bateram, quando o candidato muda a porta na chance que o apresentador dá as chances de acerto sobem para 2/3. O script está abaixo:

26 de julho de 2021

Melhorando as requisições assíncronas com Python - Parte 3

Este post é uma continuidade dos posts Lendo arquivos CSV em Python e fazendo requisições e do post Fazendo requests concorrentemente (assíncronas) no Python

Após fazermos as requests assíncronas no último post obtivemos uma melhoria signifcativa em nosso processamento, conseguindo paralelizar as requests e reduzindo muito o tempo. Porém não estamos fazendo o melhor proveito da API async do Python, percebam que cada função async também é responsável por também imprimir o resultado e o ideal seria retornarmos os resultados para tratar posteriormente. Vamos fazer isso:

A mudança foi bem sútil, apenas começamos a retornar o código de status da requisição e o payload. Agora na nossa função main precisamos receber estes resultados e iterar sobre eles.

Veja que agora passarmos armazenar o resultado de nossa função em duas variáveis, done e pending. Na variável done estão todas as tasks que foram concluídas e na pending as que ainda não terminaram, como por padrão a função asyncio.wait espera todas concluirem a variável pending sempre retornará um conjunto vazio. Agora o que precisamos fazer é iterar sobre os resultados retornados e fazer o processamento necessário sobre eles:

No código acima iteramos sobre as tasks e passamos vamos coletando todos os resultados através do método .result(). Com isto já temos como processar diversos resultados de uma vez. O único ponto aqui é que como disparamos todas as requisições do nosso CSV de uma vez, pode acontecer de sermos bloqueados pelo serviço (imagine que o CSV tenha vinte mil registros), ou até pior, poderíamos derrubar o serviço em questão como se fosse um ataque DoS. Resolver isso é bem simples, podemos dividir nossa lista em pedaços de cinco (para isso vou utilizar o código deste post do Stackoverflow que deve ser o trecho de código de três linhas mais copiado da história, risos) de tamanho fixo para realizar este processamento e acumular os resultados para o final:

Com isso agora temos um script que faz o processamento em paralelo porém sem oferecer riscos para o serviço que estamos chamando. Podemos aumentar o número de requisições em paralelo de acordo com o serviço e também podemos salvar os resultados em outro arquivo, o que vai depender da sua necessidade, mas o que fizemos até aqui já é uma base que permite criar scripts muito úteis e também aprendemos a utizar as bibliotecas async do Python para fazer requisições em paralelo, podendo utilizar estes conceitos de diversas outras formas.

22 de julho de 2021

Executando scripts no banco PostgreSQL com o psql

As vezes precisamos executar scripts grandes, como inserir uma massa de dados muito grande ou fazer diversas queries ao mesmo tempo. As ferramentas de bancos de dados ajudam muito nisso, mas as vezes o script pode ser tão grande que a ferramenta não aguenta e pode encher muito a memória (caso que aconteceu comigo recentemente onde o DBeaver tinha onze mil inserts para fazer). 

Felizmente existe uma ferramenta de linha de comando muito prática chamada psql, ele é mantido também pelas mesmas pessoas do PostgreSQL e é muito eficiente. O psql é um utilitário de linha de comando que permite executar comandos no banco de dados e também executar scripts em lote. Um exemplo de execução poderia ser assim:

psql -h localhost -d banco_de_dados -u usuario -f=arquivo.sql

Com este simples comando já é possível rodar scripts intensos no banco de dados. Existem muito mais opções disponíveis que vale apena conferir no manual, como por exemplo extrair a saída em HTML, rodar os comandos um a um para análises e muito mais.

19 de julho de 2021

Adicionando alterações em commits antigos com o rebase interativo

Durante o desenvolvimento é muito comum encontrarmos alterações para fazermos em arquivos que já foram alterados e não estão mais no último PR. Para não gerar um grande número commits de ajustes pequenos podemos reorganizar estas alterações utilizando o rebase interativo.

Preciso deixar apenas um aviso importante, como alteramos a ordem dos commits isso irá gerar uma reescrita de histórico de commits, assim caso você já tenha feito o push da versão anterior para uma branch remota será necessário refazer o procedimento com o famigerado --force, o que pode trazer perdas de commits caso essa branch esteja sendo usada por outra pessoa, outro ponto, evite fazer reescrita de histórico na branch principal do projeto.

Vamos considerar o seguinte cenário: temos um pequeno projeto, primeiro eu iniciei o repositório e criei um arquivo README.md e commitei, depois criei as funções utilitárias no módulo lib.js e commitei. Por último criei o main.js para que chamará a função em questão. Todas as alterações ainda estão na minha máquina e não mergeei na master remota. A estrutura ficou como a imagem abaixo:

Estrura de arquivos e os três commits iniciais
Após isso percebi que é necessário fazer uma correção no arquivo lib.js. A primeira coisa que faço então é esta alteração e commito ela:

Agora o fix está como o último commit
Partindo do principio que esta correção era necessária, podemos entender que todos os commits desde a criação da biblioteca de funções lib.js até a correção possuem um bug e estão quebrados. Como estas alterações ainda estão na minha branch, porque não podemos organizar isso melhor? Ai entra o rebase interativo.

O rebase interativo permite reorganizar nossos commits, alterar mensagens e diversas outras coisas, para iniciar o processo no Git é muito simples, basta você rodar o comando git rebase -i HEAD~3, traduzindo: inicie o rebase interativo a partir de três commits a partir da HEAD (o último commit). Ao executar temos a seguinte tela:

Temos a lista de commits e um manual do que temos disponível no rebase
A janela que abre contém a lista de commits e algumas instruções do que podemos fazer. No nosso caso queremos juntar o commit Fix na lib.js com o commit Criada lib de funções, para isso vamos fazer duas coisas, reordernar os commits e pedir para fazer um fixup no commit de fix, ficando:

Os commits foram reordenados pedimos para aplicar fixup no commit de fix
O comando fixup irá fundir o commit de sua linha com o de cima (no caso o commit mais antigo), juntando as alterações em um único commit. Agora podemos salvar e sair para assim o rebase prosseguir até o fim:

Rebase aplicado e commits organizados
Após isso os commits estarão mais organizados, isso ajudará você a navegar melhor pelo seu histórico de commits, ajudará também os revisores que poderão fazer o review por commits e não ficar pescando trechos de códigos em diversos commits diferentes.

15 de julho de 2021

Achados: aprendendo Node.js

Comecei a trabalhar recentemente com Node.js e estou curtindo bastante as particularidades dele. Além do Javascript existe todo um mundo de conceitos por trás do Node.js, na forma como ele foi construido e sua arquitetura. 

A único "defeito" do Node.js para mim, que na verdade é algo do Javascript, é que se você for iniciante em programação logo de cara terá que lidar com a questão de programação concorrente, eventos e coisas um pouco mais complexas, o que em outras linguagens muitas vezes ficam para um segundo momento serem aprendidas. 

Um conteúdo que me ajudou muito a entender o modelo de concorrência do Javascript e como ele funciona por debaixo dos panos é a série de artigos Node.js por Baixo dos Panos do Lucas Santos. Recomendo muito a leitura, principalmente se você já se sente mais confortável com o Javascript e quer dar um passo além.

12 de julho de 2021

Fazendo requests concorrentemente (assíncronas) no Python - Parte 2

Este post é uma continuação direta do post Lendo arquivos CSV em Python e fazendo requisições - Parte 1

Depois de conseguirmos ler o CSV e conseguirmos fazer requisições em lote, podemos agora iniciar uma primeira otimização, realizar as cotações em paralelo. Para isto usaremos a API assíncrona do Python em conjunto com uma nova biblioteca de requests, a HTTPX.

A primeira coisa que faremos é alterar a biblioteca Python que utilizamos, adaptar a requisição e nosso programa para começar a trabalhar assíncronamente. As alterações são principalmente na definição das funções e também nas mudanças para as chamadas das funções assíncronas que agora precisam do await antes:


Após isso também precisamos rodar o novo main assíncrono através de um EventLoop. Python possui diversos modelos de concorrência e paralelismo, o que usaremos aqui são as corrotinas, que são rotinas que funcionam de maneira cooperativa, isto é, quando uma função chega num ponto onde ela aguardará um determinado tempo ela cede a execução do interpretador para outras atividades. No Python essa concessão ocorrerá em momentos de entrada e saída de dados, como a leitura de um arquivo, acesso a um banco de dados ou, como nosso caso, a requisição de uma API web. Uma explicação simples como funcionam as corrotimas pode ser vista aqui.

"Legal Lucas, mas executei aqui e ainda está executando uma request de cada vez!"

Sim, este é um ponto, mesmo já utilizando a API async do Python, nosso código ainda não está executando as requests concorrentemente. Para isso precisamos preparar as corrotinas, convertendo elas em tarefas (tasks) e executa-las concorrentemente através de uma API oferecida para o Python:

Agora as coisas ficaram bem mais interessantes, quando executamos percebemos que simplesmente é disparado na tela todos as impressões de retorno uma única vez e fora de ordem. 

Podemos também fazer uma comparação de tempo de execução da versão síncrona e da versão assíncrona:

Perceba que a diferença de tempo é absurda, sendo 2.17 segundos na versão síncrona contra 0.46 segundos na versão assíncrona. Mas ai você se pergunta: "Por que então não fazemos tudo assíncrono e em paralelo?". Essa é uma pergunta simples com uma resposta difícil, várias coisas entram em jogo quando começamos a realizar operações assíncronas, só vou citar uma delas para inicio de conversa, a memória. Com todas estas operações sendo executadas concorrentemente o computador armazena todos os estados em memória ao mesmo tempo, ao contrário da execução síncrona que executa uma vez, libera memória e vai para o próximo processamento. Existem tópicos sobre essa questão ainda muito mais críticos, como as condições de corrida (racing conditions), mas não é o objetivo aqui se estender neste assunto nesta séries de posts.

Ainda podemos adicionar um controle mais fino desse processamento em paralelo, como executar em lotes o processamento para não sobrecarregar o servidor que estamos chamando e nem a nossa máquina. Vamos tentar fazer isso num próximo post, além de alguns últimos acréscimos interessantes.

8 de julho de 2021

Aprendendo a desenvolver jogos

Já faz algum tempo em que estou interessado a aprender a desenvolver jogos, apenas por diversão e aprendizado mesmo. A área de desenvolvimento de jogos tem algumas coisas muito diferentes do desenvolvimento que estou acostumado, por exemplo, em alguns momentos é comum carregar tudo na memória no inicio da execução para depois só ir exibindo, algo que as vezes pode ser dispendioso em um programa que precisa atender diversas requisições o mais rápido possível.

A pouco tempo escrevi uma resenha sobre um livro que curti muito para quem está querendo começar a aprender os algoritmos básicos de desenvolvimento de jogos, você pode ler mais sobre ela aqui.

Dando continuidade ao aprendizado eu fiz um curso de desenvolvimento de jogos com Lua na Alura. O curso segue a mesma linha do livro, é bem básico mas passa pelos conceitos de uma maneira mais rápida, falando menos sobre os detalhes dos conceitos, mas ainda assim consegue passar por todos eles. No inicio ele aborda os conceitos básicos da linguagem Lua, muito utilizada em diversos lugares, principalmente como uma linguagem de script para programas escritos em outras linguagens. Após essa introdução já é iniciado o desenvolvimento do jogo com a biblioteca LÖVE, que é capaz de fornecer todo o framework básico para lidar com imagens, sons e também o esqueleto básico de um jogo, como o game loop. 

Desta vez foi desenvolvido um jogo de avião tipo arcade em que precisamos destruir meteóros que aparecem na tela. O legal de fazer este curso após ter lido o livro de GameDev é que os conceitos se firmam de maneira muito mais simples e temos agora um framework básico para ajudar com alguns pontos, ao contrário do livro que se propunha a repassar por estes conceitos para termos um primeiro contato.

14Bis vs Meteoros:  Santos Dummont não esperava por essa
Desenvolver este jogo foi muito divertido e mais uma vez tomei a liberdade de fazer algumas escolhas que iam em outros caminhos apresentados pelo curso, acrescentando umas funcionalidades básicas. O desenvolvimento de jogos é uma área incrível e o que mais me fascina é que seu conjunto de práticas as vezes vai contra o que pregamos no dia a dia do desenvolvimento mais comum, ajudando assim a expandir cada vez mais os nossos conhecimentos.