Neste sábado o Raphael Molesim e eu, podres depois de enfrentar o Ágiles 2009 e a noite de Floripa, resolvemos resolver um Kata proposto pelo Uncle Bob, e fazer um Coding Dojo de fatoração. O Raphael propôs fazermos em Ruby com RSpec, já que, segundo ele, não dá pra fazer Coding Dojo sem RSpec (deixa ele ir na próxima reunião do .Net Architects que mostramos que ele está enganado).

Enfim, resolvemos o problema em pouco menos de uma hora e depois fizemos uma retrospectiva, conferindo como o Uncle Bob havia resolvido o problema dele, já que ele postou um Powerpoint da solução.

Algumas conclusões da retrospectiva:

  • A solução do Uncle Bob foi muito mais simples que a nossa, já que resolvemos antes o problema dos números primos, algo que ele não fez (algo que não era necessário, dependendo de como você encara o problema).
  • O Ruby realmente permite programar sem IDE, mas uma IDE decente faz muita falta, principalmente para quem não conhece muito o Ruby ou as APIs do RSpec. Um Notepad anabolizado, como o GEdit, ou o Notepad++, chega só até uma distância, e não é suficiente.
  • RSpec traz uma maneira muito legal de fazer testes. O código fica com uma elegância invejável.
  • O runner do RSpec não permite rodar só o teste que você quiser, e isso não é legal.
  • A performance no IronRuby é sofrível. Chega a ser 50 vezes mais lento que o MRI.

Vejam o que fizemos. Aqui estão os testes do fatorador, que é a classe que faz a fatoração.

require 'fatorador'

describe "Motor calculo fatoracao" do
    it "deve saber ser criado" do
        Fatorador.new
    end

    it "deve retornar 0 se for informado o número 0" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 0
        numeros[0].should == 0
    end

    it "deve retornar 1 se for informado o número 1" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 1
        numeros[0].should == 1
    end

    it "deve retornar 2 se for informado o número 2" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 2
        numeros[0].should == 2
    end

    it "deve retornar 2,2 se for informado o número 4" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 4
        numeros[0].should == 2
        numeros[1].should == 2
    end

    it "deve retornar 5 se for informado o número 5" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 5
        numeros[0].should == 5
    end

    it "deve retornar 2,3 se for informado o número 6" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 6
        numeros[0].should == 2
        numeros[1].should == 3
    end

    it "deve retornar 2,2,5 se for informado o número 20" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 20
        numeros[0].should == 2
        numeros[1].should == 2
        numeros[2].should == 5
    end

    it "deve retornar 37 se for informado o número 37" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 37
        numeros[0].should == 37
    end

    it "deve retornar 7,7 se for informado o número 49" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 49
        numeros[0].should == 7
        numeros[1].should == 7
    end

    it "deve retornar 2,163 se for informado o número 326" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 326
        numeros[0].should == 2
        numeros[1].should == 163
    end

    it "deve retornar 3 10 vezes se for informado o número 59049" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 59049
        numeros.size.should == 10
        numeros.each {|n| n.should == 3}
    end

    it "deve retornar 3,5,7 se for informado o número 105" do
        fatorador = Fatorador.new
        numeros = fatorador.fatorar 105
        numeros[0].should == 3
        numeros[1].should == 5
        numeros[2].should == 7
    end
end

 

Algumas observações sobre este código:

  1. Erramos na definição de fatoração, que não devia incluir o um. Mas não consertei depois, esse foi o código gerado entre o Raphael e eu, sem a correção.
  2. Um programador experiente de Ruby vai perceber que não somos fluentes na linguagem, mas foi divertido fazer este código.

Depois fizemos um coding dojo com Brian Marick, que foi o keynote speaker do Ágiles 2009, além de ser um dos autores do Agile Manifesto. Com ele aprendemos mais algumas coisas de Ruby e teste com Shoulda. Demos sorte de ele estar no aeroporto, de termos quase uma hora de espera do vôo e de ele ser super simpático. Depois eu vi um kata do Uncle Bob e percebi que podia ficar melhor ainda.

Seguindo em frente, o código da classe foi esse:

require 'Fixnum'

class Fatorador
    def fatorar numero
        return 0 if numero == 0
        if numero.primo?
            [numero]
        else
            numeros = []
            primo = 1
            numero_sendo_divido = numero

            begin
                primo = primo.proximo_primo
                while numero_sendo_divido % primo == 0
                    numero_sendo_divido = numero_sendo_divido / primo
                    numeros << primo
                end
            end while (primo < numero_sendo_divido)

            numeros
        end
    end
