REBOLution

Quando se apresenta uma nova linguagem (apesar de REBOL não ser uma nova linguage no aspecto desenvolvimento já que teve sua primeira versão em 1997) é tendência de muitos é ficar comparando com a sua linguagem preferida. Não faça isso. Essa negócio de “Dãããã, a minha linguagem do coração também faz isso então não vou perder o meu tempo olhando.” é coisa de n00b. Mas…

Como a esmagadora maioria das linguagens, REBOL (Relative Expression Based Object Language) foi influenciada por outras linguagens, no caso por List, Forth, Logo e Self e projetada por Carl Sassenrath (se você não conhece, importante ler um pouco sobre ele). A versão 3 foi liberada recentemente (12/12/12) sob licença aberta (Apache) apesar de ser conhecida desde 2008. É possível utilizar a versão 2 gratuitamente porém ela é proprietária. Pretendo me deter a versão 3 e a expectativa é que possuas as mesmas funcionalidades da versão 2 mais as melhorias. O porte para Android deve ocorrer em um futuro próximo (ou distante, não é possível prever por enquanto).

No momento é necessario clonar o repositório e compilar mas, assim que as coisas estiverem mais estáveis, será possível baixar na página principal da linguagem. Por enquanto é possível baixar a última versão compilada antes da abertura do código.

Por suas influências, a linguagem mistura código e dados de uma forma transparente, é interpretada, reflexiva e, talvez, a parte mais interessante, é a facilidade de criar dialetos (DSLs).

É interessante salientar que a linguagem não faz distinção entre maiúsculas e minúsculas (foreach = FOREACH), o espaço é necessário (1 + 1 = 2 ; 2+3 = erro) e o resto fica por sua conta. Como ainda está em desenvolvimento, a linguagem ainda possui alguns probleminhas e o ambiente integrado (REPL) também. Por exemplo, não é possível digitar expressões em mais de uma linha ou caracteres especiais (UTF-8). Mas você pode criar um script em um processador qualquer e executá-lo sem problemas.

Estando no REPL, se desejarmos maiores informações sobre ‘foreach‘, basta digitar:

>> help foreach
USAGE:
 FOREACH 'word data body
DESCRIPTION:
 Evaluates a block for each value(s) in a series.
 FOREACH is a native value.
ARGUMENTS:
 word -- Word or block of words to set each time (local) (word! block!)
 data -- The series to traverse (series! any-object! map! none!)
 body -- Block to evaluate each time (block!)

Aproveitando, alguns exemplos do foreach (repare a ausência de delimitadores na série):

foreach x [1 2 3 4] [
print x
]
1
2
3
4
bloco: [print x] 
foreach x [1 2 3 4 5] bloco
1
2
3
4
5
Foreach [X Y] [1 2 3 4 6] [Print [x y]]
1 2
3 4
6 none

Nos dias atuais pode nem ser tanta novidade, mas na época, acredito que não existia uma linguagem com tamanha transparência para trabalhar em rede. Para ler um arquivo de um local qualquer bastaria escrever “read http://www.rebol.com” e para executar localmente um script que estivesse em outra máquina bastava “do ftp://servidor/script.r“. Diversas linguagens permitem tarefas semelhantes mas, a grande maioria, necessita de alguma biblioteca externa ou algum include/use.

Outra característica é a grande quantidade de tipos de dados existentes. Nativamente possui string, inteiros, data, hora, monetário, pares, tupla, etc.. Por exemplo 10:30 + 0:40 = 11:10. Isto facilita muitas coisas, principalmente na criação de DSLs.

>> 3x4 + 1
== 4x5
>> 3x4 + 1x2
== 4x6
>> 127.0.0.1 + 0.0.25.1
== 127.0.25.2

A parte para avaliar e trabalhar com expressoes é onde REBOL (por enquanto estou escrevendo tudo em maiúsculas mas já está em discussão se continuará assim ou não) se sai muito bem. Quem conhece expressões regulares vai sentir muita facilidade. Um avaliador simples para verificar a validade de um CPF, onde ele deve conter 3 dígitos, um ponto, 3 dígitos, um ponto, 3 dígitos pode seguir um hifem ou uma barra e mais 2 dígitos pode ser simplesmente resolvido da seguinte forma:

num: charset "0123456789"
regra_cpf: [3 num "." 3 num "." 3 num ["-" | "/"] 2 num]
cpf?: func [cpf] [parse cpf regra_cpf]

>> cpf? "111.222.333-88"
== true
>> cpf? "111.222.333/88"
== true
>> cpf? "111.222.333/8"
== false
>> cpf? "111-222-333/88"
== false

Bem, isso é o basiquinho. É possível, por exemplo, avaliar um texto do tipo:

Venda 2 calculadoras (preço antigo) por R$20
Venda 3 calculadoras (2 pelo preço antigo e 1 pelo preço novo) por R$40

E obter como resposta: Foram vendidas 5 calculadoras por R$60.

Mas fica para a próxima.

Editado: Alguns exemplos em REBOL 2 que coloquei há algum tempo.

Anúncios

Mais um pouco de J

Vendo no site Programando Python no RS. Fui dar uma olhadinha no Projeto Euler. Pensei cá com meus botões: “Poderia brincar um pouco com J.”. E resolvi!

Alguém poderia perguntar: Por que J? Sendo uma linguagem diferente das que eu conheço (provavelmente a grande maioria da área), tira a gente daquele conforto tradicional e obriga a pensar de forma diferente. Ver um problema de ângulos diferentes é muito bom para o programador. Trabalhar só com um martelo limita um pouco a criatividade.

