30 de dez. de 2012

O que é que o Java não tem?

Recentemente apresentei uma palestra no Ti no Vale que aborda sobre alguns recursos que várias linguagens de programação, modernas e muito antigas, possui, mas que a linguagem Java não implementa.

 

22 de dez. de 2012

Javascript: as partes boas

A pouco tempo terminei de ler o livro Javascript: The Good Parts, escrito pelo Douglas Crockford e lançado em 2008. O livro é excelente e tenta colocar um ponto final na discussão sobre Javascript ser uma linguagem ruim, mas sem tentar esconder os pontos negativos da linguagem.

O autor passa por vários aspectos da linguagem como: sintaxe, objetos, arrays, regex e sobre organização de código. Mas os tópicos mais interessantes do livro, com certeza são os que abordam funções, herança e os apêndices que abordam as partes ruins do Javascript e como evitá-las.

No capítulo sobre funções ele aborda assuntos importantes no dia-a-dia de quem usa muito javascript como escopo de função, Closure, callbacks. Esses assuntos podem trazer alguma confusão quando o background do programador é de linguagens que não suportam tals features, especialmente Closure e callbacks. Funções como cidadões de primeira classe (ou de primeira ordem), apesar de ser algo comum em várias linguagens modernas, como Python e Ruby, não é suportado por linguagens como Pascal e Java que são largamente usadas em faculdades e escolas técnicas. O que pode causar estranheza em grande parte dos programadores ao ver um código Javascript que use isso.

Outra caracteristica pouco compreedida por alguns e muito bem explicada pelo Crockford no livro, é o conceito de herança em Javascript, que usa Herança por protótipo. Na Herança por protótipo, basicamente, objetos herdam diretamente de outros objetos. Não existe o conceito de classes.

A primeira linguagem que implementou herança por protótipo, foi a linguagem Self. Outras linguagens, como Lua e Io, também usam esse esquema de herança. O entendimento de herança as vezes é camuflado em uma sintaxe que lembra o conceito clássico de orientação a objetos. No livro são mostradas várias formas de se fazer herança em Javascript.

Já nos apêndices do livro, Crockford aborda como evitar as partes ruins do Javascript. Um dos exemplos que é mais simples é o uso do operador '==', que pode causar muita confusão e difucultar na hora de debugar algum código. Por exemplo:
'' == '0'          // false
0 == ''            // true
0 == '0'           // true
false == 'false'   // false
false == '0'       // true
false == undefined // false
false == null      // false
null == undefined  // true
' \t\r\n ' == 0    // true
Usando o operador '===' todos os resultados acima resultariam false.
'' === '0'          // false
0 === ''            // false
0 === '0'           // false
false === 'false'   // false
false === '0'       // false
false === undefined // false
false === null      // false
null === undefined  // false
' \t\r\n ' === 0    // false
O Livro, apesar de pequeno, 172 páginas, contém bastante conteúdo e é uma ótima referência para quem deseja se aprofundar em Javascript. Recomendo.

30 de jul. de 2012

Ferramentas Unix para desenvolvedores

As ferramentas que vem com os Sistemas operacionais Unix-like, já permitem que você execute várias ações que ajudam no dia-a-dia dos desenvolvedores. Em outros sistemas, algumas dessas ações só são possíveis de serem executadas de maneira satisfatória, com o uso de uma IDE.

Neste post pretendo falar de algumas dessas ferramentas.

Grep


O Grep é uma ferramenta que busca por padrões textuais (expressões regulares) em arquivos. O Grep, em seu comportamento mais comum, busca o padrão de texto informado em um arquivo ou da entrada padrão caso nenhum arquivo seja informado, e mostra as linhas onde o texto foi encontrado.

Neste exemplo, vou pesquisar pela palavra 'class' no início da linha:
$ grep '^class' ./django/db/models/query.py
class QuerySet(object):
class ValuesQuerySet(QuerySet):
class ValuesListQuerySet(ValuesQuerySet):
class DateQuerySet(QuerySet):
class EmptyQuerySet(QuerySet):
class RawQuerySet(object):
E este padrão foi encontrado em 6 linhas.

E neste outro exemplo, vou procurar por 'gvim', que retorna da saída do ps:
$ ps u | grep 'gvim'
myuser   15007 1.1 0.6 611320 26868 pts/1  Sl 11:54 0:00 /usr/bin/gvim -f
myuser   15049 0.0 0.0 13576  932   pts/2  S+ 11:54 0:00 grep --color=auto gvim
O Grep é uma ferramenta com muitas opções para customizar o seu comportamento, para ve-lás por completo, leia a man page do Grep.

