terça-feira, 26 de fevereiro de 2008

Primos em Smalltalk

VisualWorks Smalltalk é a linguagem orientada a objetos por excelência.

Desenvolvida na década de 1960, baseada na linguagem Simula, muitos paradigmas modernos foram criados por essa equipe do MIT.

Segue aqui um exemplo aplicado de Smalltalk: busca por números primos.

O algoritmo é baseado no crivo de Aristóstenes, que consiste em, a cada primo encontrado, eliminar seus múltiplos até o fim do conjunto.

Vamos começar declarando as variáveis locais count, com a contagem de primos encontrados, max, que conterá o tamanho do conjunto, e primeList, uma lista – na verdade dicionário – dos números marcados como não-primos:
| count max primeList |

count := 0.
max := 5000.
primeList := Dictionary new.


Começamos então com nenhum primo conhecido (0) e um conjunto de cinco mil (5000) números. Vamos agora informar ao dicionário que 1 não é primo e pedir para limpar o Transcript – área (stream) onde será exibido o resultado:
primeList at: 1 put: false.
Transcript clear.


É preciso agora criar um ciclo (loop) de 2 até o máximo de números do conjunto:
2 to: max do: [ :i |


Para cada i, é preciso verificar se é verdadeiro – se o número não existe na lista (ifAbsent:), ele não é múltiplo de nenhum dos números antecessores, portanto é primo (true):
    (primeList at: i ifAbsent: true) ifTrue: [


Se o número é primo, devemos:
  1. exibi-lo no Transcript;
  2. incrementar o contador;
  3. marcar todos seus múltiplos como não-primos


Fechando os colchetes já abertos, o código fica assim:
        Transcript show: i printString; tab.
count := count + 1.
i * 2 to: max by: i do: [ :j |
primeList at: j put: false
]
]
].


Resta agora somente exibir a contagem:
Transcript
cr;
show: 'Quantidade de primos: ', count printString;
cr.


Repara que, quando várias mensagens são enviada ao mesmo objeto, elas são separadas por ponto-e-vírgula (;).

Alguns comentários:
  • Quando um número recebe a mensagem #printString, ele retorna uma representação string para exibição.
  • Transcript trata a mensagem #tab exibindo uma tabulação e a mensagem #cr exibindo uma mudança de linha.
  • Quando um número recebe a mensagem #to:do:, reitera de forma parecida com for de C.
  • A mensagem #to:by:do: funciona como #to:do:, mas acrescenta o passo.
  • A mensagem #at:put: faz o dicionários associar um par chave-valor ou mudar o valor associado a uma chave.
  • A mensagem #at:ifAbsent: faz o dicionário retornar o valor associado a uma chave, ou um valor padrão.
  • A mensagem #ifTrue: é enviada a um objeto booleano, que executa o bloco de código somente se o objeto for verdadeiro. Outras mensagens similares são: #ifFalse:, #ifTrue:ifFalse: e #ifFalse:ifTrue:.


Bem, espero que esse pequeno tutorial seja de alguma ajuda, se não para o mercado, para expandir conhecimentos.

[]'s
Cacilhas, La Batalema

domingo, 17 de fevereiro de 2008

Script para concatenar arquivos

Sempre é válido mostrar o que nossa linguagem favorita é capaz de fazer. Uma das vantagens que percebi em Python desde o começo foi o fato dela ser ideal para o dia-a-dia. Escrever scripts para tarefas repetivas ou que possam ser feitas mais rapidamente via programação, Python está entre as melhores escolhas. O principal motivo é que escrevemos pouco.

Abaixo, eu apresento um script que fiz para pegar os textos dos arquivos que estavam numa pasta e gravá-las no arquivo saida.pgn. Cada arquivo tinha uma partida de xadrez anotada em formato pgn. Queria estas partidas num único arquivo para impressão.
import os
from os import path

saida = open('saida.pgn','w')

pasta_fonte = 'india_do_rei'

pasta_base = path.abspath(pasta_fonte)

lista = os.listdir(pasta_base)

for i in lista:
print path.join(pasta_base,i)
arq = open(path.join(pasta_base,i),'r')
saida.write(arq.read())
arq.close()
saida.write('\n')
saida.close()

Este código pode ser, logicamente, adaptado para suas necessidades. Outra coisa: ele pode ser melhorado. Caso eu precise dele novamente talvez acabe refatorando. Uma das coisas a fazer seria obter o nome da pasta via argumento na linha de comando.

quarta-feira, 13 de fevereiro de 2008

Semáforo

Este vai ser um artigo mais leve… =D

Na lista do PythonBrasil, um dos participantes perguntou como controlar a quantidade de threads em um processo.

Uma forma legal de fazer isso é usando semáforos.

Observação: o exemplo neste artigo foi desenvolvido em Python 2.5.


Vamos usar um exemplo: vamos criar um servidor que aceite conexões TCP, peça alguma string, imprima-a num arquivo de log, envie um sinal de sucesso e encerre a conexão.

Podemos começar com um arquivo de configurações. Pode ser kodserv.conf:
[global]
ip = 0.0.0.0
port = 8001
max = 3
logfile = /tmp/kodserv.log


Agora vamos começar nosso executável – kodserv.py, por exemplo – criando um abeçalho com os requerimentos de módulos um procedimento principal:
#!/usr/bin/env python
# coding: utf-8
# Ou a codificação que você usa

