domingo, fevereiro 22, 2009

Fazendo Legendas e Aprendendo Sobre a Formula 1 2009

Quem pega o costume de baixar vídeos da internet logo descobre as legendas. Neste post vamos ver uma forma de criá-las, tomando como exemplo uma bela animação que explica as novidades na Formula 1 em 2009.


O Vídeo

O vídeo que tomei por exemplo é uma animação com a locução do Sebastian Vettel, que pode ser encontrada no You Tube em http://www.youtube.com/watch?v=zTkVKPdyWs0

Mesmo que você não se interesse por legendas, vale a pena ver este vídeo:



Baixando o Vídeo

O primeiro passo é trazer o vídeo para o seu micro. Para isto contei com a extensão Video DownloadHelper do Firefox, que você pode baixar de https://addons.mozilla.org/en-US/firefox/addon/3006.

Com o Video DownloadHelper instalado, sempre que um vídeo (compatível) é apresentado, o seu ícone fica ativo. Clicando no ícone você tem a opção de baixar o vídeo. Muito simples e prático.

Mas nem sempre rápido, pois os vídeos não são exatamente pequenos. No caso do vídeo que escolhi, são 20,8M para 02:22 minutos.

Convertendo o Vídeo

O vídeo baixado está no formato flv, que pode ser visto através do VideoLAN (http://www.videolan.org/). Entretanto, este formato não é suportado pelo programa de edição de legendas que eu escolhi, portanto precisa ser convertido.

As minhas duas primeiras experiências de conversão não foram boas: o Prism Video Converter (http://www.nchsoftware.com/prism/plus.html) perdeu o som na conversão. O Format Factory (http://format-factory.en.softonic.com/) converteu o vídeo com muita qualidade, mas o áudio ficou alguns segundos atrasado.

No final acabei usando o recurso de conversão do próprio Video DownloadHelper. É preciso fazer um download adicional para ter este item acrescentado no menu Tools (Ferramentas) do Firefox.


Para obter uma qualidade boa, selecionei a opção MPEG e nos detalhes liguei "Same quality".



Legendando

Chegamos finalmente à parte que interessa. Após algumas tentativas frustradas, encontrei o Subtitle Workshop (http://www.urusoft.net/products.php?cat=sw), que atende às minhas necessidades.


Este programa é bastante rico em recurso e eu usei apenas o básico:
  • Use Movie Open para abrir o vídeo (no meu caso, "F1 new rules.mpg").
  • Use File New Subtitle para iniciar a criação das legendas.
  • Assita o vídeo e pressione Ins no começo de cada frase, para inserir uma legenda neste ponto.
  • Volte o vídeo para o começo e assista novamente. Dê pausa em cada legenda (inicialmente vazias) e digite o texto na parte de baixo da tela.
  • Volte o vídeo para o começo, selecione a primeira legenda na lista e assista mais uma vez. No final de cada frase digite Alt V para marcar o tempo final.
  • Você pode acertar os tempos diretamente na parte inferior da tela, após selecionar a legenda.
  • Use File Save As para salvar as legendas. Existem muitos formatos, normalmente se usa o SubRip (SRT). Se você começar o nome do arquivo de legenda com o nome do arquivo com o vídeo, a maioria dos tocadores usará automagicamente a legenda.
O meu arquivo de legendas pode ser baixado daqui.

Incompatibilidades

O resultado final do procedimento acima (F1 new rules.mpg + F1 new rules.srt) funcionou perfeitamente com o Windows Media Player. Entretanto, o VideoLAN não apresenta as legendas.

Usando o arquivo original (F1 new rules.flv) com o VideoLAN, as primeiras legendas não aparecem.

Se ao invés de MPEG for selecionado AVI (mpg4 + mp3) na conversão do Video DownloadHelper (não esquecendo de selecionar 'same quality'), o video toca com legendas no VideoLAN. Entretanto, o Subtitle Workshop não conseguiu mostrar o vídeo (reclamou da falta de um codec).

É claro que todos estes comportamentos dependem das versões dos softwares e dos codecs.

quinta-feira, fevereiro 19, 2009

Promoção de Aniversário Tecnofagia

A Promoção de Aniversário Tecnofagia, vai sortear R$400,00 em publicidade. Divulgue o Tecnofagia e ganhe espaços de anúncio.

PRÊMIOS

Os prêmios são oferecidos na forma de espaços de anúncio no Tecnofagia.com. Para saber mais detalhes sobre todos os posicionamentos e custos, veja nossa página de publicidade.

  1. Um banner 300×250 na lateral do Tecnofagia, durante 1 mês (valor R$200,00)
  2. Um banner 468×60 nas páginas internas do Tecnofagia, durante 1 mês (valor R$100,00)
  3. Um banner 468×60 na home do Tecnofagia, durante 1 mês (valor R$100,00)
  • O sorteio será no dia 22 de fevereiro, dia do aniversário do Tecnofagia.com.
  • Os banners dos vencedores serão veiculados de 01 a 30 de março de 2009.

Participe da promoção: http://tecnofagia.com/promocao

O Não Tão Básico do Básico - Paradigmas de Programação - Final

Os paradigmas que vimos nas partes anteriores (Não Estruturado, Estruturado e Orientado a Objeto) são os mais usados anteriormente. O que, nem de longe, significa que são os únicos. Para fechar esta série de posts, vamos ver um destes paradigmas menos usuais, a Programação Funcional.

Programação Imperativa

Os paradigmas anteriores são considerados Imperativos: um programa é composto por comandos que alteram um estado (principalmente variáveis) à medida em que são executados. Um dos comandos básicos destas linguagens é a atribuição, onde um novo valor é atribuído a uma variável. Por exemplo, seguindo aproximadamente a sintaxe do Pascal:
Integer n;

n := 3;
writeln (n);
n := 5;
writeln (n);
Neste trecho a variável n recebe inicialmente o valor 3 e posteriormente o valor 5.

Programação Funcional

Na programação funcional um programa são executados através da resolução de expressões. Expressão, neste contexto, se assemelha mais a uma fórmula matemática (que se aplica a vários valores dos seus parâmetros) do que a uma conta aritmética (que é aplicada a valores específicos).

Na programação funcional as funções são de primeira classe, podendo ser usadas nas mesmas construções que valores. Isto possibilita codificar funções de maior ordem, capazes de receber funções como parâmetros e retornar funções. Na maioria das vezes estas funções são puras, isto é, produzem apenas o seu resultado sem nenhum efeito colateral (inclusive entrada e saída).

No lugar de interação (repetições ou loops), a programação funcional costuma usar recursão.

O resultado é uma forma completamente diferente de se resolver problemas e expressar esta solução. Como exemplo, vamos ver o nosso algoritmo de determinação do maior divisor comum na linguagem Haskell.

Escrever este exemplo foi realmente uma viagem, e só consegui porque achei um exemplo pronto. Vamos ao resultado:
module Main where

mdc :: Integer -> Integer -> Integer
mdc a 0 = a
mdc a b = mdc b (a `rem` b)

main = do putStrLn "a ?"
a <- readLn
putStrLn "b ?"
b <- readLn
putStrLn "mdc(a,b) = "
print (mdc a b)
O que se passa aqui?

O maior divisor comum é definido como uma função que recebe dois inteiros e devolve um terceiro. Se o segundo for zero, o resultado é o primeiro, caso contrário o resultado é o maior divisor comum entre o segundo e o resto da divisão do primeiro pelo segundo. É exatamente o que o nosso algoritmo descreve, porém aqui não temos variáveis, interação e controle de fluxo.

A "função principal" é um pouco mais semelhante ao que fizemos nos outros paradigmas; temos até duas variáveis a e b.

Para quem quiser se aprofundar em Haskell, seguem alguns links:

Homepage do Haskell: http://haskell.org/
Tutorial curto: http://haskell.org/haskellwiki/Learn_Haskell_in_10_minutes
Haskell para quem conhece C: http://www.haskell.org/~pairwise/intro/section1.html
Um interpretador livre para Haskell: http://www.haskell.org/hugs/

terça-feira, fevereiro 17, 2009

O Não Tão Básico do Básico - Paradigmas de Programação - Parte 5

Os paradigmas que examinamos até agora (Não Estruturado e Estruturado) se concentram no código, particularmente no fluxo de execução. Como vimos na parte anterior, faz muito sentido agrupar o código às estruturas de dados associadas.

O paradigma de Programação Orientada a Objeto (também conhecido pela sigla em inglês OOP) trata os programas como um conjunto de "objetos" que interagem entre si. Um objeto é um conjunto de dados+código.

Não é deste OOP que estou falando!

A Programação Orientada a Objeto costuma utilizar um linguajar próprio, que enfatiza o objeto como a representação de uma entidade (física ou não). Enquanto que na programação estruturada se fala em rotinas que manipulam dados, na OOP fala-se em solicitar a um objeto que ele execute uma ação.

Um objeto na OOP é um exemplar (instância) de um certo tipo, ou classe. A classe pode ser vista como um molde ou forma que é usado para criar vários objetos com as mesmas rotinas e as mesmas estruturas de dados (mas individualizados pelo conteúdo destas estruturas). As rotinas que fazem parte de um objeto são normalmente chamadas de métodos; as estruturas de dados são chamadas de atributos, campos ou propriedades.

São considerados conceitos fundamentais da programação orientada a objeto: encapsulamento, herança, abstração e polimorfismo.

Encapsulamento é ocultar os detalhes de implementação de um objeto dos demais. É uma evolução do que vimos na Programação Modular. As linguagens projetadas para uso da OOP costumam fornecer recursos bem completos para controle da visibilidade e acesso dos métodos e atributos dos objetos. Desta forma uma classe define uma interface pública, que funciona como uma espécie de contrato entre o programador da classe e os programadores que vão utilizá-la. Isto possibilita (quando feito corretamente) alterar a implementação de uma classe (por exemplo para melhorar a performance ou reduzir o uso de memória) sem impactar o resto do código.

Herança é poder criar novas classes especializando uma classe já existente. Costuma-se falar na classe pai (ou base) e a classe filha (ou derivada). A herança cria uma relação do tipo "é um". Por exemplo, podemos ter uma classe pai "polígono" e classes derivadas "triângulo", "quadrado", etc. O "triângulo" é um "polígono". Ao se derivar uma classe pai, a filha herda os seus métodos e atributos. A classe filha pode acrescentar métodos e atributos ou substituir a implementação de métodos. Por exemplo, um "polígono" pode ter um método "Área" que calcula a sua área; um "quadrado" pode ter uma implementação específica deste método. Uma alternativa á herança é a composição, na qual um objeto de uma classe contem um objeto de outra classe, criando uma relação do tipo "tem um".

Um ponto crítico na análise orientada a objetos é determinar o que deve ser representado por objeto e a relação entre as classes (o que cria a "hierarquia" das classes, que é a "árvore genealógica" das classes).

A Abstração consiste em escolher o nível mais adequado da hierarquia para cada etapa do processamento. Desta forma se procura generalizar os tratamento e não se preocupar com especifidades. A composição também colabora com a abstração, permitido quebrar um objeto complexo em vários objetos mais simples.

O Polimorfismo consiste utilizar um objeto como sendo uma instância de uma classe ancestral, mas mantendo as suas características específicas. Retomando o meu exemplo, um "quadrado" pode ser usado em qualquer situação que requeira um "polígono"; se o método "Área" for chamado será utilizada a versão do "quadrado" apesar dele estar sendo usado como "polígono".

domingo, fevereiro 15, 2009

Comprando Componentes Eletrônicos na Soldafria

Recebi do leitor Luis Claudio Gambôa Lopes a indicação da loja Soldafria (www.soldafria.com) para a compra de componentes eletrônicos. E a minha primeira experiência foi muito boa.

A loja tem uma quantidade muito grande de produtos, a preços bons. Os produtos estão organizados em categorias, o que facilita localizá-los (se bem que algumas classificações não são óbvias). As descrições são bem curtas e não existem links para datasheets, portanto é para quem sabe o que quer comprar.

O valor mínimo para um pedido é de R$5,00; a entrega é pelo Correio (com opção de Sedex e PAC). O processo de cadastramento e compra foram muito tranquilos. O site não tem opção de pagamento on-line; a opção preferencial é o transferência/depósito em conta, porém tem uma opção para pagamento por cartão de crédito (que não usei).

O meu pedido foi um daqueles de chato: um monte de itens de pequeno valor (o mais caro não chegava a R$3,00). O envio via Sedex saiu R$11,20, o que é competitivo com ir de carro+metro até a Santa Efigênia. Logo após completar o pedido recebi um e-mail automático com as instruções para pagamento. Como orientado, após fazer a transferência enviei um e-mail avisando. A primeira surpresa foi que o e-mail foi respondido muito rapidamente, confirmando a transferência. Como coloquei o pedido na quarta após o almoço e o prazo previsto é até 2 dias para postagem mais até 2 dias para entrega, eu só esperava receber na semana seguinte. Imaginem a minha surpresa ao receber a entrega na manhã da quinta!

Os componentes vieram embalados em saquinhos individuais grampeados, em uma caixa bem protegida, junto com a nota fiscal. As quantidades e modelos estavam exatos.

A Soldafria acaba de ganhar mais um cliente satisfeito.

Atualização em 10/03/09: corrigido o link e um erro de digitação. Neste meio tempo fiz uma segunda compra que também correu muito bem.

De Repente 50

Pois é, estou fazendo 50 anos...

Para quem achar que sou "velho", só me resta desejar que passe pelo mesmo, e com boa saúde!

E já aviso que chega rápido. Somente quanto a gente é pequeno é que o tempo parece passar devagar e estamos sempre preocupados com a idade atual e querendo ser mais velho. A partir do momento que você faz 18 deixa de ter uma idade alvo, a faculdade, a futura profissão e os amigos começam a ocupar o tempo todo. No meu caso, vei o emprego, o casamento, os filhos... nem deu tempo para reparar quando os 30 passaram. Os 40 eu reparei de leve, sem traumas. E, num piscar de olhos, estou nos 50.

Muita coisa mudou no mundo nestes 50 anos? Sim, mas a maior parte é evolução, não revolução. Olhando somente para eletrônica e computação, os princípios já estavam lá quando nasci. A primeira patente de um transistor, que abriu o caminho para a miniaturização e a portabilidade, foi em 1925. A primeira demonstração de um transistor foi no final de 47. Calculadoras mecânicas já existiam na antiguidade. Babbage, em 1807, idealizou uma primeira calculadora programável. Grande parte das tecnologias para construção dos primeiros computadores eletrônicos já existiam no final do século 19. Olhando para cima, os primeiros satélites artificiais já tinham sido lançados. A ideia de satélites geoestacionários para comunicação já havia sido publicada em 1928 e divulgada com mais repercussão em 45 por Arthur C. Clark. Uma coisa que muita gente não percebe é a evolução de materiais, que afetou embalagens, roupas e objetos em geral tanto em aparência como funcionalidade.

A medicina avançou bastante. Embora a universalização destes avanços seja lenta, a expectativa de vida tem aumentado continua e significativamente.

As mudanças de comportamento e espírito foram grandes nestes anos. Nos anos 50 prevalecia o otimismo de pós guerra por um mundo melhor guiado pela ciência. Isto foi sendo substituído pelo medo e desconfiança com a guerra fria e o perigo da guerra nuclear. Mais adiante surgiu a preocupação ecológica. Vimemos hoje uma mistura de tudo isto.

Estou curioso sobre o que os próximos 50 anos me trarão.

sexta-feira, fevereiro 13, 2009

O Não Tão Básico do Básico - Paradigmas de Programação - Parte 4

O que vou comentar hoje não é propriamente um paradigma, mas sim uma técnica de programação: a Programação Modular.

Lá no começo, era comum um programa inteiro ser escrito em um único fonte, com milhares de linhas. Por outro lado, a capacidade humana de registrar um contexto é bastante limitada, rapidamente você esquece os detalhes daquilo que não está diretamente na sua frente. O resultado era um aumento dos erros e da complexidade de manutenção, já que o entendimento de um trecho de código podia depender de de detalhes espalhados em todo o fonte.

Um outro problema era que o processo de compilação era lento, pois todo o código tinha que ser recompilado mesmo que um trecho pequeno fosse alterado.

A solução para isto foi permitir quebrar o fonte em diversas partes (módulos), compiladas separadamente e depois juntadas por um linker. Além de permitir quebrar "fisicamente" o fonte (permitindo compilar somente os alterados), o controle da visibilidade de rotinas e variáveis de uma parte para outra (como o uso de static no C) permite quebrar o programa logicamente.

Desta forma, o programador pode agrupar em cada módulo rotinas e variáveis inter-relacionadas e controlar quais delas os outros módulos poderão acessar. Não é exagero dizer que isto viabilizou o desenvolvimento de programas de maior porte.

modulo1.c:
static int var_local;  // não pode ser acessadas pelos demais módulos
int var_publica; // pode ser acessada pelos demais módulos

// esta rotina dá acesso de leitura a var_local para os demais módulos
int LeVarLocal ()
{
return var_local;
}
modulo2.c
extern int var_publica;
extern int LeVarLocal();

// esta rotina usa variável e rotina do modulo1
void Foo ()
{
if (LeVarLocal() > 3)
var_publica = 0;
}
Note que neste pequeno exemplo a divisão em módulos permitiu restringir o acesso à var_local do modulo1. Por outro lado, o programador decidiu permitir a todos os módulos acessar var_publica.

O passo seguinte foi tornar mais formal o uso de módulos, separando a interface (definições das rotinas e variáveis acessíveis por outros módulos) da implementação (que contém as variáveis e códigos propriamente ditos).

Esta preocupação em agrupar variáveis e código inter-relacionados e controlar a interface entre estes grupos nos leva ao próximo assunto: a Programação Orientada a Objetos.

quarta-feira, fevereiro 11, 2009

O Não Tão Básico do Básico - Paradigmas de Programação - Parte 3

A Programação Estruturada

Na parte anterior vimos como inicialmente o controle de fluxo era dominado pelas instruções GOTO, gerando códigos confusos, propícios a bugs e de difícil manutenção. A reação a isto foi a Programação Estruturada.

A Programação Estruturada simplifica o controle de fluxo pela quebra do programa em estruturas geradas encadeando sub-estruturas de uma poucas formas padronizadas:
  • Sequência: na qual uma estrutura é executada incondicionalmente em seguida a outra. Em quase todas as linguagens isto é feito codificando uma estrutura logo em seguida a outra.
  • Seleção: onde uma entre várias estruturas é executada conforme o estado do programa. As linguagens estruturadas fazem isto através de instruções como IF/THEN/ELSE e SWITCH.
  • Repetição: onde uma estrutura é executada até que um certo estado do programa seja atendido. As instruções normalmente encontradas são WHILE, FOR, DO UNTIL.
Cada estrutura deve ter um único ponto de entrada. Na forma mais radical, as estruturas devem ter um único ponto de saída. Na prática, entretanto, ter um único ponto de saída acaba obrigando o encadeamento excessivo de IFs e a criação de variáveis auxiliares, o que aumenta a complexidade. Por este motivo, as linguagens estruturadas costumam fornecer instruções para encerrar uma estrutura antes do seu fim, como BREAK e RETURN.

Embora seja pouco usual, existe uma forma gráfica de representar o fluxo de um programa estruturado. O fluxo abaixo corresponde ao meu exemplo do algorítimo de Euclides:

Reparar que para obedecer às regras da estruturação foi preciso acrescentar uma iniciação dummy para r e duplicar o teste r <> 0 (alguns compiladores otimizadores conseguem suprimir este teste duplicado ao gerar o código).

Para encerrar esta etapa, vejamos o algoritmo de Euclides codificado de forma estruturada em Pascal:
begin
write ('m = ');
read (m);
write ('n = ');
read (n);
r := 1;
while r <> 0 do
begin
r := m mod n;
if (r <> 0) then
begin
m := n;
n:= r;
end
end;
writeln (n);
end.
A seguir: vamos fazer um pequeno desvio para falar sobre a Programação Modular.

segunda-feira, fevereiro 09, 2009

O Não Tão Básico do Básico - Paradigmas de Programação - Parte 2

A programação Não Estruturada

No começo havia o caos... Bem, não exatamente.

Os primeiros computadores eram programados diretamente em linguagem de máquina, por falta de outra opção. Posteriormente surgiu a linguagem Assembly onde cada instrução normalmente corresponde diretamente a uma instrução de linguagem de máquina. Nos anos 50 surgiram as primeiras linguagens de alto nível, particularmente FORTRAN e COBOL.

Em todos estes casos as estruturas de controle de fluxo refletiam de forma bem direta as instruções de máquina, com predomínio do GOTO como forma de desvio do fluxo. Isto se refletia na forma de representação da lógica de programas (antes ou depois da programação): os fluxogramas.


O fluxograma acima representa o algoritmo de Euclides para cálculo do maior divisor comum. O seu entendimento é bastante simples, assim como a sua tradução para uma linguagem de programação como BASIC:
10 INPUT "M: ", M
20 INPUT "N: ", N
30 R = M MOD N
40 IF R = 0 THEN GOTO 80
50 M = N
60 N = R
70 GOTO 30
80 PRINT "MDC = "; N
90 STOP
Uma consequência deste paradigma é a liberdade total no desvio de fluxo, o que permite criar coisas mais complexas como:
6000 REM
6010 REM MOVIMENTACAO
6020 REM
6030 N9=N
6040 N8=0
6050 GOSUB 7000
6060 IF N <> 1 THEN N0=N : A0=A1
6070 PRINT
6080 I = M(A1)
6090 IF I = -2 THEN I=N9
6100 IF I<500 i="I-500" j="O" n="I" n="I-100" t="-1" n="N+1" t =" -1" n="I-200+P" n="I-200"> 2 THEN GOSUB 8000
6420 IF N <> 1 THEN 6500
6430 REM BECO SEM SAIDA. SO SERA POSSIVEL SAIR POR ONDE SE ENTROU
6440 FOR J=1 TO 6
6450 M(J)=2
6460 NEXT J
6470 M(7-A0)=N0
6500 REM ANOTA A VISITA
6530 W(N)=1
6540 N8=N
6600 IF M(1) <> -2 THEN RETURN
6610 REM MOVIMENTO FORCADO, COM DESTINO ALEATORIO
6620 I=M(6)
6630 J=-1
6640 IF M(4) > 100*RND(J) THEN I=M(5)
6650 J=-1
6660 IF M(2) > 100*RND(J) THEN I=M(3)
6670 REM TEMOS UM NOVO DESTINO - REPETIR TUDO !
6680 GOTO 6090
Se isto (código "espaguete") parece um absurdo para o programador moderno, é importante lembrar que esta técnica tem o potencial de deixar o código mais compacto, algo que podia ser importante nos tempos de memória curta. Alguns programadores mais "talentosos" apelavam até para truques como desviar para o meio de uma instrução.

Com o passar do tempo, os computadores foram ficando mais rápidos e com mais memória e as dificuldades de programação e manutenção geradas pela programação não estruturada passaram a ser inaceitáveis, levando a manifestos como o famoso "Go To Statement Considered Harmful" .

A seguir: A Programação Estruturada

sábado, fevereiro 07, 2009

DVD Concert for George - Crítica

Em 29 de novembro de 2002, exatamente um ano após a morte de George Harrison, vários dos seus amigos se reuniram para fazer um show em sua homenagem. O show foi registrado em um DVD que eu finalmente adquiri.

Estamos falando de uma das mais incríveis reuniões de ídolos consagrados (se você não conhece algum dos nomes mencionados, é só procurar na Wikipedia). Em um monento, Dahni (filho de George, parecidíssimo com o pai e que participa do show) assustado com os oito guitarristas ao se redor resolve virar para o fundo do palco - e se dá conta que tem quatro bateristas! O que poderia ser um desastre se torna um momento mágico graças à humildade de todos os presentes, que estão completamente maravilhados de simplesmente participarem do show mesmo que discretamente.

O pacote é composto de dois DVDs, um com o show completo e o outro com uma versão editada (misturando o show com entrevistas) e alguns extras.



DVD Um - Concerto na Íntegra

O evento começa com uma invocação indiana, enquanto Olivia (a viúva de George) acende uma pira. Em seguida temos uma breve introdução de Eric Clapton (responsável pela direção musical do show) e de Ravi Shankar.

A primeira parte do show é música indiana: sob o comando de Anoushka Shankar (filha de Ravi e exímia tocadora de cítara), são executadas "Your Eyes" (de Ravi Shnkar), "The Inner Light" (belíssima música de George, vocal de Jeff Lynne cujo cabelo me lembra uma peruca de palhaço) e "Arpan" (composta por Ravi Shankar especialmente para o show e que encerra com um trecho incrível de Eric Clapton no violão).

Em seguida tem um intervalo que pode parecer deslocado do resto. Além da música, George também atuou como produtor de filmes. Entre outros, ele patrocinou o filme "A Vida de Brian" do grupo Monty Python. Daí a apresentação de dois números musicais do Monty Python: "Sit On My Face" e "The Lumberjack Song" (com a participação discreta de Tom Hanks).

E é hora do rock! Começam a subir as feras no palco (sob o nome de "Banda do George"): Eric Clapton, Jeff Lyne (guitarrista fundador da Eletric Light Orchestra, produtor do álbum Cloud 9 de Harrison com quem tocou no Traveling Wilburrys), Gary Brooker (tecladista compositor de "A Whiter Shade of Pale"), Andy Fairweather-Low (guitarrista que costuma acompanhar Eric Clapton e Roger Waters), Ray Cooper (percurcionista com uma incrível presença no palco, excursionou com Eric Clapton, Pink Floyd, Elton John, etc), Albert Lee (guitarrista extraordinário, mas com atuação discreta no show), Marc Mann (guitarrista pouco conhecido, mas que tem uma atuação marcante no show, executando a maioria das partes de slide guitar típicas de George Harrison), Chris Stainton (teclado), Jim Keltner (baterista, famoso pelo seus trabalhos em conjunto com George, Ringo e Jonh Lennon), e Henry Spinetti (um baterista de estúdio que tocou com um monte de gente famosa) e mais dois baixistas, dois saxofonistas e uma dupla de backing vocals (nada menos que Katie Kissoon e Tessa Niles, com currículos extraordinários).

A segunda parte começa com "I Want to Tell You" (de George, vocal de Jeff Lyne). Em seguida temos "If I Needed Someone" (de George, vocal de Eric Clapton), "Old Brown Shoe" (de George, vocal de Garry Broker e destaque para as guitarras de Andy Fairweather-Low e Marc Mann), "Give Me Love (Give Me Peace on Earth)" (uma música delicada de George, vocais de Jeff Lyne e belos trabalhos de slide de Andy Fairweather-Low e Marc Mann) e "Beware Of Darkness" (George, vocal de Eric Clapton).

Em seguida entra no palco Joe Brown (cantor, guitarrista e padrinho de casamento de George), um homenzarrão tocador de violão e ukulele (uma pequena guitarra havaiana) e sua banda, para interpretar as lentas "Here Comes de Sun" e "That's The Way It Goes" (ambas de George).

Joe Brown sai do palco e volta a banda, acrescida de Jim Capaldi (outro baterista famoso, que trabalhou com Jimi Hendrix e Eric Clapton e na banda Traffic e viveu por muitos anos no Brasil), Jools Holland (pianista) e Sam Brown (cantora enérgica, filha de Joe Brown) para executar "Horse To The Water" (de George e Dahni).

A banda sai então do palco, para a entrada de Tom Petty and The Heartbreakers. Embora um dos guitarristas dos Heartbreakers se esforce, a voz de Tom Petty não combina muito com "Taxman" e a bela "I Need You" parece aguada, sem o pedal de volume que George usou na gravação original e sem os backing vocals de Jonh e Paul. Uma parte da banda se junta aos Heartbreakers para tocar "Handle With Care" sucesso dos Traveling Wilburrys.

E volta mais uma vez a banda toda para tocar a melancólica "Isn't It a Pitty", com vocal de Eric Clapton no início e depois do grande Billy Preston (tecladista que foi o único músico a ter seu nome incluído na capa de um disco dos Beatles) e novo slide de Marc Mann, seguido de um solo de Eric.

"Please Welcome Ringo Starr" anuncia Eric, para delírio da plateia. Ringo canta duas músicas: Photograph (uma música simples de George e Ringo, mas - como o próprio Ringo diz - cujas palavras tomam um novo sentido - All I got is a photograph and I realize you're not coming back anymore) e Honey Don't (de Carl Perkins que George admirava muito). Ao final Ringo anuncia "um outro amigo de George" - Paul McCartney. Ringo vai para a quarta bateria e Paul assume os vocais de "For You Blue" (de George), com mais um slide de Marc Mann e solo de piano de Garry Broker.

Paul pega então um ukulele e inicia uma belíssima versão de "Something" (que todo mundo, menos Frank Sinatra, sabe que é do George), com Paul segurando o começo "no gogó" e aos poucos os outros instrumentos vão entrando. Após o solo (de Marc Mann) Eric se junta a Paul nos vocais - uma combinação incrível. Em seguida a maioria troca a guitarra por violão (exceção para Dhani que estava tocando violão nas demais e nesta toca uma Telecaster e para Marc Mann que mais uma vez assume o slide) para "All Things Must Pass" (de George), com vocal de Paul.

Paul passa então para o piano para "While My Guitar Gently Weeps", dividindo o vocal novamente com Eric. Embora no início Marc Man se destaque na guitarra, quem a faz chorar, como na gravação dos Beatles, é Eric.

E vem depois "My Sweet Lord", com um show de Billy Preston nos vocais e Marc Mann e Andy Fairweather-Low dividindo o slide.

Já em clima de final, Tom Petty and The Heartbreakers se acrescentam à banda para a estranha "Wah-Wah" (composta por George nos tempos mais tumultuados dos Beatles) . E, por último, Joe Brown retorna com o ukulele para a bela "I'll See You In My Dreams" (que não é de George).

DVD Dois - Versão Cinematográfica e Material Adicional

A versão cinematográfica substitui algumas músicas por trechos de entrevistas; a ordem das músicas é também alterada. No final fica supérflua, pois todo o material está mais completo no resto dos DVDs.

O material adicional é composto dos bastidores da apresentação do Monty Python (mostrando a presença do Tom Hanks), uma curta parte dos ensaios da "Banda do George" (com vocais alternativos para partes cantadas por Paul e Tom Petty no show) e da orquestra de Ravi Shankar e entrevistas (como curiosidades como quando o "uncle Ringo" cortou o interesse de Dhani pela bateria).

Concluindo

Imperdível.

quinta-feira, fevereiro 05, 2009

O Não Tão Básico do Básico - Paradigmas de Programação - Parte 1

De uma forma informal, podemos dizer que um Paradigma de Programação é um estilo de programação, sugerindo como um programa deve ser estruturado quanto às suas estruturas de dados e controle de fluxo.

O Básico do Controle de Fluxo

Embora os computadores se notabilizem pela rápida e precisa execução de sequências de instruções, o seu poder e flexibilidade vem da sua capacidade de decidir quais instruções serão executadas.

Os computadores atuais seguem a filosofia de programa armazenado no qual as instruções estão armazenadas em memória e um registrador (o contador de programa) aponta para a próxima instrução a executar. Na operação normal este registrador é incrementado a cada instrução executada para apontar para a instrução no endereço seguinte de memória.

Instruções de desvio permitem alterar este comportamento. O exemplo mais simples é o desvio incondicional (conhecido como goto ou jump) que coloca um novo valor no contador de programa, desviando o fluxo de execução para uma outra região da memória. As instruções de desvio condicional prosseguem sequencialmente ou desviam o fluxo em função de alguma condição. A forma como os desvios condicionais são implementados variam um pouco conforme o processador. O mais comum é a instrução definir (de forma absoluta ou relativa) o endereço para o qual o controle será desviado se a condição for verdadeira, mas existem processadores em que todas as instruções podem ser condicionais ou que possuam instruções do tipo skip que pulam a instrução seguinte se a condição for verdadeira (neste caso a instrução seguinte costuma ser um desvio incondicional).

Uma situação recorrente na programação é a necessidade de executar a mesma sequência de instruções em vários pontos do programa. A solução mais simples é repetir esta sequência em cada ponto, mas isto consome memória de forma redundante e dificulta a manutenção do programa. Surge assim a necessidade de uma instrução que desvie o fluxo para uma sequência de instruções (sub-rotina) porém permita retornar ao ponto seguinte à chamada. Existem várias formas de implementar isto, mas a mais usual é utilizar uma pilha para armazenar o endereço de retorno. A maioria dos processadores possui instruções dedicadas para chamar uma sub-rotina salvando o endereço de retorno no topo da pilha (CALL) e para retornar de uma sub-rotina para o endereço que está no topo da pilha (RET). O uso da pilha não somente permite termos vários níveis de sub-rotina como situações em que uma sub-rotina chama a si mesma (recursividade) ou é chamada por uma rotina que ela chamou (o que também causa uma re-entrância).

É em cima destas instruções básicas do processador que as linguagens de alto nível constroem as suas instruções de controle de fluxo, que os programadores usam para implementar os paradigmas de programação.

A seguir: A programação Não Estruturada

terça-feira, fevereiro 03, 2009

Básico do Básico: Álgebra Booleana

Colaborando com a série do meu amigo Caloni e reaproveitando um artigo meu para o wiki do C&C++ Brasil, vou falar um pouco sobre um fundamento básico da programação que muitos não conhecem de uma forma mais formal: a Álgebra Booleana.

Introdução

A álgebra booleana trata de expressões lógicas. George Boole inventou esta algebra para analisar expressões lógicas envolvendo conjuntos (que ele chamava de classes), porém atualmente a usamos principalmente com variáveis que podem assumir um dentre dois valores: verdadeiro ou falso, 0 ou 1, Sim ou Não, etc.

A álgebra booleana pode ser facilmente expressa através de circuitos elétricos e eletrônicos e é a base do projeto dos computadores digitais. Na programação, um conceito essencial é o desvio (ou tomada de decisão) que consiste em mudar a ordem de execução conforme uma expressão lógica.

Operações na Álgebra Booleana

Conforme mencionado, a álgebra booleana envolve variáveis que podem assumir somente dois valores. Nesta descrição vamos chamar estes valores de 0 e 1, porém lembre-se que eles podem significar qualquer par de condições que sejam exclusivas (isto é, uma variável não pode ser 0 e 1 simultaneamente) e complementares (isto é, se não for 0 é 1 e vice-versa).

Uma forma de representar as operações da álgebra booleana é através das tabelas verdade, que mostram os resultados para todas as combinações de entrada.

Uma primeira operação lógica é a negação (ou complemento), que vamos representar por !
A  !A
0 1
1 0
Uma segunda operação é o E (ou AND) que vamos representar por &&
A B A&&B
0 0 0
0 1 0
1 0 0
1 1 1
Ou seja, o E resulta em 1 somente se os dois operandos forem 1.

Outra operação é o OU (ou OR),representado abaixo por ||
A B A||B
0 0 0
0 1 1
1 0 1
1 1 1
O OU resulta em 1 se pelo menos um dos operandos for 1.

Por último vamos considerar o OU EXCLUSIVO (ou XOR), representado abaixo por ^
A B A^B
0 0 0
0 1 1
1 0 1
1 1 0
O XOR resulta 1 se um e somente um dos operados for 1. O XOR é equivalente a uma combinação de E e OU:

A^B = (A||B) && !(A&&B)

Ou seja, o XOR resulta em 1 se um ou o outro for 1 mas não ambos.

Nos circuitos eletrônicos, devido às características dos circuitos construídos com transistores, é comum encontrarmos outros tipos de operações:

A NAND B = !(A && B)
A NOR B = !(A || B)

Propriedades das Operações Booleanas

Da mesma forma que na aritmética e álgebra comuns, as operações booleanas apresentam uma série de propriedades úteis:
A && 1 = A  (elemento neutro)
A && 0 = 0
A && A = A
A && B = B && A (comutativa)
(A && B) && C = A && (B && C) (associativa)

A || 0 = A
A || 1 = 1
A || A = A
A || B = B || A
(A || B) || C = A || (B || C)

A ^ 0 = A
A ^ 1 = !A
A ^ A = 0
A ^ B = B ^ A
(A ^ B) ^ C = A ^ (B ^ C)

A && (B || C) = (A && B) || (A && C) (distributiva)
A || (B && C) = (A || B) && (A || C)

!(A && B) = (!A) || (!B)
!(A || B) = (!A) && (!B)
Notar que existe alguma semelhança entre, respectivamente, OU e E e SOMA e MULTIPLICAÇÃO, mas ela não é completa.

Álgebra Booleana na Programação

A maioria das linguagens possui o conceito de expressões lógicas, que retornam um valor verdadeiro ou falso. Estas expressões são expressões booleanas e utilizam os operadores que vimos acima. A forma mais comum de se obter um valor lógico é a partir de operadores de comparação, como igual, diferente, maior, maior ou igual, etc.

A principal utilidade das expressões lógicas na programação é no controle de fluxo, onde a próxima instrução a ser executada é determinada pelo resultado da expressão. As construções típicas para isto são os IFs e WHILEs.

As linguagens costumam também permitir armazenar o resultado das expressões lógicas em vaiáveis. A maioria das linguagens possui um tipo específico para isto, outras (como C) se utilizam de um tipo mais genérico.

O conhecimento das operações booleanas e suas propriedades facilitam a conversão de condições descritas textualmente em expressões lógicas, assim como as suas simplificações.

Encerrando com dois exemplos:

Testar se o conteúdo da variável num é maior que 0 e menor que 10

if ((num > 0) && (num < 10))

Testar se o conteúdo da variável num não é maior que 0 e menor que 10

if (!((num > 0) && (num < 10))) ou
if ((!(num > 0)) || (!(num < 10))) ou ainda
if ((num <= 0) || (num >= 10))

Reparar que neste segundo caso a negação "converteu" o E em um OU. Um erro comum é manter o E, escrevendo:

if ((num <= 0) && (num >= 10))

neste caso a expressão será sempre falsa (não existe um número que seja menor ou igual a zero E maior e igual que dez)!

12/05/09: acertada a parte final, onde alguns '<' e '>' bagunçaram tudo.