Inércia Sensorial

20 de April de 2011

Pong in Clojure

Filed under: Clojure — Tags: — inerte @ 20:14

Me and Cesar Canassa decided to create a game. I suggested a roguelike, which we will build eventually, but as both newbies in game development, we are going to start small.

With Pong.

In the last couple months I have been teaching myself some Clojure, and said to Canassa I would like to try writing Pong using it. Now here’s where his personality shines: He had never written a single line of Clojure, but said “sure!”. Later on, while researching about functional programming, he mentioned how he had read in several places how one of the few areas of development where this paradigm is not recommended is… game development.

Ooops. Too late.

Beers in hands on a Saturday night, we promised to get something running. The plan didn’t go well. Our lack of Java and Clojure knowledge, and our lack of intelligence after ten beers got in the way. We managed to open a black window and draw white lines on it, but at the same time there was a sense of struggling, a fight against the language and its libraries. We were driving the car down the hill again and again.

Somehow on Sunday, Canassa created a repository on Bitbucket for Clojure Pong, and the first changesets started to roll in.

Today we finished the game. We thought about adding sound to the ball collision on racquets and walls but the game already does much more than training and learning code should do.

We are looking for criticism, specially on idiomatic Clojure. We tried to replace the usual maintaining of a “global” state in variables that is possible in other languages and paradigms, with a “game” map that flies around functions. It is a simple approach, but probably contains a lot of unnecessary overhead. Not in terms of performance, but in terms of function responsibility.

For example, the racquet collision function takes the whole game as parameter. Feels like cheating: Instead of using variables sprinkled to determine what is happening, the values are keys of “game”. It feels like we are still using state, but it is concentrated on a map structure.

But it works, and it is also probably more fun than training and learning code should be. There is a pause screen, the ball rolls slightly angled on game start and scoring, and a FPS counter.

We are going to use some of these stuff on our next project. Breakout is coming, and I will post again when we have some news.

27 de May de 2009

Substituir múltiplas linhas por apenas uma, em PHP

Filed under: PHP — Tags: — inerte @ 21:41

Bati um pouco a cabeça para achar a expressão regular mas lá vai:

1
$string = preg_replace("/(\r\n|\n|\r)+/", "\n", $string);

16 de February de 2009

Como fazer scp em arquivos com espaços no nome

Filed under: Programação — Tags: , — inerte @ 18:59

Dica rápida de Linux na linha de comando. Só colocar entre aspas duplas e simples. Lá vai:

scp [email protected]:"'tem espacos aqui'"

04 de February de 2009

Referrer test

Filed under: Programação — inerte @ 23:01

I’ve made a test page to see how referrers (both server-side and Javascript) will work if Google changes its url query string on search results pages:

http://www.inerciasensorial.com.br/referrer-test/index.php

01 de December de 2008

Alfabeto em Python

Filed under: Python — Tags: , — inerte @ 17:19

Não precisa digitar na mão:

1
alfabeto = 'abcdefghijklmnopqrstuvwxyz'

Isso imprime as letras minúsculas:

1
print map(chr, range(97, 123))

E isso as maiúsculas:

1
print map(chr, range(65, 91))

Mas você também pode contar com o próprio Python (pilhas incluídas, afinal!):

1
2
from string import ascii_letters
print ascii_letters

E de novo:

1
2
3
import string
print string.ascii_lowercase
print string.ascii_uppercase

28 de November de 2008

Internet Explorer 6 e feeds RSS

Filed under: Programação — Tags: , — inerte @ 16:44

Um cliente acabou de mandar um email reclamando que o Internet Explorer 6 dele mostrava o XML ao clicar em um arquivo de RSS na página principal do site.

Pois bem, o IE 6 não sabe o que é RSS. Enquanto eu concordo que deveríamos ter feito algum tipo de tratamento para o IE 6, por exemplo não mostrar o link ou fazer uma página intermediária de assinatura, esse post não é sobre isso.

Faz teeempo que eu não uso o Internet Explorer 6 e não confio na versão que vem no Multiple IEs para todas as coisas. Então procurei na internet, só para confirmar as minhas suspeitas, que o IE 6 não sabe como tratar RSS, e bem… achei um site, na segunda ou terceira página de resultados, falando sobre isso.

