Personal tools
You are here: Home



 
Showing blog entries tagged as: java

Sobre a provinha do Google Developer Day 2011

Ano passado, quando a Google abriu as inscrições para o GDD, eu escrevi o primeiro de uma série de três artigos sobre a tal provinha. Esperei até o final das inscrições (e o sinal verde de um amigo que trabalha lá) para publicá-lo.

Não vou fazer diferente esse ano. Se você veio aqui querendo ver a solução dos problemas do ano passado, eles estão relacionados abaixo. Se, por outro lado, chegou aqui procurando um jeito fácil de entrar no evento, devia ter vergonha. Eu respeito a decisão da Google de usar a prova como uma forma de garantir que a platéia tenha melhores chances de entender o que o palestrante vai falar e não vou ajudar a estragar o processo de seleção de convidados deles. Quando as inscrições estiverem encerradas, eu volto aqui e publico a minha solução e aí você pode comparar com a sua.

De resto, boa sorte. A prova não é difícil e eu tenho certeza que você consegue. Você já chegou até aqui, afinal.

Um abraço e até lá.

Nota: Se você colocar aqui um comentário mostrando uma solução antes que sejam fechadas as inscrições, eu vou apagar pela mesma razão que me fez não publicar a minha ainda. Se você tem uma solução, volte depois.

Outra nota: Agora, que as inscrições estão encerradas, você pode achar soluções procurando no Google. Uma delas (em Java) foi postada em um comentário aqui. A mihha em Python está aqui. Há outras, algumas com escolhas de linguagem ainda mais estranhas do que a minha versão para computadores de 8-bits do ano passado. Acho uma pena que tantos tenham praticado GDD (Google-Driven Development, pun intended) e tenham googlado por respostas em vez de aprender a programar.

Read More…

Learning Android, by Marko Gargenta

Learning Android, by Marko Gargenta

This is a fairly good book that will take you through your first steps on Android application development. It goes over key concepts like activities, services, intents and asynchronous tasks, explores the helpers and builders in Eclipse (but does not refrain itself from going into the XML when needed) and does so by going through the development of a simple application. I think the best way to go through this book is to follow along and build your own. If the book has any weak spot, it's the sample application. For a book like this, I would not use a Twitter client - the Twitter API introduces some needless complexities into what should be a trivial example (and I, most certainly, don't want to develop yet another Twitter client). By going with a Twitter client, the book also somewhat limits itself to a certain narrow usage example and that affects what the book covers in terms of UI and tooling.

It took me a while to get through it because I opted to write an application different from the Yamba example (for the reasons I stated above). If you decide to go closely with the book example, you should do it in about a week. It also didn't help me the fact I could not dedicate more than a couple hours per week to the project.

A final piece of advice: I reviewed the ePub version on a Nook. The screen images were somewhat hard to read and I had to use the PDF version to see some of the finer detail. I am not sure whether this is a problem with the ePub version or with my specific e-reader (it has some issues). Your mileage may vary.

Pros: Short, easy to understand, well written

Cons: The example app is not perfect for the task

Best uses: Those new to Android

Read More…

A prova do GDD, em Lisp

Outro dia assisti a uma palestra sobre Hadoop e MapReduce. Todos os exemplos foram dados em um pseudocódigo que misturava Ruby com JavaScript enquanto, na minha cabeça, eu os re-escrevia de um jeito que funcionassem de verdade em Python (eu trabalho com isso). Como no meu último post eu já resolvi a provinha do Google Developer Day usando Python (e usei uma função anônima e um filter, vejam só), eu fiquei com a coceira enorme de refazer essas perguntas com Lisp (já que ela expressa maps e reduces como ninguém mais).

