Groovy ou Ruby? Um breve relato.
Enviado em 3 de Outubro de 2008
Publicado por Felipe Rodrigues | Enviar por e-mail
| Hits para esta publicação: 326
Como já é de conhecimento publico, a InfoQ Brasil será lançada no dia 20 de Outubro de 2008 incluindo vários artigos e notícias traduzidos para o protuguês, além do livro Scrum e XP direto das trincheiras também traduzido.
Todos que conhecem o portal InfoQ sabem da qualidade de seu conteúdo e pretendemos manter a mesma qualidade na versão brasileira do portal. Para começar bem, iremos cobrir e publicar as apresentações do Rails Summit 2008 ao longo do próximo ano. Esse evento promete ser um marco na história do Ruby e Rails no Brasil e como tal estarei lá pra conferir. =)
Você devem estar se perguntando o que isso tem a ver com o título do post. =)
Eu explico. Eu pedi pro Floyd Marinescu (co-fundador da InfoQ) uma idéia de quantos posts são publicados por mês na InfoQ original. Ele me respondeu o email dizendo que o máximo que ele podia fazer era conseguir um arquivo CSV com o seguinte conteúdo:
Publications.csv
TYPE;DATE;COMMUNITIES
Interview;15/06/2008;Java
Article;28/03/2008;Architecture
Article;23/05/2008;SOA
News;12/06/2008;.NET
News;12/06/2008;.NET
News;10/06/2008;Java
… continua …
Obviamente isso não resolvia meu problema. Nesse arquivo temos o tipo de publicação (Interview, Article, News ou Presentation), tem a data em que foi publicado e a comunidade em que foi publicado o artigo. O que eu pensei? Vou escrever um parser para esse arquivo e assim obter os dados que preciso.
O objetivo era conseguir um resultado como esse:
ANO
A quantidade de materias desse ano foi: <<TOTAL ANO>>
A media de materias por mes nesse ano foi: <<MEDIA DO ANO>>
ANO
A quantidade de materias desse ano foi: <<TOTAL ANO>>
A media de materias por mes nesse ano foi: <<MEDIA DO ANO>>
O Total de itens é <<TOTAL DE ITENS DO ARQUIVO>>
Decidi que faria esse parser em ruby, abri meu e-editor e comecei a digitar o programa. 15 minutos depois percebi que eu estava indo pelo caminho errado e que não conseguiria o reslutado esperado daquela forma. Meus anos de Java me acostumaram errado. Eu comecei a pensar que deveria haver algum modo mais fácil de fazer aquilo. Decidi então escrever em Groovy, linguagem que possuo mais domínio do que Ruby. Em Groovy não tive dificuldades e construi esse código:
publications.groovy
List items = []
new File(’publications.csv’).splitEachLine(’;') { line ->
items.add(line)
}
def range = 2005..2008
range.each { year ->
println year
numItems = items.findAll{it[1] =~ /${year}$/}.size
println "A quantidade de materias desse ano foi: ${numItems}"
def average = (numItems/12) as BigInteger
println "A media de materias por mes nesse ano foi: ${average}"
def weekAverage = (average/4) as BigInteger
println "A media de materias por semana nesse ano foi: ${weekAverage}\n\n"
}
println "Total de itens em todos os anos: ${items.size}\n"
Foi extremamente fácil e não gastei mais do que 15 minutos para concluir esse código. Decidi então que eu iria escrever o mesmo programa em Ruby, tentando seguir os mesmos passos que realizei em Groovy. O resultado saiu em 10 minutos com o seguinte código:
publications.rb
require ‘csv’
rows = CSV::Reader.parse(File.open(’publications.csv’),’;')
@items = rows.to_a
range = 2005..2008
range.each {|year|
puts year
@thisYearItems = []
@items.each {|item|
if (item[1] =~ /#{year}$/) != nil
@thisYearItems << item
end
}
numItems = @thisYearItems.size
puts "A quantidade de materias desse ano foi: #{numItems}"
average = (numItems/12)
puts "A media de materias por mes nesse ano foi: #{average}"
weekAverage = (average/4)
puts "A media de materias por semana nesse ano foi: #{weekAverage}\n\n"
}
puts "Total de itens em todos os anos: #{@items.size}\n\n"
Dessa experiência pude observar várias semelhanças entre as duas linguagens e também algumas diferenças. Os pontos que mais me chamaram a atenção foram o seguinte:
- Groovy possue mais métodos de conveniência do que Ruby. Esses métodos são projetados para oferecer mais comodidade para o desenvolvedor. Isso fez com que o programa escrito em Groovy utilizasse apenas 23 linhas de código, enquanto o programa em Ruby utiliza 29 linhas.
- Ruby foi mais performático neste caso (rodando ambos como script).
- Ambas são linguagens extremamento flexiveis e poderosas com recursos e facilidades muito boas. Eu não tentei escrever esse programa em Java e nem em C, mas imagino que teriamos pelo menos o dobro de linhas de código. Se alguém se habilitar a fazer essa experiência, eu agradeço.
Analisem os dois exemplos e tirem suas próprias concluões sobre qual delas é mais a sua cara.
Isso me deu uma ótima idéia para postar sobre Groovy on Rails e Ruby on Rails. Também pretendo postar sobre meta-programação nestas duas linguagens.’
Pra terminar o post vale a pena mostrar o resultado, assim já dá uma idéia de quanto conteúdo teremos na InfoQ Brasil.
2006
A quantidade de materias desse ano foi: 949
A media de materias por mes nesse ano foi: 79
A media de materias por semana nesse ano foi: 19
2007
A quantidade de materias desse ano foi: 1525
A media de materias por mes nesse ano foi: 127
A media de materias por semana nesse ano foi: 31
2008
A quantidade de materias desse ano foi: 878
A media de materias por mes nesse ano foi: 73
A media de materias por semana nesse ano foi: 18
Total de itens em todos os anos: 3352
Conto com a participação de todos vocês nesse novo e promissor portal brasileiro. =)
Fala Felipão. Muito bom! Ancioso pela Inauguração do Portal InfoQ.
Abraço
Os números são impressionantes e o comparativo ficou bem legal também.
Lembrei de uma palestra do Neal Ford no JavaOne 2008 comparando “JRuby vs. Groovy”. Vale a pena assistir.
http://nealford.com/downloads/conferences/Neal_Ford-Java1_Comparing_Groovy_and_JRuby-slides.pdf
Abraço.
Eu também to ansioso André. O legal é que você tá ativo nessa batalha. =) Revisei uma tradução sua essa semana, muito boa. Sobre REST.
Luiz,
O Neal Ford manja mesmo dessas linguagens, inclusive em relação ao poder delas para gerar DSLs. O legal que a primeira palestra decente sobre Ruby que eu assisti eu estava com ele, no QCon de Londres. Graças ao Floyd da InfoQ, conheci vários mitos da nossa área. =)
Vou ler essa palestra com certeza. Talvez me inspire a fazer mais posts nesse sentido. =)
Fala Felipe,
O código Ruby podia ter menos linhas, se vc quisesse
Nos vemos no RailsSummit, vida longa ao InfoQ-BR!
[]ão.
Fala Bruno,
Eu imagino que alguém com mais experiência diminuiria muito os dois códigos. =) A verdade é que o metodo findAll do groovy foi o que fez a diferença. O que acha de melhorar meu código Ruby?
Eu estou vivenciando uma situação em que o uso do Groovy me dá bastante poder em relação ao Ruby: tenho vários scripts Ant existentes e eles ficaram complexos e extensos. Estou substituindo vários deles por scripts Gant que permite chamar as tasks do Ant utilizando o Groovy como linguagem de script. Acredito que não teria tanta facilidade se resolvesse fazer o mesmo trabalho utilizando o Ruby…
Olá Paulo,
Não sei como funcionaria em relação ao ANT, mas o Ruby tem o RAKE que também é muito bom para automatizar tarefas.
Fala Felipe!
Parabéns pelo artigo. As duas maneiras ficaram bem simples.
E quem fizer em erlang ganha algum prêmio?
Abraços
Bater em cachorro morto é um perigo!
O seguinte código Java tb possui as mesmas 21 linhas (contagem da IDE)… Descontando as declarações de pacote e do método main, sobram apenas 10 linhas de lógica de negócio…
Nada contra Ruby, mas o exemplo não foi muito feliz….
Uma dúvida / sugestão: o Workshop Modelagem de Testes Ágeis vem para o Rio de Janeiro???
Grande Abraço, e que venha o InfoQ Brasil!
package test;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.TreeMap;
public class JavaInfoQ {
public static void main(String[] args) throws Exception {
TreeMap m = new TreeMap();
BufferedReader fr = new BufferedReader(new FileReader(”c:\\infoq.txt”));
for(String ln=fr.readLine(); ln!=null && ln.trim().length()>0; ln=fr.readLine()) {
Integer y = new Integer(ln.split(”;”)[1].split(”/”)[2]);
m.put(y, m.containsKey(y) ? m.get(y)+1 : 0);
}
for (Integer y : m.keySet()) {
System.out.printf(”%d\nA quantidade de materias desse ano foi: %d\n”, y, m.get(y));
System.out.printf(”A media de materias por mes nesse ano foi: : %f\n”, m.get(y)/12f);
System.out.printf(”A media de materias por semana nesse ano foi: : %f\n\n”, m.get(y)/52f);
}
}
}
Muito bom Fábio. Você consegui esse resultado graças ao TreeMap. (Eu nem havia pensado em utilizar um Map qdo escrevi esse artigo).
A diferença então não está na quantidade de linhas de código (graças ao TreeMap), mas na expressão do código.
Imagine uma situação onde o TreeMap não se encaixe e acrescente aí as linhas de código para organizar essa lista.
Mas a maior diferença e essa sim faria dobrar a quantidade de linhas de código é o fato de que o TreeMap não contém um objeto completo com todos os dados. Ele é apenas um Map contendo o ano e a quantidade de itens de cada ano. Experimente imprimir a quantidade de cada tipo (Articles, News, Presentations e Interviews). Mais uma vez a diferença iria aparecer.
Se você reparar, no exemplo em Groovy e Ruby, cada objeto originado era um objeto completo, com Tipo, Data e Autor. Como é que faz isso em java?
Abração
Show, a discussão agora ficou bastante rica! (e sem nenhum viés de travar uma batalha de linguagens, rsss…).
Em termos de “expressão de código”, Java tomou uma surra na parte do parsing do arquivo. A solução Ruby é de longe mais enxuta e elegante.
Porém, na segunda parte do código, não é tão trivial para um curioso ler e entender a lógica em Ruby. Por exemplo, como é que “23/05/2008″ foi compreendido como 2008? Ou seja, já requer do curioso um pouco mais de conhecimento da sintaxe da linguagem. Minha impressão é que o código em Java nesta parte é mais claro / fácil de compreender.
Não dá pra negar porém o argumento de se obter objetos completos com pouco esforço, e de vc possuir mais facilidades “built-in” para manipulá-los. Neste quesito as linguagens dinâmicas (posso chamar assim?) levam grande vantagem.
Em Java vc precisaria declarar uma classe
Publication[type, date, cmm], contendo um construtor
Publication(String type, String date, String cmm), com toda sorte de lógica de converter String para os tipos adequados (exemplo String->Date).
Porém, como nada na vida é de graça (there’s no free lunch), perde-se a checagem de tipos (o que é discutível, pois é algo que alguns amam e outros odeiam). Eu particularmente gosto desta feature.
Tb acredito que fica mais fácil determinar a “fronteira”, de um objeto em Java. Ou seja, para saber o que faz parte desse objeto (e o que não faz), basta olhar a declaração de sua classe. Mas aí já falo sem ter uma base de comparação de como Ruby lida com isso.
Para o novo desafio, a solução mais simples (sem criar uma nova classe) em Java, já apresenta novo teor de complexidade (mapas de mapas), com menos expressividade para o negócio (porém não chega a ser tão complexa):
TreeMap> typeMap = new TreeMap();
…
String type = split[0];
if (!typeMap.containsKey(type))
typeMap.put(type, new TreeMap());
Map tmap = typeMap.get(type);
tmap.put(y, tmap.containsKey(y) ? tmap.get(y)+1 : 0);
…
for (Integer y : m) {
…
for (String type : typeMap.keySet()) {
// qtde de artigos por tipo no ano “y”
typeMap.get(type).get(y);
}
}
Gostaria de ver a solução em Ruby para poder comparar…
Por fim, tenho muita curiosidade em aprender Ruby, porém respeito muito essa “panela velha” que bota comida no meu prato… Todos sabemos que Java é muito poderosa, e nem sempre é bem utilizada…
Acredito que o ditado “O Java de hoje é o Cobol de amanhã” é apenas uma versão computeira da música “Pais e Filhos”. Cada linguagem tem o poder e a importância característicos de seu tempo e dos problemas que ela resolve neste tempo.
Abs!
A discussão sem dúvida tá interessante, mas não tenho certeza se é pertinente à esse post, até porque em momento algum eu quis dizer que Java não é boa ou não é capaz de fazer coisas que podem ser feitas em Ruby ou Groovy. Outro ponto importante é que eu sou muito mais uma cara de Groovy do que de Ruby (por falta de conhecimento em Ruby mesmo), por isso, como o Bruno falou acima, o código Ruby poderia ser bem menor.
Do ponto de vista de expressão, a única coisa complexa no código groovy e ruby foi o uso da Regex para filtrar o ano. Não acho que a parte de iteração no mapa seja menos expressivo que em Java (Concordo que regex não é tão expressivo, mas o poder da regex na simplicidade do ruby/groovy não poderia ficar de fora do meu exemplo).
Se o desafio foi e de organizar a lista em tipos (Artigos, Interviews, etc..) O código Ruby que eu (um newbie em ruby) gerei foi o seguinte:
require ‘csv’
rows = CSV::Reader.parse(File.open(’publications.csv’),’;')
@items = []
rows.each { |row|
if row[0] == ‘Article’ or row[0] == ‘News’
@items < < row
end
}
range = 2005..2009
range.each {|year|
puts year
@thisYearItems = []
@items.each {|item|
if (item[1] =~ /#{year}$/) != nil
@thisYearItems << item
end
}
numItems = @thisYearItems.size
puts "A quantidade de materias desse ano foi: #{numItems}"
average = (numItems/12)
puts "A media de materias por mes nesse ano foi: #{average}"
weekAverage = (average/4)
puts "A media de materias por semana nesse ano foi: #{weekAverage}\n\n"
}
Mas a minha surpresa maior foi em groovy:
import java.math.*
List lines = []
new File('publications.csv').splitEachLine(';') { line ->
lines.add(line)
}
itens = lines.findAll { it[0] == ‘News’ || it[0] == ‘Article’ }
def range = 2004..2008
range.each { year ->
println year
numItens = itens.findAll{it[1] =~ /${year}$/}.size
println “A quantidade de materias desse ano foi: ${numItens}”
def average = (numItens/12).round(new MathContext(1)) as BigInteger
println “A media de materias por mes nesse ano foi: ${average}”
def weekAverage = (average/4).round(new MathContext(1)) as BigInteger
println “A media de materias por semana nesse ano foi: ${weekAverage}\n\n”
}
Acrescentei apenas 1 linha de código em groovy. Talvez em Ruby dê pra fazer algo tão compacto quanto em Groovy. Se algum usuário avançado de Ruby quiser nos ajudar nesse empasse. Caso contrário acredito que é indiscutível a superioridade do Groovy, pelo menos nesse exemplo.
Java ficou bem mais complexo dessa vez. Isso porque o exemplo é bem curto. Coloque isso em escala e calcule a diferença na quantiade de linhas de código. Não estou dizendo aqui que eu detesto Java. Trabalhei com Java muito tempo e ainda trabalho para alguns clientes. Mas o Groovy me oferece o poder completo da JVM. Eu poderia por exemplo usar o mesmo TreeMap que vc usou em Groovy. (Em Ruby também dá se usarmos o JRuby. Mas não é tão integrado quanto o Groovy).
Valeu Felipe!
O espírito eh esse mesmo. As soluções aqui mostradas só fizeram aumentar minha curiosidade / ansiedade em aprender Ruby.
Fico um pouco perdido / receoso / pé atrás quando vejo as trombetas do apocalypse serem anunciadas para o bom e velho Java (que não foi o seu caso), porém sem um benchmark para comparar.
Neste post tive a oportunidade de avaliar alguns prós e cons em cada abordagem - e a lição que aprendi é que vai ser muito bom quando puder incorporar uma nova arma (Ruby) ao meu arsenal!
Parabéns pelo post e pelas respostas, e aguardo ansiosamente pela realização do próximo Workshop da Fratech no Rio.
Abs!
Linguagens como Ruby e Groovy têm conquistado seu espaço entre as empresas e desenvolvedores e vieram para ficar.
Precisamos fortalecer essas comunidades, principalmente Groovy/Grails que aqui no Brasil ainda é muito fraca.
Essas comunidades facilitam a divulgação e a inicialização de novos desenvolvedores.
Espero que tenhamos um também um “Grails Submmit 2009″
grande abraço!