terça-feira, 30 de janeiro de 2024

Limpando o postgresql.conf

O Postgresql é relativamente simples de entender e configurar. Mesmo compilá-lo não é complicado.

Entretanto, o principal arquivo de configuração tem um excesso de documentação. Uma solução simples é guardar o arquivo original e limpar tudo o que não for essencial.

O que precisamos limpar são todas as linhas que começam com # ou estão em branco.


 cp postgresql.conf postgresql.conf.original
 grep -Pv "^\s*#" postgresql.conf | grep "\S" > postgresql.conf


O primeiro grep elimina todas as linhas que começam com um # e que talvez tenham espaços antes desse caracter. O segundo limpa todas as linhas em branco.

No meu caso, um arquivo com 979 linhas ficou reduzido a 18.

sexta-feira, 26 de janeiro de 2024

Grep com Contexto

O grep geralmente é usado em arquivos orientados a linhas. De quando em vez aparece algo mais complicado.

Se precisamos de mais contexto, podemos usar -B (before) e -A (after) para indicar quantas linhas queremos de antes e depois do que for encontrado:


  grep -B 4 -A 3 ERROR log.txt
  

E assim podemos ver 3 linhas para frente e 4 para trás.

Se o arquivo tiver delimitadores, podemos usar uma mágica do Perl. Considere o arquivo abaixo:


Registro 1
Nome: Fulano
Endereço: Rua Torta, 1
Sexo: M
Fim
Registro 2
Nome: Beltrana
Endereço: Rua Reta, 12
Sexo: F
Fim

Se eu quiser filtrar todos os registros com "Sexo: F", posso usar a variável $/, que indica o que é um separador de linha.


$ perl -ne "BEGIN{$/='Fim'} print if /Sexo: F/" pessoas.txt

Registro 2
Nome: Beltrana
Endereço: Rua Reta, 12
Sexo: F
$ perl -ne "BEGIN{$/='Fim'} print if /Sexo: M/" pessoas.txt
Registro 1
Nome: Fulano
Endereço: Rua Torta, 1
Sexo: M

O BEGIN serve para executarmos a atribuição apenas uma vez, porque a opção -n coloca tudo dentro de um "while(<>) {}". Mas se não nos importamos com isso, podemos simplificar com:


 perl -ne "$/='Fim'; print if /Sexo: M/" pessoas.txt
 

Podemos até incluir o \n no valor de $/ para não surgirem linhas vazias nos resultados. Para isso, também temos usar o qq() (quoted string) para não termos problemas com as aspas duplas:


 perl -ne "$/=qq(Fim\n); print if /Sexo: M/" pessoas.txt
 

Se os registros não tiverem os mesmos terminadores ou se o arquivo contiver outras estruturas que não nos interessam, podemos usar o operador flip-flop:


#!/usr/bin/perl
open(my $file, '<', $ARGV[0]);

my ($block, $found);
while(<$file>) {
    if(/^Registro/ .. /^Fim/) {
        $found=1 if /Sexo: M/;
        $block.=$_;
    } else {
        print $block if $found;
        undef $block;
        $found=0;
    }
}
print $block if $found;
  

Esse script recebe o nome do arquivo como parâmetro. A mágica acontece no "if(/^Registro/ .. /^Fim/)". O operador começa a retornar verdadeiro quando encontra Registro e passa a retornar falso quando encontra Fim. O resto do arquivo cai no else. O resto do script é óbvio.

Então, temos 3 opções para buscas progressivamente complexas em arquivos. Perl, como de costume, salva o dia.

segunda-feira, 22 de janeiro de 2024

Complicando o Problema dos Pintores

Um pintor pinta um quarto em 3h e o outro completa o serviço em 5h. Quanto tempo os dois juntos levam para pintar o quarto?

A gente poderia calcular o progresso por unidade de tempo, mas é melhor trabalhar com inteiros até onde der. Então, precisamos de um valor que os dois dividam. O mais óbvio é 3*5=15

Nesse tempo, o primeiro pinta 5 quartos (15/3) e o segundo pinta 3 quartos. Portanto, o tempo para os dois juntos pintarem um quarto será 15/(3+5): um pouco menos de 2h.

Os números 3 e 5 são primos, então o MMC deles só pode ser 3*5. Se os tempos forem 4 e 8, podemos usar 8 e a conta será 8/(1+0,5) ou 16/3.

Se usarmos 4 e 6, o MMC será 12 e a conta será 12/(3+2)=12/5.

O legal desse problema é que podemos usar qualquer quantidade de pintores. Se tivermos dois pintores lentos que levam 5h e um apressadinho que leva 3, a conta será 15/(5+3+3)=15/11.

Qual terá sido a contribuição de cada um?

