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. 🙂

Anúncios

3 opiniões sobre “Mais um pouco de J

  1. Muito interessante heim !
    Legal o post.. é tri ver várias linguagens assim nascendo, e de todo o trabalho feito em tudo, geralmente se aproveita muita coisa…

  2. Valeu o comentário Sérgio. J foi desenvolvida no início dos anos 90 (mais velha que Python e Ruby). Ela é derivada de APL que, por sua vez, é da década de 60. Ainda estou longe de ‘pensar’ na linguagem, mas chego lá.

  3. Pingback: Projeto Euler – Problema #1 em J « Fotomix’s Weblog

Os comentários estão desativados.