Então estou aqui gravando para a posterioridade. É horrível usar essa palavra, pois como eu desejaria que o IE 6 estivesse morto… mas é certo de ainda termos alguns anos com essa praga.

27 de November de 2008

Deletar até o final da linha no vim

Filed under: Programação — Tags: , — inerte @ 18:13

O comando para deletar tudo até o final da linha atual no vim é:

d$

Explicação:

O comando d pode ser seguido de um comando de movimento. dw remove uma palavra (delete word). Como o cifrão vai até o final da linha, d$ remove todos os caracters até o final.

Diretório para os arquivos temporários e de backup do vim

Filed under: Programação — Tags: , — inerte @ 17:57

Cansei dos arquivos .swp e ~ criados pelo vim infestarem meus diretórios. Basta editar o arquivo de configurações do editor (.vimrc no Linux e _vimrc no Windows) colocando o seguinte:

" liga o backup
set backup
" Aonde gravar os arquivos de backup
set backupdir=c:\temp
" Onde gravar os arquivos temporários
set dir=c:\temp

Para especificar uma localização diferente. Troque c:\temp pelo diretório que você deseja.

01 de August de 2008

Tutorial de jQuery

Filed under: Javascript — Tags: , , , — inerte @ 17:37

Tradução de Getting Started with jQuery

Esse tutorial é uma introdução à biblioteca jQuery. É necessário conhecimento básico de Javascript e document object model (DOM). Partiremos bem do começo e explicaremos detalhes quando necessário. Abrangiremos um simples exemplo de olá mundo, seletores e eventos básicos, AJAX, FX, e uso e confecção de plugins.

O tutorial não tem “clique aqui” para ver as funcionalides. A intenção é que você copie o código e tente você mesmo. Copiar, ver o quê faz, e modificar.

  1. Instalação
  2. Olá jQuery
  3. Me ache: Usando seletores e eventos
  4. Me qualifique: Usando Ajax
  5. Me anime: Usando Efeitos
  6. Me ordene: Usando o plugin tablesorter
  7. Me abrace: Escrevendo seus próprios plugins

Instalação

Para começar, precisamos de uma cópia da biblioteca jQuery, disponível no site oficial. O jQuery Starterkit já vem com algum código e CSS para facilitar. Depois de puxar e extrair o arquivo escolhido, colocamos o arquivo jquery.js no mesmo diretório, e basta abrir starterkit.html no e custom.js no seu editor preferido, e starterkit.html com o navegador.

Já temos tudo para começar com o famigerado exemplo “Olá Mundo”.

Olá jQuery

Nós começamos com um esqueleto de página html:

1
2
3
4
5
6
7
8
9
10
11
<html>
 <head>
 <script type="text/javascript" src="jquery.js"></script>
 <script type="text/javascript">
   // adicionaremos nosso código Javascript aqui
 </script>
 </head>
 <body>
   <--- adicionaremos nosso conteúdo HTML aqui -->
 </body>
 </html>

Essa página esqueleto apenas carrega a biblioteca jquery.js (tenha certeza que a URL, a parte dentro do atributo src, aponta para sua cópia local do arquivo jquery.js! Nesse exemplo assumimos que ele está no mesmo diretório dessa página exemplo). Os dois comentários indicam onde iremos adicionar novo código.

Como quase tudo feito com jQuery lê ou manipula o document object model (DOM), precisamos ter certeza que adicionaremos eventos (ou outras ações) assim que o DOM estiver pronto.

Para fazer isso, registramos um evento ready no document:

1
2
3
$(document).ready(function() {
   // fazer algo quando o DOM estiver pronto
 });

Colocar apenas um alert() nessa função não faz muito sentido, já que o alert() não requer o carregamento do DOM. Então vamos tentar algo mais sofisticado: Chamar alert() quando um link é clicado.

Adicione o seguinte dentro de <body>:
[html]Link[/html]

Agora modifique a função $(document).ready:

1
2
3
4
5
$(document).ready(function() {
   $("a").click(function() {
     alert("Olá Mundo!");
   });
 });