end

Notem que ele checa se o número é primo. A rigor isso não é necessário pra bater a meta, que é descobrir os números que fatoram outro número, mas acabamos fazendo isso também, fomos além da meta. Dessa forma, ele evita tentar dividir um número por outro que não é primo, como tentar dividir 15 por 10, por exemplo (outras otimizações ainda teriam que ser feitas pra ficar melhor). Para isso, alteramos a classe Fixnum, que no Ruby define inteiros. É como se fosse a classe System.Int32 do .Net.

Os testes de Fixnum foram esses:

require 'Fixnum'

describe "Numeros primos" do

    describe "primos" do
        it "deve retornar falso se for informado 0" do
            0.primo?.should be_false
        end

        it "deve retorno verdadeiro se for informado 1" do
            1.primo?.should be_true
        end

        it "deve retorno falso se for informado 4" do
            4.primo?.should be_false
        end

         it "deve retorno falso se for informado 6" do
            6.primo?.should be_false
        end

         it "deve retorno verdadeiro se for informado 13" do
            13.primo?.should be_true
        end

         it "deve retorno falso se for informado 21" do
            21.primo?.should be_false
        end

        it "deve retorno verdadeiro se for informado 37" do
            37.primo?.should be_true
        end

        it "deve retorno falso se for informado 49" do
            49.primo?.should be_false
        end
    end

    describe "proximo primo" do
        it "deve conseguir me entregar 3 quando informado 2" do
            primo = 2
            proximo_primo = primo.proximo_primo
            proximo_primo.should == 3
        end

        it "deve conseguir me entregar 5 quando informado 3" do
            primo = 3
            proximo_primo = primo.proximo_primo
            proximo_primo.should == 5
        end

    end
end

E aqui está a classe em si:

class Fixnum
    def primo?
        return false if self == 0

        for i in 2..(self/2) do
            return false if self % i == 0
        end

        true
    end

    def proximo_primo
        proximo = self
        while true
            proximo = proximo + 1
            return proximo if proximo.primo?
        end
    end
end

(Uma coisa um pouco irritante no Ruby é que ele não tem o operador “++”, ou seja, ou você usa “+= 1” ou faz como fizemos, “proximo = proximo + 1”.)

O download do código está aqui. Pra rodar isso tudo em IronRuby você precisa baixar a gem rspec com:

igems install rspec

E colocar os arquivos ispec e ispec.bat no diretório bin do IronRuby (normalmente c:\ruby\bin), já que estes arquivos não são criados na instalação do RSpec. Estes arquivos foram criados por mim depois que eu apanhei um pouco para descobrir como fazer pra criar, se você usa o MRI não vai precisar deste passo extra. Espero que o IronRuby passe a criá-los automaticamente até a versão 1.0, ou ao menos disponibilize uma maneira de criarmos estes arquivos de maneira mais simples, ou um diretório.

Pra rodar o RSpec é muito simples, basta chamar “ispec” e o nome do arquivo sendo testado:

ispec fatorador_spec.rb
ispec fixnum_spec.rb

Aqui vocês vêem os resultados dos testes:

Testes com RSpec rodando no IronRuby

Simples, expressivo e interessante, não é?


Postado na(s) categoria(s) IronRuby pelo Giovanni Bassi em 14 de outubro de 2009 às 12:31 | Tags: ,

Comentar


(Vai mostrar seu Gravatar)

  Country flag

biuquote
  • Comentário
  • Pré-visualização
Loading



Quem é Giovanni Bassi

Giovanni Bassi Sou uma pessoa apaixonada por tecnologia e especificamente por .Net. Sou consultor independente especialista em .Net, focado em arquitetura e melhores práticas. Tenho dezenas de artigos publicados na .Net Magazine, revista da qual sou editor técnico. Ministro palestras e cursos de vez em quando, e quando dá tempo eu respiro um pouco. Mais detalhes nesta página.

Busca

Selos

Eu vou ao TechEd Brasil 2010, e você?

MVP

MCPD

MCSD

.Net Magazine

Abaixo ao if!

Calendário

«  setembro 2010  »
seteququsedo
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
Ver detalhamento de posts no calendário

Blogs interessantes

    OPMLDownload OPML file

    Postagens recentes

    Comentários recentes

    Disclaimer / Aviso
    As opiniões colocadas neste blog são minhas e pessoais e não expressam necessariamente as opiniões de meus empregadores, pareceiros e amigos. Da mesma forma, os comentários feitos por leitores do blog não expressam a minha opinião.

    © Copyright 2010 .Net Unplugged
    Log in