from __future__ import with_statement
from ConfigParser import ConfigParser, NoOptionError
from logging import getLogger, FileHandler
from socket import (
AF_INET,
SO_REUSEADDR,
SOCK_STREAM,
SOL_SOCKET,
socket
)
from threading import Semaphore, Thread
import os, sys


def main():
if len(sys.argv) < 2:
print >>sys.stderr, "Use: %s configfile" % sys.argv[0]
sys.exit(1)

configfile = sys.argv[1]

# Lê o arquivo de configurações
c = ConfigParser()
try:
with open(configfile, 'r') as fd:
c.readfp(fd)
except IOError:
print >>sys.stderr, "File %s not found" % configfile
sys.exit(2)

try:
logfile = c.get("global", "logfile").strip()
except NoOptionError:
# Não tem logfile? Usa STDOUT
logfile = "/proc/%d/fd/1" % os.getpid()

try:
ip = c.get("global", "ip").strip()
except NoOptionError:
# Não tem IP? Usa todos os endereços
ip = "0.0.0.0"

try:
port = c.get("global", "port").strip()
except NoOptionError:
# Não tem port? Usa porta 8001/tcp
port = "8001"

try:
# Máximo de conexões simultâneas
max = c.get("global", "max").strip()
except NoOptionError:
# Não tem max? Vamos aceitar 1000 conexões
max = "1000"

# Endereço
addr = (ip, int(port))

# Cria objeto de log
logger = getLogger()
logger.addHandler(FileHandler(logfile))

# Cria semáforo: o cara da contagem
sem = Semaphore(int(max))

# Cria aplicação principal e inicia
app = AppServ(logger=logger, addr=addr, semaphore=sem)
app.loop()

# Encerra o logger
for handler in logger.handlers:
handler.close()

# Fim!
sys.exit(0)


Agora precisamos criar as classes.

Aplicação principal


A classe principal será AppServ. Seu construtor deve receber o logger, o endereço e o semáforo, criar o socket servidor e armazenar tudo. O método loop() será responsável pelo ciclo de conexões.

O destruidor (__del__()) será responsável por fechar o socket:
class AppServ:
def __init__(self, addr, logger, semaphore):
# Armazenamento dos objetos
self.skt = self.createSocket(addr)
self.logger = logger
self.sem = semaphore

def __del__(self):
# Destruidor
try:
self.skt.close()
except Exception, err:
self.logger.error(err.message)

def createSocket(self, addr):
# Cria o socket
skt = socket(AF_INET, SOCK_STREAM)
skt.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)

# Associa socket ao endereço e prepara socket para ouvir
skt.bind(addr)
skt.listen(1)

# Retorna socket
return skt

def loop(self):
while True:
try:
conn, addr = self.skt.accept()
# Classe que tratará cada conexão
DealConn(
conn=conn,
logger=self.logger,
semaphore=self.sem
).start()
except KeyboardInterrupt:
# Sai com C-c
break


Lidando com as conexões


Precisamos agora criar a classe DealConn para lidar com as conexões recebidas.

Essa classe precisa ser filha de Thread e receber a conexão, o logger e o semáforo. É agora que vamos usar esse útimo!

O semáforo basicamente faz uma contagem regressiva a cada chamada do método acquire() a partir do valor informado no construtor. Caso a contagem chegue a zero, ele trava a execução do fluxo até que alguma thread chame o método release().

O método release() reincrementa a contagem:
class DealConn(Thread):
def __init__(self, conn, logger, semaphore):
# Chama construtor pai
Thread.__init__(self)

# Avisa ao semáforo que foi criado e armazena os objetos
semaphore.acquire()
self.sem = semaphore
self.conn = conn
self.logger = logger

def __del__(self):
# Destruidor encerra socket e libera semáforo
self.conn.close()
self.sem.release()

def run(self):
# Método chamado na thread criada
conn = self.conn

# Envia prompt e aguarda string arbitrária de até
# 1024 caracteres
conn.send("> ")
data = conn.recv(1024).strip()

# Armazena o que foi recebido
msg = "%s:%d sends: %s" % (conn.getpeername() + (data,))
self.logger.warn(msg)

# Avisa da saída
conn.send("Ciau\r\n")


Fazendo funcionar


Só falta agora fazer funcionar!
if __name__ == "__main__":
main()


Se estiver usando algum sistema operacional parecido com Unix, não esqueça de tornar o arquivo executável:
$ chmod +x kodserv.py


Agora execute seu servidor, elimine possíveis erros e tente conectar-se à porta 8001/tcp de localhost. Veja que os logs serão gerados no arquivo informado pela opção logfile do arquivo de configuração, kodserv.conf.

Tente fazer mais de três conexões simultâneas. =D

Depois brinque com o arquivo de configuração e boa diversão! Dúvidas e sugestões nos comentários, por favor.

[]'s
Cacilhas, La Batalema

terça-feira, 12 de fevereiro de 2008

PHP: require e include

A diferença básica entre os dois: require irá produzir um warning e resultar em um Erro Fatal se o arquivo requerido não for encontrado. Já include irá produzir apenas o warning: mesmo que o arquivo requerido não seja encontrado, o script continuará executando.

Além desses dois, existem também as variações _once: require_once e include_once. Como o próprio nome diz, esses dois incluem o arquivo apenas uma vez, seguindo o comportamento característico de require ou include, conforme for o caso. Isso acontece para os casos de um script – por exemplo, o que faz a conexão com o banco de dados – ser incluído mais de uma vez.

Para, mais consulte a documentação do include.