E assim você deve ver o alerta assim que clicar no link.

Vamos ver melhor o quê estamos fazendo: $(“a”) é um seletor jQuery, e nesse caso, ele seleciona todos os elementos a. $ é um apelido para a função jQuery, então $() constrói um novo objeto jQuery. A função click(); chamada em seguida é um método do objeto jQuery. Ele gruda um evento click para todos os elementos selecionados (no caso do nosso exemplo, uma única ligação/link) e executa a função especificada quando o evento ocorre.

Tudo isso é similar ao seguinte código:
[html]Link[/html]

Mas da nossa maneira, não precisamos escrever onclick() para cada elemento. Nós separamos a estrutura (HTML) do comportamento (JS), assim como separamos estrutura e apresentação utilizando CSS.

Isso posto, vamos explorar seletores e eventos mais longe.

Me ache: Usando seletores e eventos

jQuery nos dá duas maneiras de selecionar elementos. A primeira usa uma combinação de CSS e seletores XPath passados como texto para o construtor jQuery (ex: $(“div > ul a”)). A segunda maneira usa diversos métodos do objeto jQuery. E as duas maneiras podem ser misturadas.

Para experimentar com alguns desses seletores, vamos escolher e modificar a primeira lista ordenada do starterkit.

Para começar, queremos selecionar a lista em si. A lista tem o id “orderedlist”. No puro Javascript, você poderia selecioná-la usando document.getElementById(“orderedlist”). Com jQuery, fazemos assim:

1
2
3
$(document).ready(function() {
   $("#orderedlist").addClass("red");
 });

O starterkit vem com uma folha de de estilos (CSS) com uma classe chamada “red” que simplesmente especifica uma cor de fundo vermelha. Logo, quando você recarregar a página no seu navegador, você deverá ver a primeira lista com uma cor de fundo vermelha. E a segunda lista, sem modificações.

Agora vamos adicionar mais algumas classes aos elementos filhos dessa lista:

1
2
3
$(document).ready(function() {
   $("#orderedlist > li").addClass("blue");
 });

O código acima seleciona todos os filhos lis do elemento com o id orderedlist e neles adiciona a classe “blue”.

Agora algo um pouco mais sofisticado. Nós queremos adicionar e remover a classe quando o usuário passar o mouse em cima do elemento li, mas apenas no último elemento da lista.

1
2
3
4
5
6
7
$(document).ready(function() {
   $("#orderedlist li:last").hover(function() {
     $(this).addClass("green");
   },function(){
     $(this).removeClass("green");
   });
 });

Para cada evento onXYZ, como onclick, onchange, onsubmit, existe um equivalente jQuery. Alguns outros elementos, como ready e hover, são disponibilizados como métodos convenientes para certas tarefas.

Com esses seletores e eventos você já pode fazer um monte de coisas, mas tem mais.

1
2
3
4
5
$(document).ready(function() {
   $("#orderedlist").find("li").each(function(i) {
     $(this).append( " BAM! " + i );
   });
 });