Ack


O Ack, é, como os próprios criadores definem, uma ferramenta como o Grep, otimizada para programadores.
Em determinados momentos você precisa saber em qual arquivos está uma classe, um método, ou qualquer padrão textual, mas não em qual arquivo procurar. Nesses casos o uso do grep pode ficar complicado.

Para habilitar esse comportamento no Grep você deve usar a opção -r que faz uma busca recursiva nos diretórios, passando o diretório onde a busca deve ser iniciada(para iniciar sempre do diretório corrente, use o caractere '.'). E para ajudar a achar a linha correta usa a opção -n.

Exemplo:
$ grep -nr 'class QuerySet(object):' .
./django/db/models/query.py:30:class QuerySet(object):
Neste caso o arquivo que contém o padrão 'class QuerySet(object):' é ./django/db/models/query.py na linha 30.

Mas esse é o comportamento padrão do Ack, cuja saída já é pensado para facilitar a busca por padrões textuais em vários arquivos:
$ ack 'class QuerySet\(object\):'
django/db/models/query.py
30:class QuerySet(object):
No Debian e derivados já existe um pacote chamado Ack para tradução para para Kanji, e o pacote do Ack se chama Ack-Grep. Para facilitar o uso do Ack você pode criar uma alias:
$ alias ack='ack-grep'

Siege


Para desenvolvedores web o Siege pode ser uma ótima ferramenta de benchmarks de sites e aplicações web em geral.
$ siege www.google.com
...
HTTP/1.1 302   0.27 secs:     222 bytes ==> /
HTTP/1.1 302   0.22 secs:     222 bytes ==> /
HTTP/1.1 200   0.30 secs:   14780 bytes ==> /

Lifting the server siege...      done.
Transactions:                     42 hits
Availability:                 100.00 %
Elapsed time:                   4.32 secs
Data transferred:               0.30 MB
Response time:                  0.67 secs
Transaction rate:               9.72 trans/sec
Throughput:                     0.07 MB/sec
Concurrency:                    6.51
Successful transactions:          52
Failed transactions:               0
Longest transaction:            3.16
Shortest transaction:           0.21
Usando o Siege você pode confrontar resultados de performance entre dois servidores web, estudar resultados antes e depois de minificar seus arquivos HTML, por exemplo.
Estas são apenas algumas ferramentas que costumo usar, no cotidiano e que facilitam bastante minha vida.

4 de jul. de 2012

Dot Files

Usuários de Linux e Mac mais experientes sempre customizam bastante o seu ambiente. Criam alias para comandos, modificam arquivos de inicialização, configuram seus aplicativos, etc.. Tudo em prol da agilidade no dia-a-dia. Manter essas customizações atualizadas, é uma tarefa complicada. E essa complicação se multiplica quando você tem que trabalhar em várias máquinas.

A iniciativa do Github de divulgar alguns projetos dotfiles, pode ser uma inspiração para versionar todos os seus dotfiles. Aí vem a frase clichê típica desses momentos: "Como eu não pensei nisso antes?"

O nome dotfiles ("dot" significa ponto e "files" arquivos,  em inglês), vem dos arquivos ocultos dos sistemas Unix-like, que iniciam com um ponto (.bashrc, por exemplo). Pois os arquivos de configuração dos usuário, normalmente, são arquivos ocultos.

Eu, que sempre senti na pele as dificuldades de manter meus arquivos de customização, adotei a ideia rápidamente.

Neste momento o meu projeto está bem cru, mas dá pra ter uma ideia de como isso será útil no meu, e no cotidiano, de quem fizer o mesmo.

1 de jul. de 2012

Shell Script

Além de ser de extrema importância no ecossistemas Unix-like, Shell Script é muito prático para automatizar tarefas e uma excelente ferramenta para SysAdmins e programadores em geral.

Nessa apresentação mostro o básico sobre o Shell Script, mais específicamente falando de Bash.

12 de mar. de 2012

bpython, um belo console de Python

Um dos pontos fortes da linguagem Python, é a possibilidade de prototipar rapidamente atráves do seu poderoso console.