J é distribuído sob a licença GPL3 (parece que começou nesse ano). Você pode baixar o ambiente do site JSoftware. Pode ser melhor a versão J602 (eu estou com a versão 701). Baixe instale e divirta-se. Por enquanto vamos ignorar a nomenclatura oficial e não seguiremos estritamente com os termos verbo, advérbio, gerúndio, sentença (serão introduzidas aos poucos durante o ou os artigos). Pode ser interessante você olhar os dois artigo que escrevi sobre J no blog. O primeiro */>:i.5 (j é Russo) e segundo Utilizando J em imagens.

Mas vamos ao que interessa. Achei que o problema 6 seria bom para o início. É algo bem simples, não vai tirar pedaço de ninguém e pode dar uma visão inicial legal da linguagem.

Dado uma sequência de números naturais, calcular a diferença entre o quadrado da soma e a soma dos quadrados. Considerando a sequência 1, 2, 3, … 10. Basicamente uma PA de razão 1 onde o primeiro termo é 1. Mas não vamos utilizar as fórmulas das PAs para resolver o nosso problema.

a) (1 + 2 + ... + 10)² = 55² = 3025

b) 1² + 2² + ... + 10² = 385

a - b = 3025 − 385 = 2640

Calcular para os 100 primeiros números naturais.

O primeiro elementos que temos é uma sequência. Em J utilizamos i.n que irá retornar os n primeiros inteiros não negativos. Para os cinco primeiros temos:

i.5 => 0 1 2 3 4

Apesar do zero não fazer diferença no nosso problema, não é o que queremos. Mas bastaria somar um a cada elemento que teremos o vetor desejado. Podemos usar a expressão ‘1 + i.5‘ para que o nosso vetor inicie em 1 e não em zero. Aqui vale um parênteses já que a avaliação das expressões é feita da direita para a esquerda (LIFO), se fosse escrito ‘i.5 + 1‘ retornaria um vetor incorreto pois seria avaliada como ‘i.6‘. Poderíamos escrever ‘(i.5)+1‘ forçando a avaliação entre os parênteses primeiro. Mas vamos utilizar o verbo incremento ‘>:‘ que, como o nome diz, incrementa os valores.

>: i.5 => 1 2 3 4 5

Para o primeiro caso, temos que somar os elementos do vetor e elevar o resultado ao quadrado. Usaremos o adverbio insert ‘/‘ para somar ‘+‘ os elementos.

+/ 1 2 3 4 5 => 1 + 2 + 3 + 4 + 5

onde

+/ >: i.5 => 15

agora é só pegar o resultado e elevar ao quadrado:

(+/ >: i.5) ^ 2

Parando aqui temos como resolver a parte a) do problema. Mas que tal uma solução mais idiomática ou, pelo menos, uma outra forma de resolver e aprender mais um pouquinho? Tem o verbo Reflexive ‘~‘ e ele serve apenas para duplicar o operador à esquerda de um outro verbo. Então ‘+ ~ 2‘ equivale a ‘2 + 2’. Como elevar um número ao quadrado é apenas multiplicar ele por ele mesmo, temos que = 3 * 3. Então podemos escrever ‘* ~ 3‘. A nossa expressão anterior ficaria:

* ~ +/ >: i.5

Como a avaliação é feita da esquerda para a direita e, com o conhecimento que você já adquiriu, a leitura da expressão é até fácil. Gera um vetor com 5 inteiros não negativos i.5 (0 1 2 3 4), incrementamos o vetor >: (1 2 3 4 5), inserimos o verbo + entre os elementos dos vetores para conseguirmos a soma +/ (1 + 2 + 3 + 4 + 5) que nos dá como resultado (15). Refletimos o valor obtido a esquerda da multiplicação (*~) que resulta em (15 * 15) e temos como resultado 255 (15² ou 15 * 15).

A parte b) do problema é semelhante a parte a). Só termos que somar o quadrado de cada elemento antes da soma dos elementos do vetor. Uma das soluções seria: +/ (>: i.5)^2. A outra solução seria: +/ * ~ >: i.5 Em ambas apenas deslocamos o insert plus.

Só para testar com 10 e ver se o resultado confere com os exemplos do problema:

* ~ +/ >: i.10 = 3025

+/ * ~ >: i.10 = 385

Podemos conferir a diferença por:

(* ~ +/ >: i.10) - (+/ * ~ >: i.10) = 2640

Mas ainda não está pronto. Ele deve ficar mais flexível e permitir o cálculo que desejarmos. Usamos apenas as primitivas da linguagem. Vamos definir um verbopara resolver o nosso problema (pense como sendo uma função em outra linguagem):

p6 =. verb define

(* ~ +/ >: i.y) - (+/ * ~ >: i.y)

ou, (ver documentação como definir verbos)

p6 =. 3 : '(* ~ +/ >: i.y) - (+/ * ~ >: i.y)'

Mas o que é aquele i.y? De que lugar ele veio? Bem, em J temos as definições chamadas ‘Monads’ e ‘Dyads’. No primeiro caso, o verbo trabalha com apenas um valor. No caso do incremento, por exemplo ‘>: 5’ retorna ‘6’ e tem como argumento apenas um valor. No caso da exponenciação, o verbo requer dois valores como por exemplo ‘2^3’ que retorna ‘8’. Então fica ‘x verbo y’. Assim, x é o ‘argumento’ da esquerda e y o da direita.

Agora é só utilizar:

p6 10

2640

p6 100

25164150

Se você achar o verbo muito longo, é possível eliminar alguns espaços.

p6 =.3 : '(*~+/>:i.y)-(+/*~>:i.y)'

O interessante é que, para quem não conhece a linguagem, mesmo um trecho pequeno e básico como esse é praticamente ilegível. Não é necessário compilar, obfuscar ou utilizar qualquer meio para dificultar a leitura. 🙂