O método find() permite procurar em descendentes do elemento já selecionado, portanto $(“#orderedlist”).find(“li”) é similar a $(“#orderedlist li”).

O método each() itera em cada elemento e permite processamento futuro. Muito métodos, como addClass(), usam each() eles mesmos.

No exemplo anterior, append() é usando para concatenar algum texto no elemento.

Outra tarefa comumente encontrada é chamar métodos em elementos DOM que não existem no jQuery. Imagine um formulário que você gostaria de recomeçar após submetê-lo com sucesso via AJAX.

1
2
3
4
5
6
$(document).ready(function() {
   // use isso para recomeçar um único formulário
   $("#reset").click(function() {
     $("form")[0].reset();
   });
 });

O código acima seleciona o primeiro elemento form e chama reset() nele. Caso você tivesse mais de um formulário na página, você também poderia fazer assim:

1
2
3
4
5
6
7
8
$(document).ready(function() {
   // use isso para recomeçar vários formulários de uma vez
   $("#reset").click(function() {
     $("form").each(function() {
       this.reset();
     });
   });
 });

O exemplo anterior seleciona todos os formulário no seu document, itera eles e chama reset() para cada um. Note que dentro da função each(), this faz referência ao elemento atual. Também note o seguinte, já que a função reset() pertence ao elemento form e não ao objeto jQuery, não podemos simplesmente chamar $(“form”).reset() para recomeçar todos os formulários da página.

Um desafio comum é selecionar apenas elementos de um grupo de similares ou idênticos. jQuery tem os métodos filter() e not() para isso. Enquanto filter() reduz a seleção de elementos aos encontrados na expressão de filtro utilizada, not() faz o contrário e remove todos os elementos encontrados na expressão. Imagine uma lista não ordenada onde você quer selecionar todos os elementos li que não tenham filhos ul.

1
2
3
$(document).ready(function() {
   $("li").not(":has(ul)").css("border", "1px solid black"); 
 });

Esse código seleciona todos os elementos li, remove os que tenham filhos ul, e aplica uma borda preta neles.

A sintaxe [expression] é derivada do XPath e pode ser usada para filtrar por atributos. Talvez você queira selecionar todos os links que tenham o atributo name:

1
2
3
$(document).ready(function() {
   $("a[@name]").css("background", "#eee" );
 });

Provavelmente mais comum que selecionar links por name, você queira selecionar links pelo atributo “href” deles. Isso pode ser um problema, já que os navegadores inconsistentemente tratam o que é “href” cada um à sua maneira. Para selecionar parte de um valor, podemos usar o seletor contém “*=” ao invés de igual “=”:

1
2
3
4
5
$(document).ready(function() {
   $("a[@href*=/content/gallery]").click(function() {
     // faça algo com todos os links que tenham /content/gallery
   });
 });

Até agora, todos os seletores foram usados para escolher filhos ou filtrar a escolha atual. Existem situações onde você precisa escolher os elementos anteriores ou próximos, conhecidos como irmãos. Imagine uma página de FAQ, onde as perguntas estão inicialmente escondidas, mas são mostradas quando a pergunta é clicada. O código jQuery para isso é:

1
2
3
4
5
$(document).ready(function() {
   $('#faq').find('dd').hide().end().find('dt').click(function() {
     $(this).next().slideToggle();
   });
 });

Aqui nós usamos cascateamento para reduzir o tamanho do código e melhorar a performance, já que ‘#faq’ é apenas selecionado uma única vez. Ao usar end(), o primeiro find() é desfeito, para procurarmos com o próximo find() no elemento #faq, ao invés dos filhos de dd.

Dentro de click(), usamos $(this).next() para procurar o próximo irmão do atual dt. Isso nos permite rapidamente selecionar a resposta seguinte à questão clicada.

Além de irmãos, você também pode selecionar elementos pais. Talvez você queira destacar o parágrafo pai do link que o usuário está passando o mouse em cima. Tente o seguinte:

1
2
3
4
5
6
7
$(document).ready(function(){
   $("a").hover(function(){
     $(this).parents("p").addClass("highlight");
   },function(){
     $(this).parents("p").removeClass("highlight");
   });
 });

Vamos voltar um passo antes de continuar: jQuery foca em fazer o código menor e portanto mais fácil de ler e manter. A linha seguinte é um atalho para $(document).ready(callback):

1
2
3
$(function() {
   // code to execute when the DOM is ready
 });

Aplicando ao exemplo Olá Mundo!, temos isso:

1
2
3
4
5
$(function() {
   $("a").click(function() {
     alert("Olá Mundo!");
   });
 });

Coberto o básico, exploraremos outros aspectos, começando com AJAX.

Me qualifique: Usando Ajax

Nessa parte escreveremos uma pequena aplicação Ajax, para dar notas a alguma coisa, igual é feito com os vídeos em youtube.com.

Nós não desejamos que esse exemplo funcione sem Ajax, apesar de ser possível, então geraremos o código e texto com jQuery a concatenaremos ele em um div com id “rating”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$(document).ready(function() {
   // gerar código
   $("#rating").append("Por favor dê nota: ");
 
   for ( var i = 1; i <= 5; i++ )
     $("#rating").append("<a href='#'>" + i + "</a> ");
 
   // adicionar código no div e aplicar gerenciadores de cliques nos links
   $("#rating a").click(function(e){
     // prevenir click normal em links
     e.preventDefault();
 
     // enviar requisição
     $.post("exemplo.php", {rating: $(this).html()}, function(xml) {
       // formatar e mostrar resultado
       $("#rating").html(
         "Obrigado pela nota, médio atual: " +
         $("average", xml).text() +
         ", número de votos: " +
         $("count", xml).text()
       );
     });
   });
 });

Esse pedaço de código gera cinco elementos a e concatena eles no elemento com id “rating”. Depois, para cada link no div, um gerenciador de clique é adicionado. Quando o link é clicado, uma requisição post é enviada para exemplo.php com o conteúdo do link como parâmetro. O resultado retornado como XML é adicionado ao div, substituindo os links.

Um problema encontrado com frequência ao carregar conteúdo via Ajax é o seguinte: Ao adicionar gerenciadores de eventos no document que também deveriam ser aplicados no conteúdo, você deve aplicá-los depois do carregamento do conteúdo. Para prevenir duplicação de código, você pode delegar à uma função. Exemplo:

1
2
3
4
5
6
function addClickHandlers() {
   $("a.remote", this).click(function() {
     $("#target").load(this.href, addClickHandlers);
   });
 }
 $(document).ready(addClickHandlers);

Agora addClickHandlers é chamado apenas uma vez quando o DOM estiver pronto e toda vez que o usuário clicar em um link com a classe remote e o conteúdo já terminou de carregar.

Atenção ao $(“a.remote”, this), isso é passado como um contexto: Para o evento ready do document, this refere-se ao documento, e portanto procura no document inteiro por links com a classe remote. Quando addClickHandlers é usado como callback para load(), this refere-se a um elemento diferente: No nosso exemplo, o elemento com id “target”. Isso previne que o evento click seja aplicado diversas vezes aos mesmos links.

Outro problema comum com callbacks são parâmetros. Você especificou seu callback mas precisa passar um parâmetro adicional. A maneira mais fácil de fazer isso é colocar o callback dentro de outra função:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// pegar dados
 var foobar = ...;
 
 // especificar gerenciador, precisa de data como parâmetro
 function handler(data) {
   //...
 }
 
 // adicionar click() e passar foobar
 $('a').click(function(){
   handler(foobar);
 });
 
 // se você precisar do contexto do gerenciador original, use apply():
 $('a').click(function(){
   handler.apply(this, [foobar]);
 });

Agora que vimos um pouco de Ajax, vamos adicionar um pouco de simples efeitos e animações à página.

Me anime: Usando Efeitos

Simples animações com jQuery são realizadas com show() e hide():

1
2
3
4
5
6
7
$(document).ready(function(){
   $("a").toggle(function(){
     $(".stuff").hide('slow');
   },function(){
     $(".stuff").show('fast');
   });
 });

Você pode criar qualquer combinação de animações com animate(), ex: deslize com opacidade:

1
2
3
4
5
6
7
$(document).ready(function(){
   $("a").toggle(function(){
     $(".stuff").animate({ height: 'hide', opacity: 'hide' }, 'slow');
   },function(){
     $(".stuff").animate({ height: 'show', opacity: 'show' }, 'slow');
   });
 });

Muitos outros efeitos legais podem ser feitos com a coleção de plugins Interface. Apesar de Interface estar no topo da lista de plugins para jQuery, existem muitos outros. A próxima parte usa o plugin tablesorter.

Me ordene: Usando o plugin tablesorter

O plugin tablesorter permite ordenar tabelas direto no navegador. Você inclui a biblioteca jQuery e o arquivo do plugin, bastando falar quais tabelas você quer ordenar.

Para esse exemplo funcionar você precisa puxar o plugin tablesorter e adicionar a seguinte linha em starterkit.html (abaixo da qual inclui o jquery):

1
<script src="jquery.tablesorter.js"></script>

Depois de incluir o plugin, você pode chamá-lo assim:

1
2
3
$(document).ready(function(){
   $("#large").tablesorter();
 });

Tente clicar nos cabeçalhos da tabela do arquivo de exemplo e veja como ela é ordenada de maneira ascendente no primeiro clique e descendente na segunda.

A tabela pode ter as linhas destacadas, e podemos fazer isso passando algumas opções:

1
2
3
4
5
6
$(document).ready(function() {
   $("#large").tablesorter({
     // visual listras
     widgets: ['zebra']	
   });
 });

A maioria dos plugins pode ser usada assim. Inclua o arquivo do plugin e chama seus métodos em alguns elementos, passando configurações adicionais para customização.

Uma lista atualizada de plugins pode ser encontrado no site jQuery Plugin.

Quando você estiver usando jQuery com frequência, você pode achar útil empacotar seu código como um plugin, ou para reusá-lo você mesmo ou na sua empresa, ou para compartilhá-lo com a comunidade. O próximo capítulo dá algumas dicas sobre como estruturar um plugin.

Me abrace: Escrevendo seus próprios plugins

Escrever seu próprio plugin para jQuery é bem fácil. E atendo-se às seguintes regras, facilitará para outras pessoas integrarem o seu plugin.

Nome do plugin

Ache um nome para ele, chamaremos o nosso de “foobar”. Crie um arquivo chamado jquery.nomedoplugin.js, ex: jquery.foobar.js

Adicione métodos próprios

Crie um ou mais métodos do plugins extendendo o objeto jQuery, ex:

1
2
3
jQuery.fn.foobar = function() {
   // fazer algo
 };

Que estará disponível executando:

1
$(...).foobar();

Confirguração padrão

Crie configurações padrões que podem ser modificadas pelo usuários, ex:

1
2
3
4
5
jQuery.fn.foobar = function(options) {
   var settings = jQuery.extend({
     value: 5, name: "pete", bar: 655
   }, options);
 };

Você pode chamar o plugin sem opções, usando as padrões:

1
$("...").foobar();

Ou com algumas opções:

1
$("...").foobar({ value: 123, bar: 9 });

Documentação

Se você disponibilizar seu plugin, você pode dar alguns exemplos e documentação. Existem vários plugins disponíveis que você pode usar como referência.

O conhecimento básico para escrever plugins é esse. Vamos escrever um nosso.

Plugin checkbox

Muita gente, ao manipular formulários com jQuery, pergunta como marcar e desmarcar botões radio ou checkboxes. O código sai assim:

1
2
3
4
5
$("input[@type='checkbox']").each(function() {
   this.checked = true;
   this.checked = false; // ou, para desmarcar
   this.checked = !this.checked; // ou, para inverter
 });

Como plugin, é bem direto:

1
2
3
4
5
jQuery.fn.check = function() {
   return this.each(function() {
     this.checked = true;
   });
 };

E agora ele pode ser usado:

1
$("input[@type='checkbox']").check();

Você também poderia escrever plugins para uncheck() e inverterCheck(). Mas ao invés disso vamos extender nosso plugins para aceitar algumas opções.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
jQuery.fn.check = function(mode) {
   // se mode não está definido, usaremos 'on' como padrão
   var mode = mode || 'on';
 
   return this.each(function() {
     switch(mode) {
       case 'on':
         this.checked = true;
         break;
       case 'off':
         this.checked = false;
         break;
       case 'toggle':
         this.checked = !this.checked;
         break;
     }
   });
 };

Permitindo um padrão para a opção, o usuário pode omitir a opção ou passar outra: “on”, “off” e “toggle”, ex.:

1
2
3
4
$("input[@type='checkbox']").check();
 $("input[@type='checkbox']").check('on');
 $("input[@type='checkbox']").check('off');
 $("input[@type='checkbox']").check('toggle');

Configurações opcionais

Com mais de uma opção opcional, dessa maneira fica complicado, pois o usuário precisaria passar valores nulos nos quais ele deseja omitir.

O uso de tablesorter anteriormente demonstra a aplicação de um objeto para resolver esse problema. O usuário pode omitir todos os parâmetros ou passar um objeto com chave/valor para cada configuração a ser sobrescrita.

Como exercício, você pode tentar reescrever o código de Nota anterior como plugin. O esqueleto do código seria parecido com:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jQuery.fn.rateMe = function(options) {
   // ao invés de selecionar um container fixo com 
   // $("#rating"), usaremos o contexto jQuery
   var container = this;
 
   var settings = jQuery.extend({
     url: "exemplo.php"
     // mais padrões iriam aqui
   }, options);
 
   // ... resto do código ...
 
   // se possível, retorne "this" para não quebrar o cascateamento
   return this;
 });