No meu laptop eu tenho uns 4 dialetos de Lisp diferentes (não existe uma "implementação canônica" como no caso de Python ou Ruby). Para brincar aqui, eu escolhi Clojure. Clojure é um dialeto de Lisp que roda sobre a máquina virtual do Java (Java é o COBOL do século XXI e provavelmente do XXII, mas a VM e toda a infra-estrutura dela são muito legais). Outro motivo é que o meu Emacs (o IDE to rule them all) está ligado ao Clojure pelo Slime. Eu instalei ambos (Clojure e Slime) com o ELPA, o gerenciador de add-ons do Emacs e, com uma gambiarra e dois symlinks, estou usando o Clojure 1.2 em vez do 1.1 (que é a versão que o ELPA oferece por hora). Eu podia ter tentado usar o Emacs Lisp (o Emacs é quase todo escrito em Emacs Lisp e um REPL dele está sempre disponível), mas ele não é nem um Lisp moderno, nem particularmente gostoso de usar. Podia ter usado Scheme (e provavelmente o resultado seria mais elegante), mas achei que, por rodar na JVM, Clojure é mais "sexy". Qualquer coisa que evite que alguém tenha que escrever código em Java é uma Coisa Boa que merece ser incentivada.

Aqui cabe uma observação: várias linguagens - Scala, Jython, JRuby - permitem que seus programas rodem dentro de máquinas virtuais Java. Todas elas permitem, também, que você use classes que fazem parte tanto do Java propriamente dito como de outros programas nos quais você talvez precise adicionar alguma funcionalidade. A produtividade individual de um desenvolvedor experiente usando uma linguagem como Python e Ruby (dos quais Jython e JRuby são dialetos) já foi observada como sendo superior à de um desenvolvedor igualmente experiente usando Java. Em outras palavras: se você está usando Java você devia dar uma olhada em linguagens mais produtivas.

E isso vale para C#/.NET. Ninguém deveria usar essas tranqueiras.

A propósito, se você quiser ver a resolução da provinha em Jython, vá à outra versão do artigo. É absolutamente a mesma coisa.

Voltando à prova, resolvê-la deu um pouco de trabalho, provavelmente porque eu ainda estou começando a aprender Clojure (o Clojure Programming/Examples/Cookbook e o livro Programming Clojure, de Stuart Halloway tem ambos sido companhia constante aqui - e são bons mesmo como uma introdução ao Lisp - e o The Joy of Clojure é o próximo no wish-list, para assim que estiver pronto) e porque lembro muito pouco de outros dialetos de Lisp (e o pouco que eu lembro, parece, mais atrapalha do que ajuda).

Aviso muito importante: Eu disse isso acima, mas preciso repetir: estou aprendendo Clojure. Posso ter feito alguma barbaridade sem saber. Mais: não fiz qualquer tentativa de otimizar o código exceto para concisão e legibilidade. Também não fiz TDD. E, por favor, não considere meu estilo pessoal como algo que seja bem-vindo (ou mesmo socialmente aceito) entre programadores Lisp ou Clojure. Aceite meus conselhos por sua conta e risco e não me culpe de nada.

Agora que você foi adequadamente avisado, vamos à provinha:

1) O que esse programa faz?

x = 7
y = 4
if y > 2, then
   y = y * 2
else
   x = x * 2
print (x + y)

No REPL do Clojure, eu fiz assim:

user> (def x 7)
#'user/x
user> (def y 4) 
#'user/y
user> (if (> y 2) (def y (* 2 y)) (def x (* 2 x)))
#'user/y
user> (+ x y)
15

O programa do exercício imprime "15".

2) Quantas vezes esse programa imprime "hello"?

Na linguagem do exercício os loops incluem as extremidades.

for i = 1 to 5
   if i != 2, then
      for j = 1 to 9
         print 'hello'

Vou fingir que, de olhar o pseudocódigo, não dá pra sacar que o programa roda 4 vezes o loop interno e imprime "hello" 36 vezes.

Em Clojure, não temos loops como em Python (ou C, ou quase qualquer outra linguagem desse time). Vamos usar uma list comprehension com o efeito colateral de imprimir coisas na tela.

(for [
        i (range 1 6) 
        j (range 1 10)
     ] 
     (if (not= i 2) 
         (println "hello")
     )
)

Ou, de um jeito um pouco mais Clojure, usando a cláusula ":when" no binding dos ranges no lugar do "jeito C de fazer" com o if dentro do bloco:

(for [
      i (range 1 6) 
      j (range 1 10)
      :when (not= i 2) 
      ] 
  (println "hello")
  )

