Eu uso algumas regras simples para decidir entre usar classes ou funções, em python:
- se existe necessidade de manter estado: classes;
- caso contrário: funções;
- para agrupar informação relacionada, tuples ou namedtuples.
Eu uso algumas regras simples para decidir entre usar classes ou funções, em python:
Algumas configurações pouco comuns mas muito úteis.
Num ambiente X ter teclas e rato rápidos… mas não demais.
xset m 1
xset r rate 220 80A segunda faz-me bastante falta, e é um efeito que não se consegue obter em Windows (excepto eventualmente se se usarem truques não triviais).
Cores para ecrãs de texto: letras verdes em fundo preto.
Costumava usar um simples green/black (testar=> xterm -fg green -bg black), mas os novos ecrãs de LED IPS têm um contraste muito maior, o que torna o “green” muito mais brilhante do que estava habituado.
Após alguma pesquisa, o chamado “kelly green” (fonte: wikipedia) provou ser o melhor verde para este tipo de painéis de alto contraste.
xterm -fg #4CBB17 -bg blackFontes mono-espaçadas para terminais e desenvolvimento: DejaVu Sans Mono Book.
Atenção à semelhança entre alguns caracteres. Ver se os seguintes são suficientemente diferentes para serem facilmente distinguidos:
i1lI O0Em particular gosto que o zero tenha algo no meio (risco, ponto) e que o “l” pequeno faça uma curvinha pelo menos em baixo.
A fonte “DejaVu Sans Mono Book” tem representações suficientemente diferentes. Outras boas alternativas:
Bom exemplo:
Mau exemplo:
Problema: precisamos de executar uma acção sujeita a limites temporais, não mais que n execuções por segundo são permitidas. Chamemos a isto throttle.
Como boa prática a função de throttle deve estar separada da acção f a executar. Uma abordagem funcional é perfeita: f = throttle(count, timeframe, f), tal que a função retornada por throttle() tenha o mesmo inteface que f.
Isto é precisamente o que fazem os decoradores em python.
Eis a minha implementação desta funcionalidade.
import datetime
def throttle(count, timeframe): # timeframe in seconds
def work(fn):
# state is lasttimemarker, remaining count
state = [datetime.datetime.now(), count]
def __work(*args, **kwargs):
elapsed = datetime.datetime.now() - state[0]
ntimeframes = int(elapsed.total_seconds() // timeframe)
if ntimeframes > 0: ## just an optimization
state[1] += count * ntimeframes
state[0] += datetime.timedelta(seconds=timeframe) * ntimeframes
if state[1] <= 0:
wait_time = timeframe - elapsed.total_seconds() % timeframe
time.sleep(wait_time)
return __work(*args, **kwargs)
state[1] -= 1
rv = fn(*args, **kwargs)
return rv
return __work
return work
####
import time
# exec 3 times on each 5 seconds timeframe
@throttle(3, 5)
def printtime():
print time.ctime(time.time())
for i in range(10): printtime()Sat Jun 28 17:16:08 2014
Sat Jun 28 17:16:08 2014
Sat Jun 28 17:16:08 2014
Sat Jun 28 17:16:13 2014
Sat Jun 28 17:16:13 2014
Sat Jun 28 17:16:13 2014
Sat Jun 28 17:16:18 2014
Sat Jun 28 17:16:18 2014
Sat Jun 28 17:16:18 2014
Sat Jun 28 17:16:23 2014
Uma idiossincrasia comum em python é iterar com ciclos for.
xs = "a b c d".split()
for x in xs:
print xa
b
c
dMas por vezes é necessário ter um contador associado. A solução mais imediata é manter um contador à parte e incrementá-lo dentro do loop. Por vezes é necessário trocar o for por um while.
Felizmente existe uma construção nativa que permite associar uma contagem a qualquer iterador: enumerate.
xs = "a b c d".split()
for i,x in enumerate(xs):
print i,x0 a
1 b
2 c
3 dO enumerate é a aplicação de uma técnica comum em linguagens funcionais: transformar uma lista de valores (escalares) numa lista de tuples, juntando a informação adicional no outro membro do tuple.
Repare-se que o enumerate corresponde à aplicação de duas funções do módulo itertools.
import itertools
xs = "a b c d".split()
for i,x in itertools.izip(itertools.count(), xs):
print i,x0 a
1 b
2 c
3 d
Um objecto de dados só tem estado, não tem comportamento. Ou traduzindo para python: são instâncias de classes sem métodos, apenas com variáveis.
Formas de definir classes de dados.
É a solução clássica e mais explícita:
class P:
def __init__(self, sln, dll=None, aspnet=None):
self.sln = sln
self.dll = dll
self.aspnet = aspnetCompacta, python voodoo.
class P:
def __init__(self, sln, dll=None, aspnet=None): self.__dict__.update(locals())Falta remover o self do próprio dicionário.
class P:
def __init__(self, sln, dll=None, aspnet=None): self.__dict__.update(locals()); del self.selfContinuando no exemplo acima, mas desta vez sem defaults.
from collections import namedtuple
P = namedtuple("P", "sln dll aspnet".split())
p = P("xpto.sln", "xpto.dll", None)
print p
# recuperando a solucao classica
class Pc:
def __init__(self, sln, dll=None, aspnet=None):
self.sln = sln
self.dll = dll
self.aspnet = aspnet
def __str__(self): return str([self.sln, self.dll, self.aspnet])
p = Pc("xpto.sln", "xpto.dll", None)
print pTem como resultado:
P(sln='xpto.sln', dll='xpto.dll', aspnet=None)
['xpto.sln', 'xpto.dll', None]Resumindo:
namedtuple fornece uma forma simples e legível de definir objectos de dados simples;Ao longo de 15 anos na área do desenvolvimento de software, sobretudo no mundo das telcos, eis o cenário que pinto sobre o estado de arte nacional.
A estratégia de TI empresarial está sujeita aos fornecedores tecnológicos.
O desenvolvimento nacional é pouco ambicioso e simples: a maior parte dos projectos desenvolvidos de raiz são uma implementação básica – ainda que por vezes massiva e/ou complicada – de um modelo source-process-dump. E.g. boa parte das aplicações web.
As necessidades de software mais exigente, do ponto de vista de complexidade, são normalmente supridas por soluções 3rd party.
A tecnologia escolhida tem pouco impacto no projecto e é irrelevante para a organização: mesmo que outras linguagens e plataformas melhorassem 10x a produtividade no desenvolvimento e deploy, esse tempo seria diluído nas ineficiências gerais do projecto.
A falta de qualidade do código é suprida via testes, ou seja, prática de code-and-fix.
As práticas de código mais comuns baseiam-se em excesso de programação imperativa mal estruturada com abuso de copy paste. Há evidência baseada em estudos que estes factores não melhoram com a experiência.
«Additionally, meetings give visibility, an essential factor to anyone who hopes to rise in big-company hierarchy. You don’t get noticed by listening thoughtfully, so anyone who’s there for visibility is likely to be a talker. The worst meetings feel like congregations of windbags with nobody listening and everybody speaking or waiting to speak. Because there are so many who need to speak, meeting duration increases seemingly without bound.»
– “Meetings, Monologues, and Conversations” in Peopleware: Productive Projects and Teams (3ª edição), Tom DeMarco, Timothy Lister
Custa assim tanto perceber?!
Esta passagem deste magnífico livro fez-me lembrar um episódio real. Há cerca de 10 anos participei num grande projecto. Inicialmente tinha sido contratado um consultor da Microsoft, temporariamente, para arrancar com a solução técnica, mas como ninguém da equipa inicial percebia o que ele estava a fazer, integraram-me no projecto para acompanhar e dar continuidade ao seu trabalho. Rapidamente me tornei o “arquitecto” de facto do projecto e um dos seus grandes motores, sobretudo a nível de inovação.
Esse projecto – bem como o meu papel no mesmo – veio recentemente a tema com uma pessoa que também tinha estado envolvida no mesmo, embora como parte interessada, externa à equipa. Ambos desconhecíamos o envolvimento do outro. A reacção dele: “Estiveste no projecto? Não me lembro de ti nas reuniões!” Q.E.D.
Objectivo: processar grupos de 3 linhas seguidas de um ficheiro.
Soluções…
with file("pythonices") as f:
lines = []
for line in f:
lines.append(line)
if len(lines) == 3:
process(lines)
del lines[:]A lista lines é usada para manter a contagem das linhas lidas. Chegando a 3, processa-as.
with file("pythonices") as f:
while 1:
try:
l1 = next(f)
l2 = next(f)
l3 = next(f)
process([l1, l2, l3])
except StopIteration:
breaktry-except para controlar a paragem do ciclo.with file("pythonices") as f:
for l1,l2,l3 in zip(f,f,f):
process([l1,l2,l3])Mais simples e legível.
E se o ficheiro não for múltiplo de 3? Os casos anteriores descartam as linhas extras. Com a última solução podemos controlar esse comportamento facilmente mexendo apenas na “fonte”. Neste caso usando itertools.izip_longest que funciona como o zip, só que convertendo os valores extra – caso os haja – num valor pré-definido.
import itertools
with file("pythonices") as f:
for l1,l2,l3 in itertools.izip_longest(f,f,f, fillvalue=None):
process([l1,l2,l3])A vantagem desta solução face às anteriores foi ter separado e abstraído a tarefa em duas instâncias: a obtenção das fontes e o seu processamento.
Criando funções que representam explicitamente as abstracções fica:
import itertools
def process(x): print x
def consume(f):
for l1,l2,l3 in itertools.izip_longest(f,f,f, fillvalue=None):
yield l1,l2,l3
with file("pythonices") as f:
for l1,l2,l3 in consume(f):
process([l1,l2,l3])De notar que não foram as funções que criaram as abstracções. As funções são estrutura. A primeira solução desta secção tem o mesmo nível de abstracção que esta última. Posso voltar no futuro ao tema abstracção.
Como é que consigo facilmente converter o texto “p_dist m show_acs curses tget termcap” num dicionário chave-valor, ou seja, nisto: {'tget': 'termcap', 'p_dist': 'm', 'show_acs': 'curses'} ?
Criar um dicionário vazio, iterar a lista dois a dois, fazer chave igual ao próximo.
t = "p_dist m show_acs curses tget termcap".split()
x = {}
for i in range(0, len(t), 2): x[t[i]] = t[i+1]
print xSubstituir a iteração 2 a 2, por uma construção funcional – zip – que extrai dois elementos de cada vez da lista. Para isso a lista precisa de ser encapsulada num iterador, que funciona como um consumidor linear da lista.
t = "p_dist m show_acs curses tget termcap".split()
x = {}
it = iter(t)
for k,v in zip(it, it): x[k] = v
print x[it,it] <=> [it]*2f(it,it) <=> f(*[it]*2)logo…
t = "p_dist m show_acs curses tget termcap".split()
x = {}
for k,v in zip(*[iter(t)]*2): x[k] = v
print xzip(*[iter(t)]*2) = [('p_dist', 'm'), ('show_acs', 'curses'), ('tget', 'termcap')]O lado direito é uma association list (alist), que é a representação python de um dicionário. Logo…
t = "p_dist m show_acs curses tget termcap".split()
x = dict(zip(*[iter(t)]*2))
print xNo limite podemos usar uma só expressão literal para criar um dicionário:
mymap = dict(zip(*[iter("""
chave1 valor1
chave2 valor2
chave3 valor3
""".split())]*2))
mplayer é mais que um video player da linha de comando (até porque também funciona como audio player). Além de aguentar com praticamente qualquer formato conhecido, também permite fazer processamento e conversões. O mencoder cria, ou converte, formatos de vídeo e som. mplayer -ao null FILE para ver sem som, ou mplayer -vo null FILE para ouvir sem imagem, devem ser os comandos multimédia que mais uso.ddrescue não aborta a leitura em falhas de hardware e, através da manutenção de um ficheiro de log, permite várias passagens de leitura da drive, gravando apenas os blocos não lidos nas passagens anteriores.top devia ter sido :)tail que funciona em directorias, monitorizando todos os ficheiros lá existentes e novos que entretanto lá sejam criados. Não há que saber: xtail /var/log control- para sair, control-c dá o status.buffer, mbuffer: o comando buffer implementa um buffer ao nível da shell. O racional é simples: um processo produtor de dados, um processo consumidor de dados, um deles é mais lento que o outro e sofre uma enorme penalização de performance quando resume um bloqueio. Põe-se o buffer no meio e já está :) O mbuffer implementa o mesmo conceito com mais algumas funcionalidades interessantes.rename: às vezes as ideias simples são as mais interessantes. rename FROM TO files... renomeia os ficheiros substituindo FROM por TO no seu nome, em que ambos são strings banais (nada de expressões regulares). Por incrível que pareça a esmagadora maioria de renames que faço encaixam neste modelo:
# xxx.jpeg => xxx.jpg
rename .jpeg .jpg *.jpeg
# t1.txt t2.txt ... t500.txt => t_001.txt t_002.txt ... t_500.txt
rename t t_00 t?.txt
rename t t_0 t??.txt
rename t t_ t???.txt
# t_001.txt t_002.txt ... t_500.txt => t1.txt t2.txt ... t500.txt
rename t_00 t t_00?.txt
rename t_0 t t_0??.txt
rename t_ t t_???.txtstar: não sei ao certo o que significa o s antes do tar, provavelmente será s de Schilling, o nome do autor da ferramenta, que também tem outros utilitários começados por s. Independentemente disso, para mim o s é de super, isto é um super tar. Funciona como tar, cpio ou pax e suporta as N variantes de formatos de arquivamento desenvolvidos ao longo da história do Unix. Tem opções de buffering builtin, lê listas de ficheiros a partir do stdin (não mais tar -cf foo.tar `find . expr`), correcto na preservação de todos os atributos dos ficheiros, usa expressões find builtin, detecta compressão automaticamente, por aí fora.
haskell, é bastante rápido.asciidoc.python já que foi criado como ferramenta de documentação oficial do projecto.reStructuredText.xpath, transformações xslt, até reformatação, está lá tudo. Útil para pequenos scripts e verificações rápidas. Dica: xml fo <file> para reformar xml.jing valida xml contra schemas RELAX NG, trang converte entre schemas. Bastante útil… quando é preciso. :)git só que mais elegante e compreensível. :)dirdiff: uma ferramenta gráfica de comparação de várias directorias, com capacidades de merging, cópia ou remoção de ficheiros. É um software antigo, em tcl/tk, ainda bastante útil. Depois de se apanhar o truque para editar os merges torna-se numa das ferramentas mais potentes para o efeito. Mas admito que o sistema escolhido seja um bocado confuso inicialmente.
nvi: o vi original há muito que passou a constar apenas nas tapes de arquivo dos sistemas BSD originais. Keith Bostic reimplementou-o como nvi (new vi), mantendo compatibilidade total face ao original com umas poucas funcionalidades extra de conveniência. O nvi manteve o espírito e tradição do antigo vi, é rápido, correcto (não tem bugs), consome poucos recursos, corre bem as minhas macros – deixando teclas livres para o fazer! – e faz bem o que deve fazer: editar texto. Totalmente a antítese do vim, mas isso são outras histórias.
VirtualBox: um bom sistema de virtualização, relativamente rápido, é excelente para uso pessoal ou pequenas equipas. 100% manipulável da linha de comando: vboxheadless -s vmname para arrancar, vboxmanage ... para todas as restantes tarefas.
make: é mais que uma ferramenta de construção de projectos – que aos poucos vai sendo substituído por ferramentas mais modernas e especializadas no domínio respectivo –, para mim funciona com um repositório de pequenas tarefas necessárias num contexto (ver: como são feitos estes artigos).
units: um programa de linha de comando com uma enorme base de dados de unidades científicas e alguma inteligência associada na conversão.
# quantas milhas por galão correspondem 6 litros aos 100 km
# notar que o programa detectou a inversão das grandezas (reciprocal conversion)
units 6l/100km miles/gallon
reciprocal conversion
* 39.202431
/ 0.025508622As motivações do spam são conhecidas e têm o seu racional, ainda que ilegítimo. Agora fazer spam assim ultrapassa qualquer nível de compreensão!
From - Wed Oct 26 10:26:54 2011
Return-Path: <loriannelvira@cnh.com>
Received: from cfirg6vf (Static-96.99.98.14.tataidc.co.in [14.98.99.96] (may be forged))
by ?????????? (8.14.4/8.14.3) with SMTP id p9Q9JrEL006873
for <??????????????>; Wed, 26 Oct 2011 09:20:02 GMT
From: "Darcel Karlyn" <loriannelvira@cnh.com>
Subject: 6rr4k5
MIME-Version: 1.0
To: <??????????????>
Date: Wed, 26 Oct 2011 12:16:11 +0200
Message-ID: <98e24f23r72-48175613-679n2l41@hnxbidy>
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit
jq
O LISP e todos os seus dialectos usam as conhecidas s-expr, que têm dois grandes problemas:
O David A. Wheeler criou o sistema readable com três truques para simplificar a sintaxe das s-expr, mantendo total compatibilidade com a linguagem:
Em três passos se simplifica brutalmente o calcanhar de Aquiles do LISP: a sintaxe. Claro que já vem tarde, mas isso é outra história.
Como utilizador veterano de Linux (e outros sistemas Unix no passado, não tanto agora) fui adoptando um grande número de ferramentas ao longo dos anos. Deixo uma pequena lista de alguns dos que mais uso e/ou considero interessantes, para não se tornar exaustiva. Ficaram de fora alguns dos tradicionais comandos de Unix (sed, awk, cat, dd, find, grep, …), software servidor (apache, samba, …) ou outros demasiados específicos.
Aviso: é natural que as minhas raízes command line se revelem um pouco.
stone proxy 8888 e tenho um proxy http lançado no porto 8888. Suporta mais protocolos e esquemas de repetição.netcat – cliente/servidor tcp/udp flexível. Um clássico, mas que ainda hoje uso. Servidor: nc -l -p 8888, cliente: echo ola | nc 127.0.0.1 8888. Para determinar rapidamente o contéudo de um pedido http:
[edit /etc/hosts "127.0.0.1 mysite"]
nc -l -p 80
[browse to http://mysite/urlpath]nmap -sV -O ip para tentar descortinar um pouco do que se está a passar, mas hoje em dia isto foge ao meu trabalho típico. A suite nmap inclui outros utilitários interessantes como o ncat (inspirado pelo netcat referido acima), ndiff e nping.curl é mais completo, permite fazer upload, suporta autenticação ntlm. O wget aceita vários URLs e funciona em modo recursivo. Complementam-se bem.rsync – inicialmente o objectivo do rsync era fazer transferências por rede optimizadas, i.e. permitia transferir apenas um conjunto mínimo de diferenças entre a origem remota e o destino local (ou vice versa), reconstruindo a mesma imagem no destino. Mas com o tempo, usar o rsync para copiar de qualquer sítio para qualquer sítio tornou-se um vício.
# actualiza em dest os conteúdos de source
rsync -av --delete /a/b/source /x/y/dest
# envia os meus dados para o servidor, via ssh, com compressão,
# restringindo a largura de banda usada a 50KB/s, para não me
# estrangular a minha ligação de rede
rsync -Pav --delete ~/stuff/. host:/stuff/. -e ssh -z --bwlimit=50cntlm – quando a Microsoft decidiu implementar um mecanismo de segurança proprietário, NTLM, sem qualquer documentação, nem suporte fora de Windows, o resto do mundo ficou às escuras. Nos sítios em que se usavam proxies com autenticação NTLM isso significava não ter acesso a nada. Felizmente alguém com tanto de generosidade como de genialidade fez o reverse engineering desse protocolo e implementou um proxy que se autenticava via NTLM num MS Proxy Server. Esse proxy era o NTLM Authorization Proxy Server, escrito em python. Mais tarde apareceu esta versão em C que trouxe algumas melhorias e funcionalidades. Exemplo real de utilização:
# cria um proxy http anónimo no porto 5866 que se autentica e usa o
# proxy windows NTLM_PROXY no porto 80
cntlm -g -d DOMAIN -u USER -p PASS -l 5866 NTLM_PROXY:80w3m, links2 – w3m é, para mim, o melhor browser em modo texto. Também funciona como paginador e até suporta tags. As outras incarnações de browsers texto (lynx, links, elinks) nunca me convenceram, excepto o links2 que adicionou suporte javascript e gráficos (links -g). De vez em quando dão jeito.
PlantUML – a partir de uma simples descrição textual o plantuml gera diagramas UML de sequência, casos de uso, classes, actividade, componentes, estado e objectos. Muito útil para documentar rapidamente fluxos e processos.
| —> |
plantuml. Os gráficos têm melhor qualidade, contudo.firefox ou standalone (é uma aplicação XUL). Surpreendemente funcional! O “Sketchy GUI” é excelente para prototipar mockups.ImageMagick, GraphicsMagick – ImageMagick é uma suite de utilitários para processar imagens da linha de comando, embora também inclua o display que é um utilitário gráfico. Assim, tarefas como converter entre formatos, alterar resoluções, aplicar efeitos ou outras operações em imagens está à distância de usar o nosso mecanismo de automatização preferido a envolver isto. Exemplo em shell+awk para re-escalar todas as imagens para 800x600:
# confirmar se está tudo ok
ls *.png *.jpg | awk -vR=800x600 '
function f(s) {sub(".[^.]*$", "_" R "&", s);return s}
{printf "convert -geometry %s \"%s\" \"%s\"\n", R, $0, f($0)}'
# executar
ls *.png *.jpg | awk -vR=800x600 '
function f(s) {sub(".[^.]*$", "_" R "&", s);return s}
{printf "convert -geometry %s \"%s\" \"%s\"\n", R, $0, f($0)}' | sh -xO GraphicsMagick foi um spin-off do ImageMagick feito em 2002 que reivindica ser melhor. Independentemente das pretensões dos autores, uma coisa que eu prefiro no GraphicsMagick é o uso do comando gm como driver dos outros subprogramas (gm convert, gm identify, …).
pinta – finalmente um programação de edição gráfica simples, funcional e usável, que funciona em Linux.
O uso do termo “recurso” como designação de pessoa tem-se vindo a generalizar e banalizar. Mas são as pessoas recursos?
Um recurso é algo do qual se faz uso para um propósito. Como recurso é indistinguível face a outros equivalentes ou iguais. Por exemplo, posso trocar um carro por outro modelo igual sem perda de funcionalidade ou necessidade de adaptação.
Ora as pessoas não têm “modelos iguais”. Se assim fosse não eram precisas entrevistas no processo de contratação, bastava consultar a ficha técnica da pessoa. Não era preciso haver período experimental, ou outros expedientes usados devido à individualidade de cada ser humano.
O próprio lugar-comum “a maior riqueza são as pessoas” enferma de uma contradição na asserção pessoa-recurso. Se as pessoas se trocam facilmente enquanto recursos, não constituem maior riqueza que os outros recursos da empresa, como instalações, veículos, etc.
Resumindo: uma pessoa é um ser humano, não é um recurso.
Por incrível que pareça há pessoas que têm dificuldade em tratar as outras pessoas por… pessoas.
Notas:
Outras leituras:
Por vezes bastam pequenas ideias para nos melhorarem a vida, e o ACK é um desses casos. Quantas vezes não sentimos necessidade de fazer coisas como:
find ! -path \*/.svn\* -print0 | xargs -0 grep fooou outras artimanhas do género? O ACK resolve este problema: é um grep que exclui à partida padrões com reconhecido desinteresse. Adicionalmente possui mais umas poucas opções extras de conveniência. É escrito em perl mas é rápido q.b. e bastante prático de se usar.
A NASA é a agência espacial dos EUA e é-lhe associada uma imagem de excelência, pelos feitos alcançados ao longo da sua história, apesar de terem tido a sua quota de reveses.
O contexto aeroespacial justifica condições ímpares que uma empresa comum não pode alcançar, no entanto algumas das práticas que a NASA tem empregue não exigem meios significativos e servem como exemplos a seguir.
Especialmente no início da exploração espacial aconteceram muitos erros, houve muitas falhas e alguns desastres com consequências trágicas. O que a NASA fez foi aprender com esses erros. Com isto melhorou os protótipos seguintes, melhorou os métodos de treino, as técnicas de construção, etc.
O conceito de aprender com os erros em si nem é uma grande lição – o termo “lessons learned” ouve-se nestes dias em qualquer empresa – a sua prática efectiva é que o é. É preciso analisar o que se passou com objectividade, fugindo à tentação humana de (des)culpabilização. E com base nessa análise é preciso tomar acções correctivas.
Ora isto é algo que, da minha experiência, nunca acontece. Isto é, ou nem sequer é feita a análise, ou é mal feita, ou não se tomam acções de correcção.
A NASA dá muito importância aos registos. Registos de construção, registos de procedimentos, registos fotográficos, recolha de amostras, levantamento de materiais e equipamentos, etc.
Os registos são importantes para analisar problemas ocorridos, para servir de base a novos desenvolvimentos, para reconstituir cenários, etc.
Ao longo do tempo popularizou-se o conceito que a NASA faz tudo a dobrar. Isto tem origem nos seus procedimentos reais desde longa data: eles mantinham duas equipas “iguais”, a principal e a de backup, sujeitas aos mesmos treinos durante todo o ciclo de preparação para uma missão, por exemplo. Também tinham réplicas de equipamentos e peças. Os próprios equipamentos construídos eram desenhados com vários sistemas de backup em casa de falha dos componentes principais.
Além de tudo o exposto, a NASA tem mostrado o conceito de estar preparado, fazendo uso, por exemplo, das mais mundanas técnicas e materiais.
Um exemplo é a fita adesiva, que desde longa data faz parte do material levado pelos astronautas para o espaço, tendo sido já útil por algumas vezes. Não sendo um material de alta tecnologia, foi importante em certos contextos devido à sua versatilidade.
No próximo artigo, usando o caso real da missão Apollo 13, mostrarei como muitos dos eventos reais ocorridos se encaixam nestas práticas.
Sejamos sinceros: escrevemos pouca e má documentação. Sobretudo técnica. Porquê?
Sim, o Word! O Word porque é universal e porque não é apropriado para escrever documentação técnica. Logo, se universalmente usamos algo inapropriado para uma finalidade, destruímo-la. QED :-)
Ainda estou para conhecer alguém, profissionalmente, que nunca me tenha enviado um .doc ou .docx. Toda a gente usa o Word! Seja para escrever 3 ou 4 linhas de anotações, seja para fazer o CV, seja para escrever regulamentação. Faz-se tudo com o Word. E nem se espera que os destinatários não abram os documentos ou não os consigam editar. É assumido que sim.
Isto “secou” as alternativas, que até existem, mas têm pouca comunidade. Não me serve de muito escrever em markdown (texto) se mais ninguém o fizer.
Primeiro algumas premissas que considero pertinentes na produção documental:
A documentação técnica deve residir ao lado do código, num sistema de controlo de versões (SCV). Para poder evoluir com o código. É a única forma prática de o conseguir.
Uma parte substancial da documentação deve ser gerada automaticamente, como APIs, tabelas, diagramas ou modelos de dados.
Ferramentas simples e universais devem poder ser usadas para manter documentação: controlar versões, obter diferenças, corrigir ortografia, substituições massivas, etc.
Devem poder ser gerados vários formatos finais para diferentes meios de publicação. Exemplos: PDF, html único, html paginado.
Os formato finais devem poder ser construídos automaticamente.
Ora nada disto se consegue em termos práticos com o Word! O Word implica ter um enorme programa lançado, que só corre em Windows, usando as suas idiossincrasias. Ficamos fechados nesse interface, com um sistema de automatização complexo e limitado em VBA. Torna-se difícil descobrir diferenças entre versões. Torna-se impossível haver edição partilhada.
A técnica mais frequente de edição em equipa é penosamente sequencial: consiste em passar um .doc de mail em mail pelos vários editores. Isto tem impactos em termos de tempo – tem que se esperar que o actual “proprietário” passe o documento – e impede a resolução imediata de conflitos. No cenário em que o editor N estraga parte das edições do editor N-1, como passa o documento em seguida ao editor N+1, o N-1 só lhe voltará a ter acesso na revisão final, o que irá dificultar bastante o seu trabalho de correcção. Na realidade já assisti a muitas edições perderem-se desta forma.
Aos pontos acima acrescento um novo:
Abrir o Word para documentar uma API, por exemplo, é pesado e “difícil”. Um fluxo típico de trabalho consiste em saltar de janela em janela, fazendo vários copy-pastes. Copy da janela de código, paste para a janela do Word. Todos os aspectos gráficos terão de ser feitos manualmente, um a um. Exemplo: colocar todas as variáveis em itálico, ou trechos de código numa fonte fixa.
Todos os pontos anteriores conseguem-se cumprir facilmente usando texto como representação fonte da nossa documentação.
Como tal a documentação deve ser escrita directamente em texto, ou pelo menos ter uma representação textual 1, para que possa ser processada por ferramentas simples e comuns. O texto é um formato verdadeiramente universal, logo qualquer ferramenta virtualmente o suporta.
Assim:
Edição leve e fácil: basta um editor de texto. Pode ser o nosso preferido. Pode ser o mesmo que usamos para editar código. Isto significa que podemos alterar a documentação sem sequer sair do contexto de desenvolvimento.
Colocando os ficheiros de documentação num SCV a gestão de versões é imediata. Quem editou o quê? O que mudou entre duas versões? Conseguimos reverter uma edição errada, ou até melhor, reverter as porções erradas de um dada edição. Tal como já o fazemos em relação ao código.
Edição simultânea em equipa: várias pessoas têm a hipótese de trabalhar na documentação em simultâneo, deixando o SCV tratar do merge das edições.
O sistema de construção do projecto pode ser adaptado para construir a documentação, incluindo também a geração das partes automáticas.
http://www.apeth.net/matt/iosbooktoolchain.html: descreve o processo usado para escrever o livro “Programming iOS 4”;
a documentação oficial do Python usa o sphinx (reStructuredText);
o projecto de documentação Read The Docs (RTD) também usa sphinx.
Morreu Dennis Ritchie, da fama do C e Unix, aos 70.
Um dos meus ídolos, fez parte da geração de ouro da computer science dos Bells Labs, a par de Ken Thompson, Brian Kernighan, Alfred Aho, Douglas McIlroy, Joe Ossanna (falecido em 1977), entre outros. Não sei porquê mas sempre foi o meu preferido.
A inspiração não dá para mais que isto:
main() { int a=58,b=a/2^3<<4,c=b^5;
printf("%c%c%c%c",a,b,c,a&017); }
A lógica da numeração romana é curiosa porque não se baseia em nenhuma base, embora o 5 e o 10 tenham um papel tipo base.
Outro aspecto interessante da sua lógica é que obedece a um padrão greedy. Como tal podem-se usar expressões regulares para converter de/para romano. Entra o sed.
conversão de romano para decimal
#! /bin/sed -f
# fr_roman.sed -- from roman (to decimal)
# Carlos Duarte, 980707
# usage: echo MCMXCVIII | sed -f fr_roman.sed
# output: 1998
#
# converts roman literals to decimal
# some input error checking is done, but not all cases are covered
# (bigger constructions following smallers, for instance)
#
# build table, all possible constructs that evaluate to [1-9]0* are here
1{
x
s/$/0/
s/$/I1/
s/$/II2/
s/$/III3/
s/$/IV4/
s/$/V5/
s/$/VI6/
s/$/VII7/
s/$/VIII8/
s/$/IX9/
s/$/X10/
s/$/XX20/
s/$/XXX30/
s/$/XL40/
s/$/L50/
s/$/LX60/
s/$/LXX70/
s/$/LXXX80/
s/$/XC90/
s/$/C100/
s/$/CC200/
s/$/CCC300/
s/$/CD400/
s/$/D500/
s/$/DC600/
s/$/DCC700/
s/$/DCCC800/
s/$/CM900/
s/$/M1000/
s/$/MM2000/
s/$/MMM3000/
x
}
# converts from MMXXII to 2000,20,2, from lookup table
s/^/\
/
G
ta
:a
s/\n\(..*\)\(.*\n.*[0-9]\)\1\([1-9]0*\)/\3,\
\2\1\3/
tb
s/\n/>>> error:/
s/\n.*//
#q
b
:b
/\n\n/!ba
s/\n\n.*$//
# convert from 2000,20,2, to 2022. this is not trivial!
#
# steps:
# 2000,20,2, -> 2000<20>2, <20> is the work digit
:d
s/,/</
te
:e
s/,/>/
tf
# exit point here: contains `2022,' so, remove the `,' and go
s/.$//
b
# 2000<20>2, -> 2000,:20<20>2, : is the cursor for changing the 20 in ,:20
# into 00 (for later, remove these from 2000)
:f
s/<\(.*\)>/,:\1&/
# 2000,:20<20>2, -> 2000,00:<20>2,
tc
:c
s/:[0-9]/0:/
tc
# 2000,00:<20>2, -> 2020,2, convert next digit
s/\(0*\),\1:<//
s/>/,/
bdconversão de decimal para romano
#! /bin/sed -f
# to_roman.sed -- converts decimal to roman
# Carlos Duarte, 980707
# do not perform error checking
# converts input treated as arabic decimals, to roman literal
#
# eg: echo 1998 | sed -f to_roman.sed
# outputs: MCMXCVIII
#
# build table, all possible constructs that evaluate to [1-9]0* are here
1{
x
s/$/1I/
s/$/2II/
s/$/3III/
s/$/4IV/
s/$/5V/
s/$/6VI/
s/$/7VII/
s/$/8VIII/
s/$/9IX/
s/$/10X/
s/$/20XX/
s/$/30XXX/
s/$/40XL/
s/$/50L/
s/$/60LX/
s/$/70LXX/
s/$/80LXXX/
s/$/90XC/
s/$/100C/
s/$/200CC/
s/$/300CCC/
s/$/400CD/
s/$/500D/
s/$/600DC/
s/$/700DCC/
s/$/800DCCC/
s/$/900CM/
s/$/1000M/
s/$/2000MM/
s/$/3000MMM/
x
}
s/.*/:&:/
ta
:a
s/:.\([^:]*\):$/&\1:/
ta
:c
s/:\(.\)/\1,/
tb
:b
s/,[0-9]/0,/
tb
/,::$/bd
s/,/;/
bc
:d
s/...$/;/
s/;00*//g
s/^/,/
G
te
:e
s/,\([^;]*\);\(.*\n.*\)\1\([IVXLCDM][IVXLCDM]*\)/\3,\2\1\3/
te
s/.\n.*//compilador de romano para expressão numérica do equivalente decimal
echo MCXLIII | sed -f from_roman.sed => 1000+100+40+1+1+1echo MCMLXXXI | sed -f from_roman.sed => 1000+900+50+10+10+10+1#! /bin/sed -f
# from_roman.sed -- output bc(1) code that convert roman to decimal
# Carlos Duarte, 980707
# usage: echo MCXLIII | sed -f from_roman.sed | bc
s/$/\
0M1000CM900D500CD400C100XC90L50XL40X10IX9V5IV4I1/
s/^/\
/
ta
:a
s/\n\(..\)\(.*\n.*[0-9]\)\1\([1-9]0*\)/\3+\
\2\1\3/
tb
s/\n\(.\)\(.*\n.*[0-9]\)\1\([1-9]0*\)/\3+\
\2\1\3/
tb
s/\n/0; ">>> error:/
s/\n.*/"/
#q
b
:b
/\n\n/!ba
s/.\n\n.*$//