Permitindo você executar o plugin da seguinte maneira:

1
$(...).rateMe({ url: "test.php" });

26 de July de 2007

Crivo de Eratóstenes em Python

Filed under: Python — inerte @ 12:56

Estou fazendo os desafios do Projeto Euler e nos dez primeiros já vi três com números primos. Meu primeiro algoritmo ingenuamente testava a primalidade de um número tentando dividí-lo por todos os números menores que ele.

Uma lista com 1.000 números primos dessa maneira gera em pouco menos de um segundo no meu computador. Já 2.000, mais de 9 segundos. E 10.001, como pedido por um dos desafios, eu nem tive paciência de esperar os cálculos acabarem. Minha primeira implementação foi a seguinte:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def primo(n):
    for i in xrange(2, n):
        if n % i == 0:
            return False
    return True
 
primo_lista = []
n = 1
while 1:
    if primo(n):
        primo_lista.append(n)
        if len(primo_lista) == 1000:
            break
    n = n + 1

Pocurando por métodos mais velozes achei dois, o Crivo de Eratóstenes e o Crivo de Atkin. O primeiro é mais simples, então decidi implementá-lo. O Crivo de Eratóstones é definido assim:

  1. Escreva uma lista dos números entre 2 e o maior número que você quer testar a primalidade. Vamos chamá-la de lista1;
  2. Escreva o número 2, o primeiro número primo, em outra lista que conterá os números primos encontrados. Vamos chamá-la de lista2;
  3. Remova o 2 e todos os múltiplos dele da lista1;
  4. O primeiro número que sobrar da lista1 é primo. Escreva ele na lista2;
  5. Remova essa número e todos os seus múltiplos da lista1. A remoção pode começar da raiz quadrada do número, já que múltiplos menores foram removidos em passos anteriores;
  6. Repita os passos de 4 a 6 até não restarem mais números na lista1;