Executando isso no REPL, ele vai imprimir os "hello"s (como o exemplo em Python fez) entrecortados pelo retorno da list comprehension (que o Python não fez) e isso é chato de contar (ainda mais chato do que o exemplo do Python). Do mesmo jeito que eu fiz com Python, eu vou usar um contador. Como o Clojure abraça a idéia de programação sem efeitos-colaterais e memória transacional, você precisa envolver a coisa cujo estado você pretende alterar em um pouco de burocracia. Famos criar um contador mutável que pode ter seu valor alterado durante a execução do programa.

(def contador (ref 0))

E, com isso, nosso código fica assim:

user> (def contador (ref 0))
(for [
      i (range 1 6)
      j (range 1 10)
      :when (not= i 2)
      ]
  (println (dosync (alter contador inc)) "hello")
  )

A função "dosync" cuida da transação (eu poderia alterar várias variáveis de uma vez) e "alter" faz a alteração propriamente dita do dado que está na referência "contador". "dosync" nos devolve o resultado da última coisa que ele fez, que foi incrementar o contador. Agora, na janela do REPL, vemos algo como:

32 hello
33 hello
34 hello
35 hello
36 hello
nil nil nil nil nil nil nil nil nil nil)
user>

Que é exatamente o que nós queríamos. Agora sabemos que o programa imprime "hello" 36 vezes. Se tivéssemos compilado o programa e o executado a partir de um shell, não veríamos os "nil"s (que só são mostrados pelo REPL porque são o retorno de da função println agrupados pelo for - que retorna uma lista com os resultados).

Poderíamos ter sido mais espertos e simplesmente contado o comprimento da lista que o for nos deu (o monte de "nil"s que atrapalha a leitura da nossa saída), mas isso é para o próximo exercício.

3) Quais números, entre 5 e 2675 são pares e divisíveis por 3?

O exercício diz que eu posso escrever um programa para isso. Eu não fiz isso com Python e ainda não preciso fazer isso com Lisp. Tenho uma pontinha de dó de quem precisa.

user> (count (for [n (range 5 2676) :when (and (= (mod n 3) 0) (even? n))] n))
445

Que foi porco da primeira vez e continua porco agora. Quem leu o primeiro post (ou estava acordado no ginásio) sabe que se um número é par e divisível por 3, ele é divisível por 6. Assim, podemos usar o mesmo truque que usamos com Python na nossa solução:

user> (count (for [n (range 5 2676) :when (= (mod n 6) 0)] n))
445

Eu só sinto saudade da função "even?" da primeira implementação. É legal.

4) Números bonitos

A pergunta nos apresenta Barbara e seus critérios para que números sejam ou não bonitos. Para ela, números são bonitos se contiverem um dígito 4 e não contiverem um dígito 9. Ao final, nos pergunta quais números entre 14063 and 24779, inclusive, são bonitos.

Agora eu vou usar um pedaço do Clojure que não vem carregado, mas que é muito útil: uma biblioteca de manipulação de strings. Para usá-la, vamos fazer:

user> (use 'clojure.contrib.string)
WARNING: repeat already refers to: #'clojure.core/repeat in namespace: user,
 being replaced by: #'clojure.contrib.string/repeat
WARNING: butlast already refers to: #'clojure.core/butlast in namespace: user, 
being replaced by: #'clojure.contrib.string/butlast
WARNING: reverse already refers to: #'clojure.core/reverse in namespace: user, 
being replaced by: #'clojure.contrib.string/reverse
WARNING: get already refers to: #'clojure.core/get in namespace: user, 
being replaced by: #'clojure.contrib.string/get
WARNING: partition already refers to: #'clojure.core/partition in namespace: user, 
being replaced by: #'clojure.contrib.string/partition
WARNING: drop already refers to: #'clojure.core/drop in namespace: user, 
being replaced by: #'clojure.contrib.string/drop
WARNING: take already refers to: #'clojure.core/take in namespace: user, 
being replaced by: #'clojure.contrib.string/take
nil

Os warnings dizem que ela mudou o comportamento de algumas coisas que já estavam disponíveis. Não se preocupe com isso: só queremos a função "substring?". Agora, para saber quantos são bonitos, vamos começar com uma função que decide se um número é bonito ou não:

user> user> (defn bonito? [x]
        (and
         (substring? "4" (str x))
         (not(substring? "9" (str x)))
         ))
#'user/bonito?

Por favor, ignore que eu converti x duas vezes para uma string. Eu otimizo para legibilidade. Outra coisa: eu poderia ter usado .indexOf diretamente ou usá-lo para fazer meu próprio "substring?", mas como clojure.contrib é parte do Clojure, eu acho melhor não duplicar o que é parte do pacote.

Agora temos um "bonito" no nosso namespace. Vamos testá-la, para ver se  fizemos tudo direito:

user> (bonito? 4)
true
user> (bonito? 9)
false
user> (bonito? 49)
false
user> (bonito? 1941)
false

Daí, basta aplicá-la usando a mesma técnica que usamos no problema anterior:

user> (count (for [
           n (range 14063 24780) 
           :when (bonito? n)]
           n))
3047

Se você preferir, pode dispensar a função:

user> (count (for [
           n (range 14063 24780) 
           :when (and (substring? "4" (str n)) 
                      (not(substring? "9" (str n)))) ]
           n))

E tem um jeito ainda mais curto, usando a função "filter".

user> (count (filter bonito? (range 14063 24780)))
3047

E, se eu quiser tornar a coisa um pouco mais obscura usando uma função anônima:

user> (count (filter (fn [n] (and (substring? "4" (str n))
                             (not(substring? "9" (str n))))) 
                     (range 14063 24780)))
3047

Apesar das aparências, funções anônimas não existem para atrapalhar a legibilidade do seu código mas para que você possa construí-las somente quando necessário e passá-las como parâmetro para outras funções que vão fazer coisas com elas.

5) Os telefones

A última pergunta nos apresenta um país em que os números de telefone têm 6 dígitos. Números não podem ter dois dígitos consecutivos idênticos, porque isso é caído. A soma dos dígitos tem de ser par, porque isso é legal e o último dígito não pode ser igual ao primeiro, porque isso dá azar.

Vamos começar com os caídos. Ao contrário do nosso exemplo em Python, eu preciso me preocupar com os limites dos índices. (nth "abcdef" -1) não é aceitável em Clojure.

user> (defn caido? [n] 
        (some true? (for 
                     [i (range (- (count (str n)) 1))]
                     (= (nth (str n) i) (nth (str n) (+ 1 i)))
                     )))
#'user/caido?

Camos fazer alguns testes para ver se estamos encontrando números caídos:

user> (caido? 123456)
nil
user> (caido? 122345)
true
user> (caido? 123451)
nil

Observe que, ao contrário da solução em Python (que tem um bug, aliás) a função que identifica números caídos não identifica números que não são caídos mas que dão azar como se fossem caídos.

Vamos precisar, então, escrever uma função para os que dão azar. Essa é fácil:

user> (defn da-azar? [n]
        (= (first (str n)) (last (str n))))
#'user/da-azar?

Só para ter certeza do que fizemos:

user> (da-azar? 123456)
false
user> (da-azar? 623456)
true
E, finalmente, uma para os números legais:
user> (defn legal? [n] 
        (even? 
         (reduce + 
                  (map (fn [s] (Integer/parseInt s)) 
                       (for [c (str n)] (str c))))))
#'user/legal?

Aqui eu preciso dizer uma coisa: tem que existir um jeito melhor de invocar o método estático parseInt de Integer. Essa cicatriz entre o lado Lisp e o lado Java está muito feia.

Voltando ao exercício, vamos testar nossa função para termos certeza de que não fizemos nada errado:

user> (legal? 12)
false
user> (legal? 55)
true
Com as três na mão, basta usarmos duas no nosso critério:
user> (count (for [n (range 100000 1000000)
           :when (and (legal? n)
                      (not (da-azar? n))
                      (not (caido? n)))]
           n))
238500

Mas o exercício não perguntou quantos caídos entre 100000 e 999999. Eles nos deu uma lista. Com um pouco de mágica de clipboard, colocamos os números em uma string, que quebramos e fazemos uma lista:

user> (def tudo "214966
215739
220686
225051
225123
(...)
720202
720568
720576
")
#'user/tudo
user> tudo
"214966\n215739\n220686\n225051\n225123\n...720202\n720568\n720576\n"
user> (def numeros (split-lines tudo))
#'user/numeros
user> numeros
("214966" "220686" "225051" "225123"..."720202" "720568" "720576")
user> (count numeros)
200

Então, usamos a lista e chegamos no resultado:

user> (count (for [n numeros
           :when (and (legal? n)
                      (not (da-azar? n))
                      (not (caido? n)))]
           n))
61

61 de 200 é o resultado que tivemos antes. Se erramos, erramos duas vezes do mesmo jeito.

Não doeu tanto assim, doeu?

Muitos programadores morrem de medo de Lisp. Morrem de medo, vêm um monte de parênteses, acham confuso e desistem. Lisp é uma linguagem muito interessante - a mais antiga ainda em uso - e impõe uma certa disciplina que é útil para qualquer programador. Robert "Uncle Bob" Martin recomenda que todo programador profissional aprenda uma linguagem "diferente" das que conhece por ano. A analogia que ele faz é interessante: um carpinteiro que só sabe fazer banquinhos de pinho não é um bom carpinteiro. Ele precisa aprender a trabalhar com outros tipos de madeira e a fazer outros tipos de móvel.

Se tudo o que você sabe fazer são sites em PHP ou módulos de ERP em C#, talvez seja hora de pensar em aprender algo novo.

Read More…

Resultados da Pequena Pesquisa

Alguns dias atrás, depois de um post relativamente grosseiro que eu fiz, eu me propus a conduzir uma pequena pesquisa para responder algumas perguntas:

  1. Programadores inexperientes realmente tem uma tendência a escolher Java como linguagem de implementação?
  2. Programadores tendem mesmo a rejeitar o BASIC (Visual Basic, VBScript, VB.Net etc)?
  3. Quais programadores rejeitam o que? Há algum padrão reconhecível por experiência ou escolha de linguagem?

Hoje, depois de minhas merecidas férias e de mais ou menos 300 respostas, eu acho que posso começar a comentar sobre alguns resultados interessantes.

Sobre a pesquisa

Como o formulário diz, a pesquisa é metodologicamente falha - de um questionário incompleto, potencialmente inconsistente a uma amostra viciada - e uma causa perdida do ponto de vista científico. Não tenho intenção alguma de traçar o perfil definitivo dos programadores lusófonos. Minha intenção era apenas lançar alguma luz sobre as questões que eu enumerei há pouco e confirmar ou refutar meu mal-criado post.

Um dia, talvez, eu faça algo sério. Hoje não.

De longe, o problema mais chato de ajuste dos dados foi a amostra viciada: minhas preferências de listas de discussão impactaram significativamente as respostas. Previsivelmente, muita gente da comunidade de usuários de Python respondeu, desviando todas as leituras. Para chegar a dados mais significativos, em alguns casos eu precisei reduzir o peso das respostas em que Python é a primeira escolha para refletir melhor a realidade. Vários dos números que eu vou apresentar a seguir refletem esses ajustes.

Java e os inexperientes

Java foi a linguagem de escolha de 19% dos novatos (com experiência de até 3 anos), praticamente empatada com C# (17%). Ambas ficaram atrás de PHP que é a escolha de 33% dos inexperientes.

O fato de Java e C# serem relativamente populares entre os novatos pode ser atribuído a vários fatores: das oportunidades de emprego aos excelentes IDEs disponíveis (sim, Visual Studio é um ótimo IDE, mas apenas se você nunca for desenvolver nada para qualquer outra plataforma que não Windows).

A parte importante é que meu mal-criado post está errado: os inexperientes preferem PHP.

Quanta gente odeia o BASIC?

Essa não foi surpresa. Falando friamente, VB (que é o único dialeto sobrevivente de BASIC) é uma porcaria. Foi legal nos baixos anos 90, principalmente porque permitia escrever programas para Windows quando a única alternativa seria o Microsoft C ou a família Turbo Pascal e seus descendentes. Linguagens de terceiros para Windows sempre deixavam "cicatrizes" de interface e isso sempre me incomodou - é parecido com o efeito de se usar AWT em programas Java: eles simplesmente não parecem "certos" em nenhuma plataforma.

De qualquer modo, metade dos respondentes declararam que não usariam BASIC (o que inclui VB.net e VBScript) por nada nesse mundo. Eu acho isso um progresso significativo que enche meu coração de esperança.

O que os programadores mais detestam?

Essa é, no fundo, a pergunta mais interessante. Para respondê-la, precisamos separar nossos programadores em categorias. Para isso vou usar dois critérios: linguagem de escolha e tempo de janela.

Entre os novatos, a linguagem mais detestada continua sendo o BASIC (e seus descendentes). Surpreendentemente, o segundo lugar nessa categoria é o Java. Aparentemente os novatos que não gostam de Java têm opiniões fortes a respeito da linguagem.

Entre os veteranos, com 10 ou mais anos de experiência, o BASIC é o mais detestado. O segundo lugar é do Perl (o que me surpreende um pouco), seguido de Java e C# bem de perto.

Entre os 3 que preferiram BASIC, a linguagem mais detestada por dois deles é o próprio BASIC. Eu imagino que esses dois ou não entenderam o formulário ou não quiseram colaborar. O outro dos três respondentes que preferem BASIC, escolheu quase todas as outras linguagens como as que ele nunca jamais usaria. Como eu o conheço de muito longa data (ele preencheu o nome), eu acredito que ele conheça a lista toda. Ou que tenha me pregado uma peça.

Entre os PHPistas, BASIC é, de novo, a linguagem mais detestada. Interessantemente, o segundo e terceiro lugares pertencem a Erlang e Lisp, respectivamente. Eu achei esse resultado confuso - nunca vi um PHPista que conhecesse qualquer uma dessas duas linguagens o suficiente para detestá-la. Java também é bastante detestado entre os PHPistas, mas não sei se posso levar muito a sério esses resultados considerando a posição do Erlang e do Lisp. Vai entender...

Os Javistas detestam BASIC acima de todas as outras linguagens, mas, em seguida, detestam Smalltalk, Perl e C# igualmente. Interessante, porque pouquíssimas pessoas usam ou usaram Smalltalk (que eu considero uma das linguagens mais interessantes que existem por aí). Eu brinco que o Smalltalk/80 faz o Java/2009 parecer primitivo. E no fundo parece mesmo.

Os amantes do C# também detestam BASIC, o que pode surpreender, uma vez que BASIC é uma linguagem importante no portfolio de linguagens da Microsoft e que Windows é o único ambiente em que C# faz algum sentido. Ainda assim, os C#-istas detestam BASIC com menos energia do que os demais. Em termos de linguagens detestadas, aliás, eles são os que menos detestam.

A turma do Ruby é interessante: detesta BASIC como todo mundo, mas não polariza sua seletividade em nenhuma outra linguagem. Eles detestam muitas linguagens (praticamente todas estão representadas), mas parecem detestá-las igualmente.

Finalmente, o pessoal do Python, que ficou um pouco super-representado nessa pesquisa por conta das listas em que ela foi divulgada, detesta BASIC, como todo mundo, mas detesta Java mais do que qualquer outro grupo - um pouco mais até do que detestam BASIC - coisa única nessa pesquisa. Depois de Java e BASIC, detestam Perl. C# e PHP ficam com distantes quarto e quinto lugares.

O que quer dizer tudo isso?

Muito pouco.

A natureza falha dessa pesquisa não nos deixa tirar conclusões e ter falsas ilusões a respeito de sua validade, mas pode nos apontar em direções interessantes e para outras pesquisas mais elaboradas. Seria interessante saber que outras linguagens os programadores conhecem. Seria bom também relacionar isso com para que plataforma eles desenvolvem. Seria bom também ter mais amostras, mesmo como está - para isso ela vai continuar disponível para preenchimento aqui. Quem quiser respondê-la, sinta-se à vontade.

A pesquisa, assim como está, é um retrato divertido de se olhar e, quanto muito, material para discussões nos botecos próximos aos nossos escritórios. Mas nada muito mais sério do que isso.

E, claro, eu estaria mentindo se dissesse que eu não me diverti muito com ela.

O que, no fim, é o que conta.

Pelo menos pra mim.

Nota: Você encontra esse artigo (com um título melhor, feito por um jornalista de verdade) lá no Webinsider. Lá você também vai encontrar os comentários dos leitores de lá, que são muito mais numerosos que os daqui.

Read More…