A provinha do GDD, em BASIC (para micros de 8 bits)
Eu confesso que me diverti resolvendo a provinha do GDD. Pra quem chegou agora, foi um teste aplicado pelo pessoal da Google para filtrar inscritos no Google Developer Day. Como eles tiveram muito mais inscritos do que espaço físico comportava, fazia sentido. Embora muitos tenham reclamado, era uma prova muito simples e eu resolvi usando duas linguagens diferentes, Python e Clojure, ambas modernas.
BASIC?!
Um passatempo meu é em paleocomputação, em particular computadores obsoletos, daquelas famílias que não deixaram descendentes diretos. Eu coleciono e, quando possível, restauro, computadores antigos - se você tiver um desses, a propósito, aceito doações. Por isso, eu resolvi brincar um pouco e resolver a provinha do GDD usando um TRS-80 Model III e um Apple II (um //e enhanced, para ser mais preciso). Para tornar a minha vida mais fácil (e evitar um divórcio) eu usei emuladores em vez de computadores reais. Com isso eu pude escrever o código usando um editor de textos de verdade e "colá-lo" no emulador. Em ambas as plataformas, usei seus interpretadores BASIC embutidos na ROM.
Um outro motivo é por esse dialeto de BASIC ser uma linguagem muito mais simples e pobre de recursos do que as duas outras que eu usei nessa brincadeira. Essa simplicidade é visível nas construções que aparecem nesses programas. É interessante notar como os mecanismos de controle de fluxo do programa são mínimos e se mapeiam quase que diretamente ao que o processador entende.
O emulador
A história do emulador é, por si, interessante. Para os dois eu usei o MESS. MESS é uma derivação do MAME que pretende replicar, da forma mais precisa possível, computadores obsoletos. Como os pacotes do Ubuntu me deram algumas dores-de-cabeça, eu compilei a versão mais nova do MESS. Não é difícil e o pessoal do IRC (#messdev na EFNet) foi mais do que gentil e eu teria apanhado muito mais sem eles.
Aliás, quanto a essa coisa de IRC, se vocë desenvolve software e não usa, devia usar. IRC é como um monte de projetos são coordenados, reuniões são feitas e dúvidas tiradas. É normalmente mais rápido que listas de discussão e fórums web. Ache velho e feio por sua conta e risco. Eu não levo a sério nenhum projeto de software aberto que não tenha um canal de IRC com desenvolvedores e usuários conversando.
Mas vamos aos problemas, que são o mais interessante
1) O que esse programa faz?
x = 7 y = 4 if y > 2, then y = y * 2 else x = x * 2 print (x + y)
Em um TRS-80, faríamos assim:
10 X = 7 20 Y = 4 30 IF Y > 2 THEN Y= Y * 2 ELSE X= X * 2 40 PRINT X + Y
Mas isso seria uma estupidez. BASIC (aquele dos anos 70 e 80, pelo menos) é um ambiente interativo - você pode testar idéias de forma rápida e fácil, sem escrever programas. Um programador dos anos 80 faria assim:
X = 7 Y = 4 IF Y > 2 THEN Y= Y * 2 ELSE X= X * 2 PRINT X + Y
E, no seu TRS-80, veria isso:
Em um Apple II, por conta do BASIC não ter a palavra ELSE, teríamos que fazer um pouco diferente:
X = 7 Y = 4 IF Y > 2 THEN Y= Y * 2 IF Y <= 2 THEN X= X * 2 PRINT X + Y
E veria algo como
Em forma de programa, no Apple II (e em qualquer caso em que precisássemos de um IF/THEN/ELSE multi-linhas), o jeito típico seria usar um GOTO:
10 X = 7 20 Y = 4 30 IF Y > 2 THEN Y= Y * 2 : GOTO 50 40 X= X * 2 50 PRINT X + Y
E teríamos isso:
Como a versão BASIC disse "15" e concordou com as versões Python e Lisp, eu me dou por satisfeito.
2) Quantas vezes esse programa imprime "hello"?
for i = 1 to 5 if i != 2, then for j = 1 to 9 print 'hello'
O programa ficaria assim:
10 FOR I = 1 TO 5 20 IF I <> 2 THEN FOR J = 1 TO 9: PRINT "hello": NEXT 30 NEXT
Sorte nossa o FOR J caber em uma linha.
Ainda assim, estamos com o mesmo problema que tivemos na nossa primeira tentativa (no original em Python) - que é nos obrigar a contar. Um jeito um pouco melhor ficaria assim:
10 FOR I = 1 TO 5 20 IF I <> 2 THEN FOR J = 1 TO 9: C = C + 1 : PRINT C, "hello": NEXT 30 NEXT
Alguém pode perguntar: "Qual é o valor inicial de C?". No BASIC daquele tempo variáveis do BASIC são tipadas pelo nome ("C" só pode representar um número real) e variáveis não inicializadas são zero ou, no caso de strings ("C$", por exemplo) strings vazias. Ou seja, assim que o programa começa a rodar, C é zero - e podemos contar com isso.
Se estiver incomodando muito, mas muito mesmo, digite "5 C = 0" e seja feliz. Não vai fazer mal.
Se fizermos isso no TRS-80, ficaremos com algo assim:
De novo, podemos ficar felizes. O programa em BASIC concorda com os outros - temos 36 (que é a resposta certa, afinal).
3) Quais números, entre 5 e 2675 são pares e divisíveis por 3?
Aqui estamos tão mal quanto o pessoal que fez com Java. Não dá pra fazer nada tão conciso quanto os exemplos em Python e em Clojure. BASIC não tem nenhuma das funcionalidades de geradores de listas que Ruby, Python, Clojure e qualquer linguagem moderna tem.
10 FOR I = 5 TO 2675 20 IF I / 3 = INT(I /3) AND I / 2 = INT(I / 2) THEN C = C + 1 30 NEXT 40 PRINT C
Quando rodar, o programa vai dizer que há 445 números assim.
4) Números bonitos
A pergunta nos apresenta Barbara e seus critérios para que números sejam ou não bonitos. Para ela, números são bonitos se contiverem um dígito 4 e não contiverem um dígito 9. Ao final, nos pergunta quais números entre 14063 e 24779, inclusive, são bonitos.
Esse fica mais complicado. O BASIC que vinha nesses computadores era limitado e não permitia que o programador definisse funções além do mais trivial (uma linha, sem condições). Vamos, por isso, usar uma técnica há muito esquecida (ou que deveria ter sido) que é passar informações por meio de variáveis globais. No caso dos nossos interpretadores, todas as variáveis são globais. Mesmo quem programa em assembly pode contar com a pilha nessas horas. Em BASIC, nem isso.
Você leu direito. Programar naquele tempo não era fácil.
O esqueleto do programa fica assim:
10 FOR I = 14063 TO 24779 20 GOSUB 10000 30 IF B = 1 THEN C = C + 1 40 NEXT 50 PRINT C 60 END
Mas ainda falta o trecho que começa em 10000 que, você deve ter adivinhado, diz se o I é um número bonito e devolve um 1 em B se ele for. O que você vai ver não é bonito para nenhum valor de I, portanto, prepare-se:
10000 REM Primeiro vemos se tem um 4 10010 I$ = STR$(I) 10020 B = 0 10030 FOR J = 1 TO LEN(I$) 10040 IF MID$(I$, J, 1) = "4" THEN B = 1 10050 NEXT 10060 IF B = 0 THEN RETURN 10070 REM Se tem um 4, procuramos um 9 10080 FOR J = 1 TO LEN(I$) 10090 IF MID$(I$, J, 1) = "9" THEN B = 0 10100 NEXT 10110 RETURN
Após um longo tempo, seu computador vai dizer que são 3047 números bonitos:
Nesse ponto, estou começando a achar que eu era mais inteligente nos anos 80. O malabarismo mental de lembrar que variável guarda o que e em que partes do programa essas coisas são alteradas é complicado. E é um bom exercício.
5) Os telefones
A última pergunta nos apresenta um país em que os números de telefone têm 6 dígitos. Números não podem ter dois dígitos consecutivos idênticos, porque isso é caído. A soma dos dígitos tem de ser par, porque isso é legal e o último dígito não pode ser igual ao primeiro, porque isso dá azar.
Vamos começar pelas sub-rotinas. A primeira, para ver se o número de telefone é caído:
10000 REM ve se N$ e caido. Se for, C = 1 10010 C = 0 10020 FOR J = 1 TO LEN(N$) -1 10030 IF MID$(N$, J, 1) = MID$(N$, J + 1, 1) THEN C = 1 10040 NEXT 10050 RETURN
Depois, vendo se ele dá azar:
11000 REM se N$ der azar, A = 1 10010 A = 0 10020 IF LEFT$(N$, 1) = RIGHT$(N$, 1) THEN A = 1 10030 RETURN
E, por fim, se ele é legal:
12000 REM se N$ for legal, L = 1 12010 L = 0: S = 0 12020 FOR J = 1 TO LEN(N$) 12030 S = S + VAL(MID$(N$, J, 1)) 12040 NEXT 12050 IF S / 2 = INT(S / 2) THEN L = 1 12060 RETURN
Vale a pena notar um padrão - como todas as variáveis são globais, é preciso tomar cuidado para que um pedaço do programa não estrague o outro. Nessas subrotinas meu habitual "FOR I" passou a ser "FOR J" para que meu loop da sub-rotina não estrague o loop principal. Eu podia também ter reutilizado o L como acumulador da soma e não estragar o S com isso.
O bloco principal
10 FOR I = 1 TO 200 20 READ N$ 30 GOSUB 10000 40 GOSUB 11000 50 GOSUB 12000 60 IF C = 0 AND A = 0 AND L = 1 THEN Q = Q + 1 70 NEXT 80 PRINT Q 90 END
Só temos um problema - de onde virão os 200 números? O comando READ, na linha 20, lê um valor de uma série de dados literais embutidos no código. Assim, adicionamos uma série de linhas a partir da linha 1000, cada uma com um comando DATA e uma série de strings. Montamos essa linha com um pouco de mágica de copy e paste e busca e troca.
Sim, eu poderia ter pulado os testes depois do primeiro eliminar o número, assim como ter começado com o teste da linha 12000, que é mais simples e rápido. Os algorítmos poderiam ser melhores também e eu poderia ter usado variáveis inteiras onde possível para que os programas rodassem mais rápido.
São 40 linhas com essa cara:
1000 REM Os numeros que precisamos filtrar 1010 DATA "214966", "215739", "220686", "225051", "225123" ... 1400 DATA "715315", "720200", "720202", "720568", "720576"
Quando rodamos o programa, temos a resposta esperada (já fizemos isso algumas vezes, afinal).
TRS-80 e Apple II concordam. 61 parece um bom número.
Isso era mesmo necessário?
Não. Eu não precisava ter feito isso nem mesmo uma vez. A primeira eu fiz porque achei as perguntas interessantes. Muitas e muitas vezes eu já entrevistei pessoas incapazes de escrever um programa de 5 linhas sem ajuda de um Google e, francamente, estou tentado a propor testes formais antes mesmo de um humano colocar os olhos em um currículo. O em Clojure foi motivado pelo contorcionismo sintático de expressar um algoritmo em um cruzamento de Ruby e JavaScript. Esse último veio pelo meu interesse em emuladores, pelo meu interesse em computadores antigos e por eu achar que aprender a programar com um computador de 8 bits é divertido. A simplicidade desses computadores facilita ao novato entender, direito, tudo o que acontece dentro deles nem. Não há compiladores cuidando de você e otimizando código ineficiente. Não há milhares de linhas de código de um kernel distribuindo tempo do processador por dezenas de processos. Tudo é bem simples e acontece do jeito que você mandou.
E, na hora de aprender, simples é bom.
Acho que o próximo precisa ser em FORTH. Vou começar a procurar um dialeto legal pra brincar.
E quanto a você?
Se você quiser brincar com um BASIC antigo, eu recomendo que comece lendo os manuais. Manuais do Apple II e do TRS-80 são fáceis de achar. Outra coisa: eu tomei cuidado de escrever esses programas de modo a eles serem portáteis - você pode usá-los em seu Prológica, Microdigital, ou Unitron. Deve poder rodá-los no seu Atari XL ou no seu Commodore 64. Não testei em todos.
Se você achar a brincadeira (é uma brincadeira - ninguém vai pagar vocë por saber programar um computador de 30 anos de idade) legal, compre um computador antigo e cuide dele. Limpe, conserte. Essas máquinas não vão durar para sempre e marcam o momento importante que essa tecnologia deu: quando deixou de ser aquela máquina que vivia fechada em uma sala e passou a ser aquela que tínhamos em casa. Como disse um especialista da Christies sobre o Apple I que foi leiloado esses dias, "é um pedaço de plástico verde que mudou nossas vidas".
Precisamos cuidar deles.
Mais uma coisa: não me escapou o fato de que esses dois BASICs que eu usei são produtos da Microsoft. Ela fazia coisas bem legais nos anos 70.
Hold different
This time, inspired by eddieplan9, one for all iPhone4 users who have experienced reception problems due to the way they hold their phones.
I really want to like Apple
I do. Seriously.
I loved my Apple IIs passionately. I love my Macintosh collection (from SE to Bondi-blue iMac). While not being a heavy Mac user, I keep a Mac on my desk as a second computer to my main computer (a netbook running Linux because I like carrying it around, because it's cheap and because I like Linux better than OSX for work), I still like Apple's products and recommend them when I feel it's appropriate. For instance, when my then fiancée wanted a new notebook, I convinced her she would be happy with a Macbook, and so she is. She even married me after that.
But I don't think Apple loves me. Or, by the way, any of their lovers.
One cable
Some time ago, I bough an iPod Touch. I was about to build an application for it, and, so, I needed one. I quickly fell in love with it as a media player as well as a über-PDA with web access. At the time, there was no iPhone SDK and the project was canned, but, by then, I already liked the iPod pretty much. The cancellation was also fine by me because, while Objective-C is a much better idea than C++, being better than C++ doesn't say that much. Still, I kept the iPod and soon listening to audio podcasts became part of my morning routine as much as watching the video ones became a lifesaver when our weekly air-traffic-control meltdown left me stranded on some small airport with no wireless access.
So, it was only natural for me to buy a cable to hook it up to my TV.
Despite the size of Brazil's consumer market, there are no Apple Stores here. Many people attribute this, along with what appears to be active sabotage by Apple, to its deep hatred for the only country that had a company that dared to attempt to produce a Mac-compatible computer (that's one ugly story). fortunately, there are some companies who decided they would try to cater to the unrequited love Apple turned its corporate back on and build the stores themselves. So, I went to the next best thing: the local "a2you" chain of stores and got myself a composite cable.
It worked beautifully.
I mean... The user interface is really horrid for playing videos and watching them from the couch. Unless your videos happen to be long enough, you will have to play each one from the iPod itself. There appears to be no video playlist thingie anywhere on the iPod. Audio went just fine, but not video.
Then I got involved in another project involving podcasts.
So, the time came for me to update the iPod's software. It is a first-gen iPod Touch that came with 1.0 software (update to 1.1.5, if I remember correctly), so I created an account on the iTunes store and downloaded the software. OS 3 is a nice improvement over 2.x and is a huge improvement over the 1.x I was running. It's not as snappy as the 1.x (it really seems built for the "S" iPhones) but it's bearable.
But it had one unwelcome side effect: my composite cable no longer works.
DRM (as in Digital Restrictions Management)
I mean, it does, then it doesn't.
It's not a cable problem. It worked flawlessly with 1.x. It still works on 1.x units.
The problem seems to be the software. It, apparently checks if the cable is made by Apple and then, in the middle of the playback (just demonstrating the cable works perfectly), it freezes the video and spits a "This accessory is not compatible with this iPod" or something like it. Well... It's a cable! How incompatible with something can a cable be? Is it a DRM issue? Is it built-in for future HD iPods to render cables that do not provide copy-protection useless? It's an analog cable! The lowest-quality one! Who would consider using it for piracy? And to pirate what? TED lectures? Episodes of Cranky Geeks? Conference presentations? It's much easier just to rip the DRM off the original file (and there are many automated tools for that).
This and the recent Amazon Kindle problem - Jeff Bezos can apologize as much as he wants, the ability to remove content you already purchased is still there and can be abused anytime Amazon feels like breaking promises - got me really weary of DRM. Even for single-purpose devices I buy for a single purpose, a software update can break the hardware I already own, even something as simple as a cable. It's not my device if Apple can do things like this with it. I may possess it, but, in reality, it belongs to, and obeys, Apple. If Apple decides to brick it, bricked it will be.
Then, there is also the shady process Apple uses to approve applications. It's not in the best interest of their customers to Apple to have a stranglehold on applications for the platform. I understand they want quality control, but customers may want to circumvent that control for their own uses. Or because they have different ideas about quality.
Defective by Industrial Design
You know... The Apple tablet the Financial Times seems to have confirmed today looks sweet. I would love to play with it. I would even consider buying one, but I won't. The point is, as much as I like Apple's attention to detail, its outstanding industrial design, I can't justify buying a product that's not really mine. Call me spoiled, but using stuff like Linux made me feel I am really in control. The netbook is mine, and nobody will make my computer do something I don't approve. If it ceases to work, it will be my fault.
So, if Apple would please unbreak their software ecosystem in a way it doesn't actively try to screw its customers, I may consider buying a tablet (or an iPhone or even another iPod touch for the day this one dies).
But recovering the trust I had on their attention to their customers will take some time.
An Interesting Update
I must confess I did not search the web (or Apple's forums) before either upgrading or buying the cable (that one was not my fault, as it worked by the time it was purchased and stayed that way until a couple days ago), but, because someone reminded me I could do that, I googled for it and found this:
http://discussions.apple.com/thread.jspa?threadID=2046835
So, at least now I know I am not alone with my cable problems. And, finally, I am convinced it's not a cable problem and not a connector problem, but a vendor selection problem.
Also, for some time, a lively discussion about it, and, perhaps, my scientific method, happened here:
http://news.ycombinator.com/item?id=726922
Hacker News (the site at ycombinator.com) is considered a Troll-free zone and I wish it to remain so. Please, if you want to participate there, mind your manners and read and obey the guidelines.
An obvious answer and why I still won't switch to Mac
There was one nagging thought that was lurking in my brain while I wrote my last post that finally condensed into a fully-formed idea: you should use the platform that has everything the platform you develop for has.