A classe a seguir, escrita em Python, retorna uma lista com todos os números primos entre 2 e um número especificado (max_n).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
max_n = 50
 
class CrivoDeEratostenes:
    def __init__(self, max_n):
        self.lista = range(2, max_n + 1)
        self.primo_lista = [2]
        self.crivo()
 
    def crivo(self):
        primo_n = self.lista[0]
        max_n = self.lista[-1] + 1
        self.lista.remove(primo_n)
        for n in self.lista:
            if n % primo_n == 0:
                self.lista.remove(n)
        if len(self.lista) > 0:
            self.primo_lista.append(self.lista[0])
            return self.crivo()
        else:
            return self.primo_lista
 
 
print CrivoDeEratostenes(max_n).primo_lista
 
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

Pesquisando sobre Eratóstenes na Internet, descobri que ele foi um animal. O grego viveu dois séculos antes de Cristo e conseguiu calcular com 16% de erro a circunferência da Terra. Leu nos papiros de Biblioteca de Alexandria (aonde foi Diretor) que o sol iluminava o fundo de um poço na cidade de Siena (hoje Assuã) ao meio dia do dia mais longo (o solstício) no verão daquele cidade, em 21 de Junho. Ou seja, a luz fazia um ângulo reto com a Terra.

Mas no dia 21 de Junho, quando era meio dia em Siena, o Sol projetava sombras em Alexandria. Eratóstenes conclui então que a Terra era redonda. O ângulo das sombras deu 7º12′, quase 1/50 dos 360º de uma circunferência, então a distância entre Alexandria e Siena multiplicada por 50 seria a circunferência do planeta.

Mandou um pessoal percorrer essa distância e eles voltaram com 925km. E 50 vezes 925 é 46.250 km, enquanto a Terra tem 40.076 km.

Queria crescer de novo pra ser igual a ele 🙂

« Newer PostsOlder Posts »

Powered by WordPress