Personal tools
You are here: Home



 
Showing blog entries tagged as: programação

Benedetti and Cranley's Head First jQuery

A very helpful book for those who want to get familiar with jQuery. The examples cover a great deal of common-use functionality and offer some guidance on better practices and style for those who are unfamiliar to modern JavaScript programming.

Read More…

More Emacs - a better eshell

Posted by Ricardo Bánffy at Sep 05, 2011 01:35 PM |

Some Emacs users live their entire lives without ever meeting eshell. Eshell is a command-line shell where you can run programs, list directories, copy files and do all kinds of things you would normally need a terminal window for. Eshell even made a cameo appearance in Tron Legacy, as the command-line interface to Encom's computers. Unfortunately, there is something it won't do: it's not easy to start more than one eshell session - when you invoke eshell for the second time, it goes back to the buffer the first eshell opened, normally called "*eshell*". I could invoke the "rename-uniquely" command manually after opening each eshell window (or before attempting to open a new one), but that's annoying (and I ofen forget it).

I keep eshell bound to C-$ (control-$ for the emacs-illiterate ou ^$ for really old-school folks - not that any keyboard I know of emits valid ASCII when someone presses Control-$). I often need more than one shell. "ansi-term" will do the buffer-renaming magic and I can start a number of those, but that's not really what I want - ansi-term runs the shell as a separate process. I use it when I need things I'd need another terminal for, like input and output redirection, but I'd prefer to use eshell when all I need is another shell.

Unfortunate people who don't use Emacs will think I would need to dig up the sources of eshell to add the rename-uniquely invocation and change them. I'll remind those poor folks than your init.el file is, in fact, executed when Emacs starts and, therefore, all I need to do is to change my binding from

(global-set-key (kbd "C-$") 'eshell)

to

(global-set-key (kbd "C-$") '(lambda () 
                               (interactive)
                               (eshell)
                               (rename-uniquely)
                               ))

In case you are wondering, the "interactive" function is invoked to mark this function as a command. If I didn't do it (as I foolishly did earlier this morning, before reading this) I'd be greeted with

Wrong type argument: commandp, (lambda nil (eshell) (rename-uniquely))

That's nice, but not perfect. If you invoke it, you will notice your first eshell buffer is named "*eshell*<2>". That's bad.

It happens because the buffer is uniquely renamed every time it's created, not only when there already is a buffer named "*eshell*". For that, we need a little more code:

(global-set-key (kbd "C-$") '(lambda () 
                               (interactive)
                               (if (member "*eshell*" (mapcar* 'buffer-name 
                                                      (buffer-list)))
                                   (progn (eshell)
                                          (rename-uniquely))
                                   (eshell))))

OK. That was stupid. We test whether there is already a buffer named "*eshell*" and then we try to invoke eshell to create a new one, forgetting that eshell will also find the buffer called "*eshell*" and switch to it rather than creating a new one. We then, dumbly, rename our only eshell buffer.

This thing needs more brains.

Since I don't expect to have more than a handful eshells running (you really shouldn't try to), progressing through numbered buffer names until we find a free one should be a fine approach:

(global-set-key (kbd "C-$") '(lambda () 
                               (interactive)
                               (let ((i 1)
                                     (found-a-name nil))
                                 (while (not found-a-name)
                                   (setq buffname 
                                         (concat "*eshell*<" (int-to-string i) ">"))
                                   (setq found-a-name (not (member buffname 
                                                                   (mapcar* 'buffer-name 
                                                                            (buffer-list)))))
                                   (if found-a-name 
                                       (eshell i)
                                     (setq i (+ i 1))
                                     )))))

Now we start from i = 1 and check if there is already a buffer named "*eshell*<i>". If there is not, create one with that name (passing the number to eshell) and end our search. This is doubly pretty, because the first solution would rename our first eshell as "*eshell*<2>". With this one, the first eshell is "*eshell*<1>", which is elegant. If you manually open an eshell, it'll be called "*eshell*", distinguishing it from our automatically named shells.

Still, as a friend of mine well pointed out, the code is really ugly. It'll also fail if the default name for eshell buffers is changed. In short, it's a mess. I felt compelled to do better:

(global-set-key (kbd "C-$") 
                '(lambda () 
                   (interactive)
                   (let ((i 1))
                   (while (member (concat eshell-buffer-name "<" (int-to-string i) ">")
                                  (mapcar* 'buffer-name (buffer-list)))
                     (setq i (+ 1 i)))
                   (eshell i))))

Much more concise and to-the-point. I like it.

Can you customize Eclipse like that?

A bug. Yes, they hit me too

After publishing this, I noticed the variable eshell-buffer-name is not defined until after you invoke eshell for the first time. If you try to C-$ on a freshly started Emacs session, you'll get a

Symbol's value as variable is void: eshell-buffer-name

message. In order to fix this, the code must check whether eshell-buffer-name is bound and, if it's not, we start the buffer giving it a 1.

(global-set-key (kbd "C-$")
                '(lambda ()
                   (interactive)
                   (let ((i 1))
                     (if (boundp 'eshell-buffer-name)
                         (progn
                           (while (member (concat eshell-buffer-name "<" (int-to-string i) ">")
                                          (mapcar* 'buffer-name (buffer-list)))
                             (setq i (+ 1 i)))
                           (eshell i))
                       (eshell 1))))
)

OK. Now I am satisfied.

Edit: And now, I feel stupid

A friend of mine, very politely, possibly to avoid embarrassing me in public, sent me an e-mail pointing out he didn't quite understood what I was trying to accomplish here. In his message, he pointed out I could just invoke (eshell t). When passed "t" (boolean true in Lisp) as a parameter, eshell does precisely what I wanted it to do. So, the new version in my init.el is even shorter:

(global-set-key (kbd "C-$") '(lambda () (interactive) (eshell t)))

Well... At least I learned something.

Read More…

Fazendo um pouco melhor (ainda a provinha do GDD)

Ainda não desisti de resolver a prova do GDD em Erlang. Não. Não preciso resolver em Erlang, mas, com tanta gente usando Java, PHP e até PL/SQL pra resolvê-la (e com um amigo que usou Haskell), eu fiquei com vontade.

Também posso repetir uma do ano passado e fazê-la em Lisp.

Então. Erlang é avessa a loops. Loops fazem coisas mudarem de estado e linguagens funcionais não gostam que coisas mudem de estado. O dialeto de Lisp que eu usei ano passado também, mas faz uma concessão e me deixa fazê-los.

O comparador

Assim, a nossa primeira função com loop da prova, cmp_goog, que é assim:

def cmp_googlon(p1, p2):
    v = 'jgptzmqskbclrhdfnvwx'
    for l1, l2 in zip(p1, p2):
        if v.index(l1) != v.index(l2):
            return v.index(l1) - v.index(l2)
    return len(p1) - len(p2)

Ficaria assim:

def cmp_googlon(p1, p2):
    v = 'jgptzmqskbclrhdfnvwx'
    if p1 == p2: return 0
    elif len(p1) == 0 or len(p2) == 0: return len(p1) - len(p2)
    elif p1[0] == p2[0]: return cmp_googlon(p1[1:], p2[1:])
    else: return  v.index(p1[0]) - v.index(p2[0])

Note que, em vez de comparar as strings em um loop, eu comparo só seus primeiros elementos e, se os dois forem iguais, eu chamo o comparador de novo, agora com as strings sem a primeira posição.

Bases numéricas

A outra função serve para nos dar o valor em base 10 de um número em googlon. O original é um clássico, em que você percorre os dígitos do número e vai totalizando os valores de cada casa:

def valor_numerico(p):
    v = 'jgptzmqskbclrhdfnvwx'
    vn = 0
    i = 0
    for c in p:
        vn += v.index(c) * (20 ** i)
        i += 1
    return vn

A idéia é que o valor de um número é o valor do seu dígito menos significativo somado ao produto da base multiplicada pelo valor do resto. No caso de números em base 10, 123 é dado pela soma de 3 com o produto de 10 e 12, sendo que 12 é dado por 2 somado a 10 vezes 1. Transcrito em Python, a nova versão é bem mais concisa:

def valor_numerico_f(p):
    v = 'jgptzmqskbclrhdfnvwx'
    if len(p) == 1:
        return v.index(p)
    else:
        return v.index(p[0]) + 20 * valor_numerico_f(p[1:])

Agora eu preciso de algumas horas para escrever a versão em Erlang. Desejem-me sorte.

Read More…

Solução para a provinha do Google Developer Day 2011 em Python

Esse ano, resolvi tratar com um pouco mais de respeito a prova do GDD. Ano passado, eu resolvi com o prompt do Python aberto, olhando o browser em uma janela e o prompt na outra. Esse ano, as perguntas eram um pouco mais trabalhosas e as strings maiores e isso me fez, em vez disso, escrever um programa. Mais tarde eu coloco o programa no github, mas, por hora, eu transcrevo e comento aqui a minha solução.

A prova apresenta dois textos escritos no idioma Googlon, com os quais vamos trabalhar, e quatro perguntas. Para ajudar (eles são realmente bonzinhos), eles dão as soluções para o primeiro texto, o que ajuda a testar se o código está correto.

Letras foo, bar e preposições

A primeira pergunta explica que algumas letras são chamadas de "letras foo" e todas as demais, "letras bar" e que preposições são palavras de cinco letras que começam com uma letra bar e não contém a letra "q" (é muito provável que a sua prova seja diferente da minha e que elas sejam geradas aleatoriamente - é como eu faria). Para ajudar a resolver, eu escrevi algumas funções

def foo(l):
    return l in "snrbg"

def bar(l):
    return not foo(l)

def preposicao(p):
    return len(p) == 5 and bar(p[-1]) and 'q' not in p

Aí, testar a resposta dada no enunciado é fácil:

>>> palavras_a = texto_a.split(' ')
>>> len(filter(preposicao, palavras_a))
63

Assim como responder a pergunta:

>>> palavras_b= texto_b.split(' ')
>>> len(filter(preposicao, palavras_b))
57

Verbos e verbos em primeira pessoa

A segunda pergunta ensina que verbos são palavras de 6 letras que terminam em uma letra bar. Se ele também começar com uma letra bar, estará em primeira pessoa.

def verbo(p):
    return len(p) >= 6 and bar(p[-1])

def verbo_primeira_pessoa(p):
    return verbo(p) and bar(p[0])

Agora podemos testar nosso código com o dado do enunciado:

>>> len(filter(verbo, palavras_a)) == 216
True
>>> len(filter(verbo_primeira_pessoa, palavras_a)) == 160
True

E descobrir a nossa própria resposta:

>>> len(filter(verbo, palavras_b))
224
>>> len(filter(verbo_primeira_pessoa, palavras_b))
154

No meu caso, eu tinha 224 verbos, dos quais 154 em primeira pessoa.

Vocabulário

Agora o problema pede para criar uma lista com o vocabulário, ordenado segundo o alfabeto googlon. Para isso, eu vou usar o ordenador que já vem embutido nas listas do Python e vou produzir uma função de comparação. Essa função pode ser facilmente plugada em seu próprio sort, se você quiser muito:

def cmp_googlon(p1, p2):
    v = 'jgptzmqskbclrhdfnvwx'
    for l1, l2 in zip(p1, p2):
        if v.index(l1) != v.index(l2):
            return v.index(l1) - v.index(l2)
    return len(p1) - len(p2)

E podemos testar com os dados do enunciado:

>>> vocabulario_a_unsorted = list(set(palavras_a))
>>> ' '.join(sorted(vocabulario_a_unsorted, 
...                        cmp = cmp_googlon)) == vocabulario_a
True

Nota: ao construir um set com as palavras do texto A, eu eliminei as repetições. Como objetos do tipo set não são ordenáveis, eu transformei o conjunto em uma lista. 

Agora podemos encontrar nossa resposta:

>>> vocabulario_b_unsorted = list(set(palavras_b))
>>> vocabulario_b = ' '.join(sorted(vocabulario_b_unsorted, cmp = cmp_googlon))
>>> vocabulario_b
'jgspd jgv jpgzkx jzvjw jmrmlq jmdxx jmntpzq jqw jspk jkc jbb jcphbk jch jcv jlkm...

Números

Agora a prova nos explica que todas as palavras em googlon tem um valor numérico. Em googlon, os números são escritos do dígito menos significativo para o mais significativo e em base 20. Pra isso, precisamos de mais uma função:

def valor_numerico(p):
    v = 'jgptzmqskbclrhdfnvwx'
    vn = 0
    i = 0
    for c in p:
        vn += v.index(c) * (20 ** i)
        i += 1
    return vn

que, podemos testar contra o enunciado:

>>> valor_numerico('blsmgpz') == 262603029
True

Essa parte está certa. Mas a pergunta pede para contarmos os números bonitos. Para eles, números bonitos são divisíveis por 5 e maiores que 492528.

def numero_bonito(p):
    return valor_numerico(p) > 492528 and (valor_numerico(p) % 5 == 0)  

Agora podemos testar:

>>> len(filter(numero_bonito, vocabulario_a.split(' '))) == 75
True

E chegar na nossa resposta:

>>> len(filter(numero_bonito, vocabulario_b.split(' '))) 
71

Conclusão

Foi difícil? Nem um pouco. Se você não conseguiu responder por conta própria, precisa estudar mais. Foi mais trabalhosa do que a do ano passado? Um pouco. Por outro lado, ela tinha informações suficientes para você poder testar suas próprias soluções - e isso ajudou quem teve mais problemas para resolver a prova a aprender alguma coisa. Prova boa é assim - você aprende enquanto faz.

Uma última observação: eu gosto de list comprehensions, mas também gosto de map, filter e reduce. O código fica mais limpo quando eles são bem empregados.

Read More…