Para quem sentia necessidade de maior agilidade usando o console, o IPython, trouxe várias features, como o uso de instrospecção, que permite o auto-complete de métodos e atributos e acesso as Docstrings, dentre outras características que ajudam os desenvolvedores.

Eu descobri essa semana outro interpretador, o bpython. Com features parecidas as do IPython, mas um layout mais próximo do console original, ele já poderia substituir o IPython sem problemas. Mas algumas features são bem chamativas, dentre elas destacaria, o suporte ao compartilhamento de código via bpaste e a possibilidade de salvar o código em arquivo.

fonte: http://bpython-interpreter.org




Para mais informações, acesse a documentação do bpython.

11 de mar. de 2012

List Comprehensions em Python

List comprehensions, em ciência da computação, é um construtor de listas que se baseia em outras listas pré-existentes.

Esse conceito não é novo e data dos anos 60's. Mas o termo comprehension foi usado pela primeira vez na linguagem NPL.

E as List Comprehension tem como origem as representação do conjuntos da matemática:

{x ∈ N | 0 < x < 7}
Lê-se: x pertence ao conjunto dos Naturais, tal que 0 é menor que x e x menor que 7. O que restringiria esse conjunto a {1, 2, 3, 4, 5, 6}.

Em Python, uma List comprehension tem a seguinte sintaxe:

[x * 2 for x in lista]
Nesse exemplo abaixo, multiplicamos os itens da lista por 2, atribuimos à variável nova_lista:

>>> lista = [1, 2, 3, 4, 5]  
>>> nova_lista = [x * 2 for x in lista]  
>>> nova_lista  
[2, 4, 6, 8, 10]  
Sendo que o 'x * 2' é executado em cada item da lista. Um código mais limpo  e intuitivo do que uma versão usando a builtin function map.

>>> def vezes_2(n):
...     return n * 2
...     
... 
>>> map(vezes_2, range(6))
[0, 2, 4, 6, 8, 10]
Poderiamos fazer essa operação com uma lambda function.

>>> map(lambda x: x * 2, range(6))
[0, 2, 4, 6, 8, 10]

Um código análogo sem List comprehension:

>>> lista = [1, 2, 3, 4, 5]  
>>> nova_lista = []  
>>> for x in lista:  
...   nova_lista.append(x * 2)  
...    
...  
>>>  
>>> nova_lista  
[2, 4, 6, 8, 10]  
Dentro das  List comprehensions ainda se pode usar condicionais, para filtrar elementos na criação das novas listas.

>>> frase = ['o', 'rato', 'roeu', 'a', 'roupa', 'do', 'rei', 'de', 'roma']
>>> [p for p in frase if p[0] == 'r'] 
['rato', 'roeu', 'roupa', 'rei', 'roma']
Aqui eu verifico se a primeira letra da palavra é 'r'.

Criei um pequeno script para comparar a performance das List comprehensions. Escrevi duas funções, uma usando List comprehensions e outra não, que criam uma lista com os números pares entre 0 e 1000000. Para fazer a comparação usei a biblioteca cProfile.

def foo():  
    l = []  
    for i in range(1000000):  
        if i % 2 == 0:  
            l.append(i)

def foo_listcomp():  
    l = [i for i in range(1000000) if i % 2 == 0]  

cProfile.run('foo()')  
cProfile.run('foo_listcomp()')  
Executando o código.

$ python bench.py  
   500004 function calls in 1.739 seconds  


Ordered by: standard name  

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.009    0.009    1.739    1.739 <string>:1(<module>)
     1    0.936    0.936    1.730    1.730 bench.py:61(foo)
500000    0.769    0.000    0.769    0.000 {method 'append' of 'list'...
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof...
     1    0.025    0.025    0.025    0.025 {range}
  

   4 function calls in 0.165 seconds  

Ordered by: standard name  

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.015    0.015    0.165    0.165 :1()
     1    0.125    0.125    0.150    0.150 bench.py:67(foo_listcomp)
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof...
     1    0.024    0.024    0.024    0.024 {range}  
O código convencional chega a ser 10 vezes mais lento que o código que utiliza List comprehension. 1.739 segundos contra 0.165.

A performance e a simplicidade do código justifica o uso das List comprehensions quando se quer criar listas a partir de listas, sua finalidade semântica. Mas quando a lógica envolvida na geração da nova lista é muito complexa, o seu uso pode diminuir a legibilidade do código drasticamente.

Referências:
Google Style Guide
Documentação Python
The History of Python