Você já quis inserir um elemento num vetor e obter o vetor numa tacada só? Já quis que o vetor.push(el) do JavaScript retornasse o vetor em vez de o tamanho?

Se já quis e ficou frustrado, saiba que tem como fazer! Existe um operador que serve exatamente para esses momentos:

vetor.push(el), vetor

Como assim, André? Que operador? Que raio foi isso aí?

Sim! Esse é o mágico e desconhecido operador , (vírgula) da linguagem C, herdado por JavaScript. Ele avalia cada expressão e dá como resposta o resultado da última:

// Os parênteses são necessários aqui porque 
// o operador vírgula tem uma precedência baixíssima

const resultado = (expr0, expr1, expr2); // resultado = expr2

Já fazia uns 15 anos que eu me perguntava para quê ele servia na prática, e, hoje, finalmente, encontrei a resposta! O operador vírgula serve quando você está num ponto do código que precisa de uma expressão e você quer causar um efeito colateral.

Você talvez já o tenha visto no incremento de um for com mais de uma variável: for (int i = 0, j = 0; i < 10; ++i, ++j). Nesta resposta do StackOverflow tem um outro exemplo de uso, para fazer leitura de dados. É tipo quando a gente faz assim em Java:

while ((byteLido = inputStream.read()) != -1) {
// ...
}

O while pede uma expressão booleana. Entregamos uma para ele, mas, antes disso, damos uma mexidinha no estado do sistema!

Se você já fez programação funcional com vetores em JavaScript, provavelmente já sentiu falta das funções cons e conj do Clojure, ou : do Haskell.

Dá para simular essas funções assim: [elemento].concat(vetor) ou vetor.concat([elemento]). Meio feião e cria um vetor bobo para jogar fora pouco depois. Vamos usar isso para fazer uma implementação recursiva de cauda da função zip:

function zip(v1, v2) {
const loop = (ret, v1, v2) =>
v1.length === 0 || v2.length === 0 ? ret : loop(
ret.concat([[v1[0], v2[0]]]), v1.slice(1), v2.slice(1));
return loop([], v1, v2);
}

Ficou razoável… Mas que colchetaiada!

E com push, como ficaria? Normalmente, evitamos alterar variáveis. No caso, ret é um vetor novo, então não tem muito problema. Além disso, evitar criar vetores extras pode ser uma boa aqui porque estamos em JavaScript, que não tem as otimizações comuns em linguagens funcionais. Ficaria assim:

function zip(v1, v2) {
const ret = [];
function loop(v1, v2) {
if (v1.length === 0 || v2.length === 0) {
return ret;
} else {
ret.push([v1[0], v2[0]]);
return loop(v1.slice(1), v2.slice(1));
}
}
return loop(v1, v2);
}

Eita… Um monte de ; {} return! Nem parece mais programação funcional. loop era só uma funçãozinha de uma expressão só, agora virou um trambolho, devido a termos que mudar de ? para if/else.

E com se usarmos o operador vírgula?

function zip(v1, v2) {
const loop = (ret, v1, v2) =>
v1.length === 0 || v2.length === 0 ? ret : loop(
(ret.push([v1[0], v2[0]]), ret), v1.slice(1),v2.slice(1));
return loop([], v1, v2);
}

Opa… Aí, sim, hein! push só é executado quando os tamanhos dos vetores são diferentes de zero, e o resultado da expressão entre parênteses será ret. Causamos um efeito colateral maroto e inofensivo.

Simples ou complicado, claro ou obscuro, o fato é que agora está explicada a existência desse misterioso operador!

Espantoso? Nem tanto… O comando do em Clojure faz a mesma coisa!

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s