Para calcular a contribução de cada um, basta multiplicar o recíproco do tempo de cada pintor pelo tempo total do serviço. Então, nosso amigo que leva 3h, terá contribuído com 15/8 * 1/3 = 15/24. O colega folgado contribuirá com 15/8*5 = 15/40. Com base nisso, podemos dividir o pagamento.

O MMC de 24 e 40 é 120 e então podemos confirmar que 15*5 + 15*3 = 120 e a nossa conta fecha 100%.

A conta fecha também com os três pintores: (15/(13*5))+(15/(13*3))+(15/(13*3))=5/13+3/13+3/13=1.

Se usarmos 3, 5, e 7, o produto será 105 e a soma 71 (105/3 + 105/5 + 105/7). É preciso ter cuidado para não esquecer que a soma é das divisões dos produtos por cada tempo, não os tempos em si.

Outra forma de ver a conta, é que é a soma dos produtos 2 a 2 (no caso de termos 3 pintores. Ou seja: 3*5 + 3*7 + 5*7.

Claro que só posso pensar numa solução n a n se o nominador for o produto de todos os tempos. Por exemplo, com os tempos 3, 5, e 5, seria preciso usar 3*5*5 para então calcular o denominador com 3*5 + 3*5 + 5*5. Note que 75/55=15/11. Usar o 15 seria um equivoco. Então o mais fácil é não misturar a combinatórica com o MMC. Use um ou outro, conforme o gosto pessoal.

Acho que basta deste assunto. Os pintores nunca vêm no horário combinado mesmo.

quinta-feira, 4 de janeiro de 2024

Niklaus Wirth (1934-2024)

O ano de 2024 começou com o falecimento do pai do Pascal, Niklaus Wirth.

Pascal foi a minha segunda linguagem de programação, depois do BASIC. Primeiro, tive que sofrer um pouco por não ter linhas numeradas; uma vez superada essa barreira, sofri um com ponteiros.

A linguagem, entretanto, ensinou-me bons hábitos e o esforço valeu a pena.

O ambiente em que eu aprendi a usar o Pascal merece uma nota: ISO Pascal da Acornsoft.

Para entender esse ambiente, é preciso entender um pouco sobre a máquina no qual ele rodava: o BBC Micro da Acorn.

Esse micro usava um 6502 e, portanto, só podia endereçar 64KB. Para contornar essa limitação, ele alternava um bloco de 16KB dentre os ROMs disponíveis. Esses ROMs podiam conter linguagens ou simplemente comandos para o Sistema Operacional; quando um comando era digitado na linha de comando, o SO procurava o código nos ROMs instalados. Havia também ROMs para Lisp, Forth, BCPL, Prolog, Logo, e COMAL.

Dentre os ROMs podia haver também chips de RAM! No caso do meu micro, havia 4x16KB. Essa memória podia se comportar como ROMs comuns (era possível carregar uma imagem de ROM a partir de um disquete, por exemplo), ou o programador podia usar como bem entendesse, uma vez que bastava colocar um valor num certo endereço de memória para trocar o ROM ou RAM visível para a CPU.

Havia mais algumas complicações, como 20KB de Shadow Memory que ficavam "atrás" da memória de vídeo e que podiam ser usadas para gráficos, desde que o programador usasse apenas as rotinas do SO. Havia também mais 12KB que o SO usava para não roubar mais RAM do usuário.

O Pascal vinha em 2 ROMs: um para o compilador e outro para o editor, as bibliotecas, e o interpretador de BL-code (um tipo de p-code). Esse BL-code foi inventado para contornar as limitações da máquina: simplesmente compilar para código de máquina era inviável, porque os binários ficariam muito grandes.

Como o compilador também estava em BL-code, para poder compilar um programa, o interpretador se copiava para a memória principal. Para rodar um programa já compilado, o interpretador podia ficar na área de ROM.

O editor ocupava apenas 4KB, mas tinha pesquisa com expressões regulares. É difícil acreditar nisso hoje em dia. Ele rodava no modo texto (40x25) e, portanto, gastava apenas 1KB de memória de vídeo. Os programas, entretanto, podiam ser rodados em qualquer modo.

Este foi o primeiro Pascal fiel à norma que rodou numa máquina sem disco. Ele recebeu elogios até da Primeira Ministra Margaret Thatcher; não que ela entendesse exatamente o que estava elogiando, mas elogiou.

A caixa incluía, além do software em ROM, um disquete (dava para rodar com disquete também), um manual, um cartão de referência, e um excelente livro entitulado: Pascal from BASIC. Afinal, os autores conheciam bem seu público.

E com essas boas lembranças, nos despedimos do grande Niklaus Wirth.