Dicas para jogar golfe no ECMAScript 6 e acima


88

Isso é semelhante a outras "Dicas para jogar golfe em <...>", mas direcionadas especificamente aos novos recursos do JavaScript apresentados no ECMAScript 6 e acima.

JavaScript inerentemente é uma língua muito detalhado, function(){}, .forEach(), convertendo string para array, array como objeto a matriz, etc, etc são super incha e não é saudável para o Golfe.

O ES6 +, por outro lado, possui alguns recursos super úteis e presença reduzida. x=>y, [...x]etc são apenas alguns exemplos.

Publique alguns truques legais que podem ajudar a eliminar esses poucos bytes extras do seu código.

NOTA: Os truques para o ES5 já estão disponíveis em Dicas para jogar golfe em JavaScript ; as respostas a este segmento devem se concentrar em truques disponíveis apenas no ES6 e em outras versões futuras do ES.

No entanto, esse segmento também é para usuários que atualmente jogam golfe usando os recursos do ES5. As respostas também podem conter dicas para ajudá-los a entender e mapear os recursos do ES6 de acordo com o estilo de codificação do ES5.

Respostas:


42

Operador de propagação ...

O operador de spread transforma um valor de matriz em uma lista separada por vírgula.

Caso de uso 1:

Usar diretamente uma matriz em que uma função espera uma lista

list=[1,2,3]
x=Math.min(...list)
list=[10,20], a.push(...list) // similar to concat()

Caso de uso 2:

Crie um literal de matriz a partir de uma iterável (geralmente uma string)

[...'buzzfizz'] // -> same as .split('')

Caso de uso 3:

Declarar um número variável de argumentos para uma função

F=(...x) => x.map(v => v+1)
// example: F(1,2,3) == [2,3,4]

Veja doc mozilla


3
Agora eu tenho um voto negativo aqui. Obviamente alguém notou algo de terrivelmente errado nesta dica, sendo muito tímido para deixar um comentário e explicar o que ...
edc65

Parece tudo bem. Talvez tenha sido a falta de ponto e vírgula? ;) (Btw, você também pode usá-lo como parâmetros de descanso, como splats em Ruby)
gcampbell

Você poderia acrescentar que ele também tem um caso de uso em assinaturas de função :)
Felix Dombek

Misclick não queria downvote
Stan Strum

@StanStrum acontece. Eu vou fazer uma pequena atualização para este post para que você possa, eventualmente, alterar seu voto (ou você já fez?)
edc65

21

Truques aprendidos aqui desde que entrei

Minha principal linguagem de programação é JS e principalmente ES6. Desde que entrei neste site há uma semana, aprendi muitos truques úteis com colegas. Estou combinando alguns deles aqui. Todos os créditos para a comunidade.

Funções de seta e loops

Todos sabemos que as funções de seta economizam muitos bytes

function A(){do something} // from this
A=a=>do something // to this

Mas você tem que ter em mente algumas coisas

  • Tente agrupar várias declarações usando ,ie (a=b,a.map(d))- Aqui, o valor retornado é a última expressãoa.map(d)
  • se a sua do somethingparte for mais de uma declaração, você precisará adicionar os {}colchetes ao redor .
  • Se houver {}colchetes, você precisará adicionar uma declaração de retorno explícita.

O acima exposto é válido muitas vezes quando você tem loops envolvidos. Então, algo como:

u=n=>{for(s=[,1,1],r=[i=1,l=2];c=l<n;i+=!c?s[r[l++]=i]=1:1)for(j of r)c-=j<i/2&s[i-j];return n>1?r:[1]}

Aqui estou desperdiçando pelo menos 9 caracteres devido ao retorno. Isso pode ser otimizado.

  • Tente evitar loops. Use .mapou .everyou em .somevez disso. Observe que se você deseja alterar a mesma matriz para a qual está mapeando, ela falhará.
  • Enrole o loop em uma função de seta de fechamento, convertendo a função de seta principal como instrução única.

Portanto, o acima se torna:

u=n=>(s=>{for(r=[i=1,l=2];c=l<n;i+=!c?s[r[l++]=i]=1:1)for(j of r)c-=j<i/2&s[i-j]})([,1,1])|n>1?r:[1]

caracteres removidos: {}return

caracteres adicionados: (){}>|

Observe como eu chamo o método de fechamento, que preenche corretamente a variável ne, como o método de fechamento não está retornando nada (ou seja, retornando undefined), eu bit a bit ou retornamos a matriz nem uma única instrução da função de seta externau

Vírgulas e ponto e vírgula

Evite-os do que nunca,

Se você estiver declarando variáveis ​​em um loop, ou como mencionado na seção anterior, usando ,instruções separadas para ter funções de seta de instrução única, poderá usar alguns truques bem bacanas para evitá-las ,ou ;raspar os últimos bytes.

Considere este código:

r=v=>Math.random()*100|0;n=r();m=r();D=v=>A(n-x)+A(m-y);d=0;do{g();l=d;d=D();....

Aqui, estou chamando muitos métodos para inicializar muitas variáveis. Cada inicialização está usando um ,ou ;. Isso pode ser reescrito como:

r=v=>Math.random()*100|0;n=r(m=r(d=0));D=v=>A(n-x)+A(m-y);do{d=D(l=d,g());....

Observe como uso o fato de o método não incomodar a variável passada a ele e o uso para depurar 3 bytes.

Diversos

.search ao invés de .indexOf

Ambos dão o mesmo resultado, mas searché mais curto. Embora a pesquisa espere uma expressão regular, use-a com sabedoria.

`Strings de modelo`

Isso é super útil quando você precisa concatenar uma ou mais partes da corda com base em determinadas condições.

Pegue o exemplo a seguir para gerar uma quine em JS

(f=x=>alert("(f="+f+")()"))()

vs.

(f=x=>alert(`(f=${f})()`))()

Em uma string de modelo, que é uma string dentro de duas aspas (`), qualquer coisa dentro de a ${ }é tratada como um código e avaliada para inserir a resposta resultante na string.

Vou postar mais alguns truques mais tarde. Feliz golfe!


1
.search é mais curto, use-o sempre que possível! mas não é o mesmo que .indexOf. .search quer um regexp, não uma string. Tente'abc'.search('.')
edc65

@ edc65 Atualizado!
Optimizer

Você pode modificar a matriz original com os métodos da instância. O segundo é o índice atual e o terceiro é a matriz que está sendo iterada.
Isiah Meadows

8
"Entrou no site há uma semana" - 21,4k rep ...
GamrCorps

2
Além disso .map, a recursão é outra técnica que às vezes pode ajudá-lo a transformar um forloop em uma expressão.
Neil

20

Usando atalhos de propriedades

As abreviações de propriedades permitem definir variáveis ​​para os valores de uma matriz:

a=r[0];b=r[1] // ES5
[a,b]=r       // ES6 - 6 bytes saved

Isso também pode ser usado como:

a=r[0],b=r[2] // ES5
[a,,b]=r      // ES6 - 5 bytes saved

Você pode até usar isso para reverter variáveis:

c=a,a=b,b=c // ES5 - uses extra variable
[b,a]=[a,b] // ES6 - not shorter, but more flexible

Você também pode usar isso para reduzir slice()funções.

z = [1, 2, 3, 4, 5];

a=z.slice(1) // a = [2,3,4,5]; ES5
[,...a]=z    // a = [2,3,4,5]; ES6

Conversões básicas

O ES6 fornece uma maneira muito mais curta de converter o formulário Base-2 (binário) e Base-8 (octal) em decimal:

0b111110111 // == 503
0o767       // == 503

+pode ser usado para converter uma seqüência de caracteres binária, octal ou hexadecimal em um número decimal. Você pode usar 0b, 0oe 0x, por binário, octal e hexadecimal, respectivamente .:

parseInt(v,2) // ES5
+('0b'+v)     // ES6 - 4 bytes saved; use '0o' for octal and '0x' for hex
'0b'+v-0      // Shorter, but may not work in all cases
              // You can adapt this your case for better results

Se você estiver usando isso> 7 vezes, será mais curto usá parseInt-lo e renomeá-lo:

(p=parseInt)(v,2)

Agora ppode ser usado parseInt, economizando muitos bytes a longo prazo.


O truque de conversão de base é bom, mas é mais provável que o número de conversão esteja na forma de uma variável em vez de literal, nesse caso, torna-se muito mais longo.
Optimizer

1
'0x'+v-0é ainda mais curto, mas pode não funcionar tão bem em alguns cenários.
ETHproductions

1
A propósito, 0767(ES5) é mais curto que a 0o767notação (ES6).
Camilo Martin

@CamiloMartin 0767é uma extensão não padrão e é explicitamente proibida no modo estrito.
Oriol 20/01

1
O modo estrito do @Oriol era um meme ruim. Isso não ajudou no desempenho, realmente não forçou você a escrever um bom código e nunca se tornaria o padrão de qualquer maneira. 0literais octais pré-prefixados não estão indo a lugar algum e são tão válidos quanto ecmascript 0o.
Camilo Martin

19

Usando modelos de string com funções

Quando você tem uma função com uma string como argumento. Você pode omitir o ()se você não tiver nenhuma expressão:

join`` // Works
join`foobar` // Works
join`${5}` // Doesn't work 

9
Esteja avisado, isso realmente passa uma matriz. fun`string` é o mesmo que fun(["string"])não fun("string"). Isso é bom para funções que convertem para string, como alert, mas para outras, isso pode causar problemas. Para obter mais informações, consulte o artigo MDN
Cyoce

5
Breve coisa de referência: fun`foo${1}bar${2}bazé equivalente a chamarfun(["foo","bar","baz"],1,2)
Cyoce

14

Compreensões de matriz (Firefox 30-57)

Nota: as compreensões de array nunca foram padronizadas e tornaram-se obsoletas com o Firefox 58. Use por sua própria conta e risco.


Originalmente, a especificação do ECMAScript 7 continha vários novos recursos baseados em array. Embora a maioria deles não tenha chegado à versão finalizada, o Firefox suporta (possivelmente) o maior desses recursos: nova sintaxe sofisticada que pode substituir .filtere .mapcom a for(a of b)sintaxe. Aqui está um exemplo:

b.filter(a=>/\s/.test(a)).map(a=>a.length)
[for(a of b)if(/\s/.test(a))a.length]

Como você pode ver, as duas linhas não são tão diferentes, exceto a segunda que não contém as palavras-chave volumosas e as funções de seta. Mas isso explica apenas o pedido .filter().map(); o que acontece se você tiver .map().filter()? Isso depende da situação:

b.map(a=>a[0]).filter(a=>a<'['&&a>'@')
[for(a of b)if(a<'['&&a>'@')a[0]]

b.map(a=>c.indexOf(a)).filter(a=>a>-1)
[for(a of b)if((d=c.indexOf(a))>-1)d]

b.map(a=>a.toString(2)).filter(a=>/01/.test(a))
[for(a of b)if(/01/.test(c=a.toString(2)))c]

Ou que se você quer quer .map ou .filter ? Bem, geralmente resulta menos OK:

b.map(a=>a.toString(2))
[for(a of b)a.toString(2)]

b.filter(a=>a%3&&a%5)
[for(a of b)if(a%3&&a%5)a]

Portanto, meu conselho é usar compreensões matriz onde quer que você costuma usar .map e .filter , mas não apenas um ou o outro.

Compreensões de string

Uma coisa boa sobre as compreensões do ES7 é que, diferentemente de funções específicas de matrizes como .mape .filter, elas podem ser usadas em qualquer objeto iterável, não apenas em matrizes. Isso é especialmente útil ao lidar com strings. Por exemplo, se você deseja executar cada caractere cem uma string através de c.charCodeAt():

x=>[...x].map(c=>c.charCodeAt())
x=>[for(c of x)c.charCodeAt()]

São dois bytes salvos em uma escala bastante pequena. E se você quiser filtrar certos caracteres em uma string? Por exemplo, este mantém apenas letras maiúsculas:

x=>[...x].filter(c=>c<'['&&c>'@')
x=>[for(c of x)if(c<'['&&c>'@')c]

Hmm, isso não é mais curto. Mas se combinarmos os dois:

x=>[...x].filter(c=>c<'['&&c>'@').map(c=>c.charCodeAt())
x=>[for(c of x)if(c<'['&&c>'@')c.charCodeAt()]

Uau, um total de 10 bytes salvos!

Outra vantagem da compreensão de strings é que strings codificados economizam um byte extra, pois você pode omitir o espaço após of:

x=>[...'[](){}<>'].map(c=>x.split(c).length-1)
x=>[for(c of'[](){}<>')x.split(c).length-1]

x=>[...'[](){}<>'].filter(c=>x.split(c).length>3)
x=>[for(c of'[](){}<>')if(x.split(c).length>3)c]

Indexação

As compreensões de matriz tornam um pouco mais difícil obter o índice atual na string / array, mas isso pode ser feito:

a.map((x,i)=>x+i).filter ((x,i)=>~i%2)
[for(x of(i=0,a))if(++i%2)x+i-1]

A principal coisa a ter cuidado é garantir que o índice seja incrementado toda vez, e não apenas quando uma condição for atendida.

Compreensões do gerador

As compreensões de gerador têm basicamente a mesma sintaxe que as compreensões de matriz; basta substituir os parênteses por parênteses:

x=>(for(c of x)if(c<'['&&c>'@')c.charCodeAt())

Isso cria um gerador que funciona da mesma maneira que uma matriz, mas essa é uma história para outra resposta.

Sumário

Basicamente, embora as compreensões sejam geralmente mais curtas .map().filter(), tudo se resume às especificidades da situação. É melhor tentar nos dois sentidos e ver qual funciona melhor.

PS Sinta-se livre para sugerir outra dica relacionada à compreensão ou uma maneira de melhorar essa resposta!


Aqui está um truque para os intervalos que salvarão mais alguns caracteres: (x,y)=>[...Array(y-x)].map(a=>x++)
#

2
Você pode cortar outros 11 bytes para fazer uma escala de 0 a x:x=>[...Array(x).keys()]
Mwr247

Última para a compreensão: n=>[for(x of Array(n).keys())if(/1/.test(x))x](salva 7 bytes) #
Mwr247 22/15/15

@ Mwr247 Na verdade, agora posso ver que os intervalos geralmente não são tão curtos com compreensões quanto com outros recursos agradáveis ​​do ES6. Em vez disso, adicionarei uma seção sobre strings e deixarei você lidar com intervalos.
ETHproductions

Vale a pena notar que as compreensões de matriz foram descontinuadas e removidas de todas as versões recentes do javascript. Veja os documentos MDN sobre o assunto.
Keefer Rourke

13

As expressões de função no ES6 usam a notação de seta e ajudam muito a salvar bytes se comparados com a versão do ES5:

f=function(x,y){return x+y}
f=(x,y)=>x+y

Se sua função tiver apenas um parâmetro, você poderá omitir os parênteses para salvar dois bytes:

f=x=>x+1

Se sua função não possui parâmetros, declare-a como se tivesse uma para salvar um byte:

f=()=>"something"
f=x=>"something"

Cuidado: as funções de seta não são exatamente iguais function () {}. As regras para thissão diferentes (e melhor IMO). Veja documentos


2
Mas quando você está golf-ing, você geralmente não se preocupam com thisetc.
Optimizer

1
Geralmente não, mas é uma ressalva, uma que você talvez nunca saiba quando surgir. Também é mais comum que lambdas não precisem de uma ligação local dessa função na produção.
Isiah Meadows

Além disso, se você quiser receber todos os seus argumentos, poderá usar o recurso de argumento "rest", por exemplo, f=(...x)=>x teria isso f(1,2,3) => [1,2,3].
Conor O'Brien

1
Aqui vai uma dica específica para este site: se você está respondendo com uma função que toma a forma (x,y)=>...que você pode salvar um byte com currying , substituindo-ox=>y=>...
Cyoce

12

O uso evalde funções de seta com várias instruções e umreturn

Um dos truques mais ridículos que já encontrei ...

Imagine uma função simples de seta que precise de várias instruções e a return.

a=>{for(o="",i=0;i<a;i++)o+=i;return o}

Uma função simples que aceita um único parâmetro a, que itera sobre todos os números inteiros [0, a)e os prende no final da cadeia de saída o, que é retornada. Por exemplo, chamar isso 4como o parâmetro renderia 0123.

Observe que essa função de seta teve que ser colocada entre chaves {}e ter uma return ono final.

Essa primeira tentativa pesa 39 bytes .

Não é ruim, mas usando eval, podemos melhorar isso.

a=>eval('for(o="",i=0;i<a;i++)o+=i;o')

Essa função removeu os colchetes e a instrução de retorno envolvendo o código em evale simplesmente fazendo a última instrução na evalavaliação para o. Isso faz com que o evalretorno o, que por sua vez faz com que a função retorne o, já que agora é uma única instrução.

Essa tentativa aprimorada pesa 38 bytes , economizando um byte do original.

Mas espere, tem mais! As declarações Eval retornam qualquer que seja sua última declaração avaliada. Nesse caso, o+=iavalia como o, portanto, não precisamos do ;o! (Obrigado, edc65!)

a=>eval('for(o="",i=0;i<a;i++)o+=i')

Essa tentativa final pesa apenas 36 bytes - uma economia de 3 bytes em relação ao original!


Essa técnica pode ser estendida a qualquer caso geral em que uma função de seta precise retornar um valor e ter várias instruções (que não possam ser combinadas por outros meios)

b=>{statement1;statement2;return v}

torna-se

b=>eval('statement1;statement2;v')

salvando um byte.

Se statement2avalia para v, isso pode ser

b=>eval('statement1;statement2')

economizando um total de 3 bytes.


1
Eu acho que, apenas escrever uma função anônima pode ser ainda mais curto
Downgoat

@ vihan sim, ambas as funções podem ser anônimas para economizar 2 bytes cada. A economia de um byte ainda permanece.
jrich

1
Mas ainda melhor: eval retorna a última expressão evalued, assim você não precisa ;o- experimentá-lo:a=>eval('for(o="",i=0;i<a;i++)o+=i')
edc65

4
Mas seqüências de caracteres de modelo!
Conor O'Brien

1
@ CᴏɴᴏʀO'Bʀɪᴇɴ Gostaria de explicar como as strings de modelos funcionariam aqui usando a função de exemplo como contexto?
5306 WallyWest

10

Preferir novas linhas de sequência de modelo sobre "\ n"

Isso começará a render até mesmo um único caractere de nova linha no seu código. Um caso de uso pode ser:

(16 bytes)

array.join("\n")

(15 bytes)

array.join(`
`)

Atualização: Você pode até deixar de fora as chaves devido a seqüências de caracteres de modelo marcadas (obrigado, edc65!):

(13 bytes)

array.join`
`

5
Mas, melhor ainda, você pode evitar o parêntese. Leia docs ( developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/… ) para saber o porquê
edc65

Ah, certo. Obrigado, eu adicionei.
Chiru

9

Matrizes de preenchimento - valores estáticos e intervalos dinâmicos

Originalmente, deixei esses comentários como compreensivos, mas, como esse post foi focado principalmente em compreensões, achei que seria bom atribuir a isso seu próprio lugar.

O ES6 nos deu a capacidade de preencher matrizes com valores estáticos sem o uso de loops:

// ES5
function(x){for(i=0,a=[];i<x;i++)a[i]=0;return a}

// ES6
x=>Array(x).fill(0)

Ambos retornam uma matriz de comprimento x, preenchida com o valor 0.

Se você deseja preencher matrizes com valores dinâmicos (como um intervalo de 0 ... x), no entanto, o resultado é um pouco mais longo (embora ainda menor do que o modo antigo):

// ES5
function(x){for(i=0,a=[];i<x;i++)a[i]=i;return a}

// ES6
x=>Array(x).fill().map((a,i)=>i)

Ambos retornam uma matriz de comprimento x, começando com o valor 0 e terminando em x-1.

O motivo pelo qual você precisa estar .fill()lá é porque simplesmente inicializar uma matriz não permitirá que você a mapeie. Ou seja, fazer x=>Array(x).map((a,i)=>i)retornará uma matriz vazia. Você também pode contornar a necessidade de preenchimento (e, assim, torná-lo ainda mais curto) usando o operador de dispersão da seguinte forma:

x=>[...Array(x)]

Usando o operador e a .keys()função spread , agora você pode fazer um curto intervalo de 0 ... x:

x=>[...Array(x).keys()]

Se você deseja um intervalo personalizado de x ... y, ou um intervalo especializado completo (como números pares), pode se livrar .keys()e usar .map(), ou usar .filter(), com o operador spread:

// Custom range from x...y
(x,y)=>[...Array(y-x)].map(a=>x++)

// Even numbers (using map)
x=>[...Array(x/2)].map((a,i)=>i*2)

// Even numbers (using filter)
x=>[...Array(x).keys()].filter(a=>~a%2)

Aqui está uma sugestão para o segundo exemplo: x=>Array(x).fill(i=0).map(a=>i++)Além disso, não tenho certeza de que o 0 in .fill(0)seja necessário. Você já tentou isso sem?
ETHproductions

@ETHproductions Você está certo, esqueci que o 0 não é necessário no preenchimento antes do mapa. Isso faz com que 1 caractere seja menor que o sugerido, então eu continuarei assim. Obrigado!
Mwr247

Além disso, para o último exemplo, a=>a%2-1funciona bem, assim como a=>a%2<1.
ETHproductions

1
Um novo truque que aprendi: [...Array(x)]funciona tão bem quanto Array(x).fill()é 2 bytes mais curto. x=>[...Array(x)].map((a,i)=>i)
ETHproductions

1
@yonatanmn Very nice! Somente os comentários seriam 1) o 1/4exemplo seria mais curto [0,0,0,0]e 2) as funções estritas serão específicas da implementação, portanto, não retornarão um comprimento confiável ( Map32 bytes no Chrome, mas 36 bytes no Firefox).
#

9

Retornando valores em funções de seta

É de conhecimento geral que, se uma única instrução segue a declaração da função da seta, ela retorna o resultado dessa instrução:

a=>{return a+3}
a=>a+3

-7 bytes

Portanto, quando possível, combine várias instruções em uma. Isso é feito com mais facilidade, colocando as instruções entre parênteses e separando-as com vírgulas:

a=>{r=0;a.map(n=>r+=n);return r}
a=>(r=0,a.map(n=>r+=n),r)

-8 bytes

Mas se houver apenas duas instruções, geralmente é possível (e mais curto) combiná-las com &&ou ||:

a=>{r=0;a.map(n=>r+=n);return r}

// - Use && because map always returns an array (true)
// - declaration of r moved into unused map argument to make it only 2 statements
a=>a.map(n=>r+=n,r=0)&&r

-9 bytes

Finalmente, se você estiver usando o mapa (ou similar) e precisar retornar um número e garantir que o mapa nunca retornará uma matriz de um comprimento com um número, você pode retornar o número com |:

a=>{a=b=0;a.map(n=>(a+=n,b-=n));return a/b}

// - {} in map ensures it returns an array of undefined, so the | will make the returned
//   array cast from [ undefined, undefined, undefined ] to ",," to NaN to 0 and 0|n = n,
//   if the map returned [ 4 ] it would cast from [ 4 ] to "4" to 4 and make it 4|n
a=>a.map(n=>{a+=n,b-=n},a=b=0)|a/b

Nesse último exemplo, você também precisa ter certeza de que o número sempre será um número inteiro.
ETHproductions

8

Hacks aleatórios de string de modelo

Essa função reproduz duas strings (ou seja, se transforma "abc","de"em "adbec"):

f=(x,y)=>String.raw({raw:x},...y)

Observe que isso só funciona quando xfor maior que y. Como isso funciona, você pergunta? String.rawfoi projetado para ser uma tag de modelo, assim:

String.raw`x: ${x}\ny: ${y}\nx + y: ${x + y}`

Isso basicamente chama String.raw(["x: ", "\ny: ", "\nx + y: ", ""], x, y, x + y), embora não seja tão simples. A matriz de modelos também possui uma rawpropriedade especial , que é basicamente uma cópia da matriz, mas com as strings brutas. String.raw(x, ...args)basicamente retorna x.raw[0] + args[0] + x.raw[1] + args[1] + x.raw[2] + ...e assim por diante até xficar sem itens.

Então, agora que sabemos como String.rawfunciona, podemos usá-lo para nossa vantagem:

f=(x,y)=>String.raw({raw:x},...y)                   // f("abc", "de") => "adbec"
f=x=>String.raw({raw:x},...[...x].keys())           // f("abc") => "a0b1c"
f=(x,y)=>String.raw({raw:x},...[...x].fill(y))      // f("abc", " ") => "a b c"

Claro, para esse último, f=(x,y)=>x.split``.join(y)é bem mais curto, mas você entendeu.

Aqui estão algumas funções de riffling que também funcionam se xe ytêm o mesmo comprimento:

f=(x,y)=>String.raw({raw:x.match(/.?/g)},...y)
f=(x,y)=>String.raw({raw:x},...y)+y.slice(-1)  // Only works if x.length == y.length

Você pode aprender mais sobre o String.raw MDN .


7

Como jogar golfe com recursão

A recursão, embora não seja a opção mais rápida, é muitas vezes a mais curta. Geralmente, a recursão é mais curta se a solução puder ser simplificada para uma parte menor do desafio, especialmente se a entrada for um número ou uma sequência. Por exemplo, se f("abcd")pode ser calculado a partir de "a"e f("bcd"), geralmente é melhor usar recursão.

Tomemos, por exemplo, fatorial:

n=>[...Array(n).keys()].reduce((x,y)=>x*++y,1)
n=>[...Array(n)].reduce((x,_,i)=>x*++i,1)
n=>[...Array(n)].reduce(x=>x*n--,1)
n=>{for(t=1;n;)t*=n--;return t}
n=>eval("for(t=1;n;)t*=n--")
f=n=>n?n*f(n-1):1

Neste exemplo, a recursão é obviamente muito menor do que qualquer outra opção.

Que tal a soma dos códigos:

s=>[...s].map(x=>t+=x.charCodeAt(),t=0)|t
s=>[...s].reduce((t,x)=>t+x.charCodeAt())
s=>[for(x of(t=0,s))t+=x.charCodeAt()]|t  // Firefox 30+ only
f=s=>s?s.charCodeAt()+f(s.slice(1)):0

Essa é mais complicada, mas podemos ver que, quando implementada corretamente, a recursão economiza 4 bytes .map.

Agora vamos ver os diferentes tipos de recursão:

Pré-recursão

Este é geralmente o tipo mais curto de recursão. A entrada é dividida em duas partes ae b, e a função calcula algo com ae f(b). Voltando ao nosso exemplo fatorial:

f=n=>n?n*f(n-1):1

Nesse caso, aé n , bé n-1 e o valor retornado é a*f(b).

Nota importante: Todas as funções recursivas devem ter uma maneira de parar de repetir quando a entrada é pequena o suficiente. Na função fatorial, isso é controlado com o n? :1, ou seja, se a entrada for 0 , retorne 1 sem chamar fnovamente.

Pós-recursão

Pós-recursão é semelhante à pré-recursão, mas um pouco diferente. A entrada é dividida em duas partes ae b, e a função calcula algo com a, depois chama f(b,a). O segundo argumento geralmente tem um valor padrão (ou seja f(a,b=1)).

A pré-recursão é boa quando você precisa fazer algo especial com o resultado final. Por exemplo, se você deseja o fatorial de um número mais 1:

f=(n,p=1)=>n?f(n-1,n*p):p+1

Mesmo assim, no entanto, pós- nem sempre é mais curto do que usar pré-recursão dentro de outra função:

n=>(f=n=>n?n*f(n-1):1)(n)+1

Então, quando é mais curto? Você pode perceber que a pós-recursão neste exemplo requer parênteses em torno dos argumentos da função, enquanto a pré-recursão não. Geralmente, se as duas soluções precisarem de parênteses em torno dos argumentos, a pós-recursão será cerca de 2 bytes mais curta:

n=>!(g=([x,...a])=>a[0]?x-a.pop()+g(a):0)(n)
f=([x,...a],n=0)=>a[0]?f(a,x-a.pop()+n):!n

(programas aqui retirados desta resposta )

Como encontrar a solução mais curta

Geralmente, a única maneira de encontrar o método mais curto é tentar todos eles. Isso inclui:

  • rotações
  • .map(para cadeias de caracteres, [...s].mapou s.replace; para números, você pode criar um intervalo )
  • Compreensões de matriz
  • Pré-recursão (às vezes em outra dessas opções)
  • Pós-recursão

E essas são apenas as soluções mais comuns; a melhor solução pode ser uma combinação desses, ou até algo completamente diferente . A melhor maneira de encontrar a solução mais curta é tentar de tudo .


1
+1 para o seu valor, e eu gostaria de adicionar outro +1 para Zootopia
edc65

7

Maneiras mais curtas de fazer .replace


Se você deseja substituir todas as instâncias de uma substring exata por outra em uma string, a maneira óbvia seria:

f=s=>s.replace(/l/g,"y") // 24 bytes
f("Hello, World!")       // -> "Heyyo, Woryd!"

No entanto, você pode fazer 1 byte menor:

f=s=>s.split`l`.join`y`  // 23 bytes
f("Hello, World!")       // -> "Heyyo, Woryd!"

Observe que isso não é mais curto se você quiser usar qualquer recurso de regex além do gsinalizador. No entanto, se você estiver substituindo todas as instâncias de uma variável, geralmente é muito menor:

f=(s,c)=>s.replace(RegExp(c,"g"),"") // 36 bytes
f=(s,c)=>s.split(c).join``           // 26 bytes
f("Hello, World!","l") // -> "Heo, Word!"

Às vezes, você deseja mapear cada caractere em uma string, substituindo cada um por outra. Muitas vezes me vejo fazendo isso:

f=s=>s.split``.map(x=>x+x).join`` // 33 bytes
f=s=>[...s].map(x=>x+x).join``    // 30 bytes
f("abc") // -> "aabbcc"

No entanto, .replaceé quase sempre mais curto:

f=s=>s.replace(/./g,x=>x+x)  // 27 bytes
f=s=>s.replace(/./g,"$&$&")  // Also works in this particular case

Agora, se você deseja mapear cada caractere em uma string, mas não se importa com a string resultante, .mapgeralmente é melhor porque você pode se livrar de .join``:

f=s=>s.replace(/./g,x=>t+=+x,t=0)&&t // 36 bytes
f=s=>[...s].map(x=>t+=+x,t=0)&&t     // 32 bytes
f("12345")  // -> 15

Para o último caso, se apenas alguns caracteres correspondentes a uma expressão regular (like /\w/g) estiverem interessados, o uso de replace será muito melhor como nesta demonstração .
Shieru Asakoto

6

Gravando literais RegEx com eval

O construtor regex pode ser muito volumoso devido ao nome longo. Em vez disso, escreva um literal com eval e backticks:

eval(`/<${i} [^>]+/g`)

Se a variável ifor igual a foo, isso irá gerar:

/<foo [^>]+/g

Isso é igual a:

new RegExp("<"+i+" [^>]+","g")

Você também pode usar String.rawpara evitar ter que escapar repetidamente das barras invertidas\

eval(String.raw`/\(?:\d{4})?\d{3}\d{3}\d{3}\d{3}\d{3}\d{3}\d{4}/g`)

Isso produzirá:

/(?:\d{4})?\d{3}\d{3}\d{3}/g

Qual é igual a:

RegExp("\\(?:\\d{4})?\\d{3}\\d{3}\\d{3}\\d{3}\\d{3}\\d{3}\\d{4}","g")

Tenha em mente!

String.rawocupa muitos bytes e, a menos que você tenha pelo menos nove barras invertidas, String.rawserá mais longo.


Você não precisa o newlá dentro, portanto, usando o construtor é realmente mais curto para o segundo exemplo
Optimizer

5

.forEachvs forloops

Sempre prefira .mapqualquer loop for. Economia fácil e instantânea.


a.map(f)
for(x of a)f(x);
for(i=0;i<a.length;)f(a[i++]);
  • Total de 8 bytes para o original
  • 8 bytes salvos vs for-of ( redução de 50% )
  • 22 bytes salvos vs estilo C para loop ( redução de 73% )

a.map(x=>f(x,0))
for(x of a)f(x,0);
for(i=0;i<a.length;)f(a[i++],0);
  • Total de 16 bytes para o original
  • 2 bytes salvos vs for-of ( redução de 11% )
  • 16 bytes salvos vs estilo C para loop ( redução de 50% )

a.map((x,i)=>f(x,i,0))
for(i in a)f(a[i],i,0);
for(i=0;i<a.length;)f(a[i],i++,0);
  • Total de 22 bytes para o original
  • 1 byte economizado vs entrada ( redução de 4% )
  • 11 bytes salvos vs estilo C para loop ( redução de 33% )

a.map(x=>f(x)&g(x))
for(x of a)f(x),g(x);
for(i=0;i<a.length;)f(x=a[i++]),g(x);
  • Total de 19 bytes para o original
  • 2 bytes salvos vs for-of ( redução de 10% )
  • 18 bytes salvos vs estilo C para loop ( redução de 49% )

5

Usando contadores não inicializados na recursão

Nota : Estritamente falando, isso não é específico para o ES6. No entanto, faz mais sentido usar e abusar da recursão no ES6, devido à natureza concisa das funções das setas.


É bastante comum encontrar uma função recursiva que usa um contador kinicialmente definido como zero e incrementado a cada iteração:

f = (…, k=0) => [do a recursive call with f(…, k+1)]

Sob certas circunstâncias, é possível omitir a inicialização desse contador e substituir k+1por -~k:

f = (…, k) => [do a recursive call with f(…, -~k)]

Esse truque normalmente salva 2 bytes .

Por que e quando isso funciona?

A fórmula que torna possível é ~undefined === -1. Portanto, na primeira iteração, -~kserá avaliado como 1. Nas próximas iterações, -~ké essencialmente equivalente a -(-k-1)igual a k+1, pelo menos para números inteiros no intervalo [0… 2 31 -1].

No entanto, você deve garantir que k = undefineda primeira iteração não perturbe o comportamento da função. Você deve ter especialmente em mente que a maioria das operações aritméticas que envolvem undefinedresultará em NaN.

Exemplo 1

Dado um número inteiro positivo n, esta função procura o menor número inteiro kque não se divide n:

f=(n,k=0)=>n%k?k:f(n,k+1)   // 25 bytes

Pode ser reduzido para:

f=(n,k)=>n%k?k:f(n,-~k)     // 23 bytes

Isso funciona porque n % undefinedé NaN, o que é falso. Esse é o resultado esperado na primeira iteração.

[Link para a resposta original]

Exemplo 2

Dado um número inteiro positivo n, esta função procura por um número inteiro pque (3**p) - 1 == n:

f=(n,p=0,k=1)=>n<k?n>k-2&&p:f(n,p+1,k*3)  // 40 bytes

Pode ser reduzido para:

f=(n,p,k=1)=>n<k?n>k-2&&p:f(n,-~p,k*3)    // 38 bytes

Isso funciona porque pnão é usado de todo na primeira iteração ( n<ksendo falso).

[Link para a resposta original]


5

Funções ES6

Matemática

Math.cbrt(x)salva caracteres que Math.pow(x,1/3).

Math.cbrt(x)
Math.pow(x,1/3)

3 caracteres salvos

Math.hypot(...args)é útil quando você precisa da raiz quadrada da soma dos quadrados dos args. Tornar o código ES5 para fazer isso é muito mais difícil do que usar um built-in.

A função Math.trunc(x)não seria útil, pois x|0é mais curta. (Obrigado Mwr247!)

Existem muitas propriedades que exigem muito código no ES5, mas mais fáceis no ES6:

  • Math.acosh, asinh, atanh, cosh, sinh, tanh. Calcula o equivalente hiperbólico das funções trigonométricas.
  • Math.clz32. Pode ser possível fazer no ES5, mas agora é mais fácil. Conta zeros à esquerda na representação de 32 bits de um número.

Há muito mais, por isso estou indo só para listar alguns:
Math.sign, Math.fround, Math.imul, Math.log10, Math.log2, Math.log1p.


Math.trunc(x)é quatro vezes maior que x|0.
Mwr247

@ mwr247: Ok, será atualizado.
Ev3commander 22/10/2015

Aqui estão os equivalentes mais curtos do ES5 que conheço para algumas dessas funções: Math.hypot(a,b) => Math.sqrt(a*a+b*b)(3 bytes a mais; fica ainda mais com mais argumentos), Math.sign(a) => (a>0)-(a<0)(1 byte mais curto, mas precisa de parênteses em alguns casos; pode não funcionar com NaN)
ETHproductions

@ETHproductions Você precisa da matriz de argumentos para (a solução alternativa es5 do) hypot. E você tem certeza de que a solução alternativa para Math.sign funciona para -0? (Ele deve retornar -0)
ev3commander

1
@ ev3commander São apenas substituições em linha de seus respectivos equivalentes ES6, e são reduzidos para 99% dos usos. A recriação dessas funções exigiria muito mais código. Além disso, não vejo razão para precisar de um caso especial para -0, pois (AFAIK) não há como obter -0, exceto especificando-o manualmente, e praticamente não há utilidade para ele no code-golf. Mas obrigado por apontar essas coisas.
ETHproductions

5

Otimizando pequenos intervalos constantes para map()

Contexto

map()for[0..N1]

for(i = 0; i < 10; i++) {
  do_something_with(i);
}

pode ser substituído por:

[...Array(10).keys()].map(i => do_something_with(i))

ou mais comumente:

[...Array(10)].map((_, i) => do_something_with(i))

Array(N)N

[0..N1]

i

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ≤ 6       | use a raw array of integers          | [0,1,2,3].map(i=>F(i))          | 2N+10
N = 7       | use either a raw array of integers   | [0,1,2,3,4,5,6].map(i=>F(i))    | 24
            | or a string if your code can operate | [...'0123456'].map(i=>F(i))     | 23
            | with characters rather than integers |                                 |
8 ≤ N ≤ 9   | use scientific notation 1e[N-1]      | [...1e7+''].map((_,i)=>F(i))    | 24
N = 10      | use scientific notation 1e9          | [...1e9+''].map((_,i)=>F(i))    | 24
            | or the ES7 expression 2**29+'4' if   | [...2**29+'4'].map(i=>F(i))     | 23
            | the order doesn't matter and your    |                                 |
            | code can operate with characters     |  (order: 5,3,6,8,7,0,9,1,2,4)   |
            | rather than integers                 |                                 |
11 ≤ N ≤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map((_,i)=>F(i))   | 25
N = 18      | use the fraction 1/3                 | [...1/3+''].map((_,i)=>F(i))    | 24
N = 19      | use the fraction 1/6                 | [...1/6+''].map((_,i)=>F(i))    | 24
20 ≤ N ≤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map((_,i)=>F(i))   | 25
N = 22      | use scientific notation -1e20        | [...-1e20+''].map((_,i)=>F(i))  | 26
23 ≤ N ≤ 99 | use Array(N)                         | [...Array(23)].map((_,i)=>F(i)) | 27

NB : O comprimento do código de retorno de chamada F(i)não é contado.

[1..9]

[1..9]

[...17**6+'8'].map(i=>F(i))  // order: 2,4,1,3,7,5,6,9,8; length: 23

Otimizações sem contador

N

N           | Method                               | Example                         | Length
------------+--------------------------------------+---------------------------------+-------
N ≤ 5       | use a raw array of integers          | [0,0,0,0].map(_=>F())           | 2N+10
6 ≤ N ≤ 10  | use scientific notation 1e[N-1]      | [...1e7+''].map(_=>F())         | 20
11 ≤ N ≤ 17 | use scientific notation 1e[N-1]      | [...1e12+''].map(_=>F())        | 21
N = 18      | use the fraction 1/3                 | [...1/3+''].map(_=>F())         | 20
N = 19      | use the fraction 1/6                 | [...1/6+''].map(_=>F())         | 20
20 ≤ N ≤ 21 | use scientific notation 1e[N-1]      | [...1e20+''].map(_=>F())        | 21
N = 22      | use scientific notation -1e20        | [...-1e20+''].map(_=>F())       | 22
23 ≤ N ≤ 99 | use Array(N)                         | [...Array(23)].map(_=>F())      | 23

NB : O comprimento do código de retorno de chamada F()não é contado.


Não deveria 2**26ser 2**29?
Shaggy

@Shaggy Heck. Boa pegada!
precisa

Não queria editar em mim mesmo porque tenho cegueira de código! : D
Shaggy

Usando .keys(), você não precisa de um lambda:[...Array(10).keys()].map(do_something_with)
long-lazuli

@ longa-lazuli Se você não precisa de um lambda e só quer um intervalo, então você provavelmente não precisa mapear tanto ...
Arnauld

4

Atribuições de reestruturação

O ES6 introduz uma nova sintaxe para desestruturar atribuições, ou seja, cortar um valor em partes e atribuir cada parte a uma variável diferente. Aqui estão alguns exemplos:

Strings e matrizes

a=s[0];b=s[1];       // 14 bytes
[a,b]=s;             //  8 bytes

a=s[0];s=s.slice(1); // 20 bytes
a=s.shift();         // 12 bytes, only works if s is an array
[a,...s]=s;          // 11 bytes, converts s to an array

Objetos

a=o.asdf;b=o.bye;c=o.length; // 28 bytes
{asdf:a,bye:b,length:c}=o;   // 26 bytes

a=o.a;b=o.b;c=o.c; // 18 bytes
{a,b,c}=o;         // 10 bytes

Essas atribuições também podem ser usadas em parâmetros de função:

f=a=>a[0]+a[1]+a[2]
f=([a,b,c])=>a+b+c

f=b=>b[1]?b[0]+f(b.slice(1)):b[0]*2
f=b=>b[1]?b.shift()+f(b):b[0]*2
f=([a,...b])=>b[0]?a+f(b):a*2

4

Mais uma maneira de evitar return

Você sabe que deve usar eval para funções de seta com várias instruções e um retorno . Em alguns casos incomuns, você pode economizar mais usando uma subfunção interna.

Eu digo incomum porque

  1. O resultado retornado não deve ser a última expressão avaliada no loop

  2. Deve haver (pelo menos) 2 inicializações diferentes antes do loop

Nesse caso, você pode usar uma subfunção interna sem retorno, tendo um dos valores iniciais passados ​​como parâmetro.

Exemplo Encontre o recíproco da função soma da exp para valores em um intervalo de a a b.

O longo caminho - 55 bytes

(a,b)=>{for(r=0,i=a;i<=b;i++)r+=Math.exp(i);return 1/r}

Com eval - 54 bytes

(a,b)=>eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i);1/r")

Com uma função interna - 53 bytes

(a,b)=>(i=>{for(r=0;i<=b;i++)r+=Math.exp(i)})(a)||1/r

Observe que, sem a exigência de um limite de intervalo menor a, eu posso mesclar as inicializações de ie er, e a versão eval é mais curta.


Em sua amostra não há necessidade de mantera
l4m2

@ l4m2 Eu não posso obter o seu ponto, ajudar por favor ...
edc65

(i,b)=>{for(r=0;i<=b;i++)r+=Math.exp(i);return 1/r}
l4m2 13/04

@ l4m2 uh direita, return a/rseria um exemplo melhor
edc65

1
eval ainda é melhor (a,b)=>1/eval("for(r=0,i=a;i<=b;i++)r+=Math.exp(i)")e, neste caso,(i,b)=>1/eval("for(r=0;i<=b;)r+=Math.exp(i++)")
JayXon 19/09

4

Usando a sintaxe de currying para funções diádicas e recursivas

Funções diádicas

Sempre que uma função usa exatamente dois argumentos sem valores padrão, o uso da sintaxe de currying salva um byte.

Antes

f =
(a,b)=>a+b  // 10 bytes

Chamado com f(a,b)

Depois de

f =
a=>b=>a+b   // 9 bytes

Chamado com f(a)(b)

Nota : Esta postagem no Meta confirma a validade dessa sintaxe.

Funções recursivas

O uso da sintaxe de currying também pode salvar alguns bytes quando uma função recursiva usa vários argumentos, mas só precisa atualizar alguns deles entre cada iteração.

Exemplo

A função a seguir calcula a soma de todos os números inteiros no intervalo [a,b]:

f=(a,b)=>a>b?0:b+f(a,b-1)   // 25 bytes

Como apermanece inalterado durante todo o processo, podemos salvar 3 bytes usando:

f =                         // no need to include this assignment in the answer anymore
a=>F=b=>a>b?0:b+F(b-1)      // 22 bytes

Nota : Conforme observado por Neil nos comentários, o fato de um argumento não ser passado explicitamente para a função recursiva não significa que ele deva ser considerado imutável. Se necessário, poderíamos modificar adentro do código de função com a++, a--ou qualquer sintaxe semelhante.


O último exemplo pode ser escrito como a=>F=b=>a>b?0:a+++F(b), modificando apara cada chamada recursiva. Isso não ajuda nesse caso, mas pode salvar bytes em casos com mais argumentos.
Neil

Heh, eu só estava pensando em escrever uma dica para este :-)
ETHproductions

4

Função de teste de primazia

A seguinte função de 28 bytes retorna truepara números primos e falsenão primos:

f=(n,x=n)=>n%--x?f(n,x):x==1

Isso pode ser facilmente modificado para calcular outras coisas. Por exemplo, essa função de 39 bytes conta o número de números primos menor ou igual a um número:

f=(n,x=n)=>n?n%--x?f(n,x):!--x+f(n-1):0

Se você já possui uma variável nque deseja verificar quanto à primalidade, a função da primalidade pode ser bastante simplificada:

(f=x=>n%--x?f(x):x==1)(n)

Como funciona

f = (         // Define a function f with these arguments:
  n,          //   n, the number to test;
  x = n       //   x, with a default value of n, the number to check for divisibility by.
) =>
  n % --x ?   //   If n is not divisible by x - 1,
  f(n, x)     //     return the result of f(n, x - 1).
              //   This loops down through all numbers between n and 0,
              //     stopping when it finds a number that divides n.
  : x == 1    //   Return x == 1; for primes only, 1 is the smallest number
              //     less than n that divides n.
              //   For 1, x == 0; for 0, x == -1.

Nota: Isso falhará com um erro "muita recursão" quando chamado com uma entrada suficientemente grande, como 12345. Você pode contornar isso com um loop:

f=n=>eval('for(x=n;n%--x;);x==1')

1
Mas não com muita recursão para uma entrada tão pouco quanto 12345
edc65

x==1provavelmente pode ser x<2uma economia.
CalculatorFeline

Graças @CalculatorFeline, mas, em seguida, ele falha para 1ou 0(porque xseria 0ou -1, respectivamente)
ETHproductions

Pode ser útil em certos casos. Além disso, !~-xpara -0 bytes.
CalculatorFeline

3

Array#concat() e o operador spread

Isso depende muito da situação.


Combinando várias matrizes.

Prefira a função concat, a menos que seja clonada.

0 bytes salvos

a.concat(b)
[...a,...b]

3 bytes desperdiçados

a.concat(b,c)
[...a,...b,...c]

3 bytes salvos

a.concat()
[...a]

6 bytes salvos

// Concatenate array of arrays
[].concat.apply([],l)
[].concat(...l)

Prefira usar uma matriz já existente para Array#concat().

Fácil 4 bytes salvos

[].concat(a,b)
a.concat(b)

3

Retornar resultado intermediário

Você sabe que, usando o operador vírgula, é possível executar uma sequência de expressões retornando o último valor. Mas abusando da sintaxe literal da matriz, você pode retornar qualquer valor intermediário. É útil em .map (), por exemplo.

// capitalize words
// f is a flag indicating if prev char is space
[...x].map(c=>(f?c=c.toUpperCase():0,f=c<'!',c),f=1).join('')

// shortened to ...
[...x].map(c=>[f?c.toUpperCase():c,f=c<'!'][0],f=1).join('')

3
Lembre-se, é claro, isso .join('')pode ser #.join``
Cyoce

3

Definir padrões de parâmetros de função

($,a,b,_)=>_!=undefined?'asdf':_ // before
($,a,b,_)=>_!=[]._?'asdf':_ // before, but a bit golfed
($,a,b,_='asdf')=>_ // after

Este é realmente útil ...

No entanto, certifique-se de entender que algo _=>_||'asdf'mais curto é quando você passa apenas um argumento (útil) para a função.


1
Eu notar que usando um OR _=>_||'asdf'é geralmente mais curto na maioria dos casos
Downgoat

@Downgoat Eu observaria que isso retorna "asdf"para uma entrada de ""(string vazia).
ETHproductions

2
Observe que o padrão é avaliado sempre que o argumento tiver sido undefined, mesmo se você passar explicitamente esse valor. Por exemplo, [...Array(n)].map((a,b,c)=>b)sempre passa undefinedpara ae, portanto, você pode fornecer um valor padrão para ele (embora não em termos de b).
315 Neil

3

Use em evalvez de chaves para funções de seta

As funções de seta são impressionantes. Eles assumem a forma x=>y, onde xé um argumento e yé o valor de retorno. No entanto, se você precisar usar uma estrutura de controle, como while, teria que colocar chaves, por exemplo =>{while(){};return}. No entanto, podemos contornar isso; felizmente, a evalfunção pega uma string, avalia essa string como código JS e retorna a última expressão avaliada . Por exemplo, compare estes dois:

x=>{while(foo){bar};return baz} // before
x=>eval('while(foo){bar};baz')  // after
//                            ^

Podemos usar uma extensão desse conceito para reduzir ainda mais o nosso código: aos olhos de eval, as estruturas de controle também retornam sua última expressão avaliada. Por exemplo:

x=>{while(foo)bar++;return bar} // before
x=>eval('while(foo)++bar')      // after
//                        ^^^^^

3

Operações lógicas de golfe no ES6

"GLOE (S6)"

Lógica geral

Digamos que você tenha construído declarações se t. Veja se você pode usar qualquer uma das seguintes substituições:

Traditional conjuction: s&&t
Equivalent conjuction: s*t OR s&t

Traditional disjunction: s||t
Equivalent disjunction: s+t OR s|t

(Eles podem não funcionar se o pedido estiver errado; ou seja, +e *têm uma precedência de pedido menor que ||e &&).

Além disso, aqui estão algumas expressões lógicas úteis:

  • Quer sou té verdade / XOR:s^t
  • se tsão o mesmo valor de verdade: !s^tous==t

Lógica de matriz

Todos os membros da acondição de satisfação p:

a.every(p)                             // 10 bytes (11 bytes saved)
a.map(x=>c&=p(x),c=1)                  // 21 bytes (16 bytes saved)
for(i=0,c=1;i<a.length;c&=p(a[i++]));  // 37 bytes (hideously long)

Pelo menos um membro da acondição satisfaz p:

a.some(p)                            // 9  bytes (13 bytes saved)
a.map(x=>c|=p(x),c=0)                // 21 bytes (14 bytes saved)
for(i=c=0;i<a.length;c|=p(a[i++]));  // 35 bytes (just please no)

Nenhum membro da acondição de satisfazer p: !a.some(p).

O elemento eexiste na matriz a:

a.includes(e)                        // 13 bytes, standard built-in
~a.indexOf(e)                        // 13 bytes, "traditional" method
a.find(x=>e==x)                      // 15 bytes, find (ES6)
a.some(x=>x==e)                      // 15 bytes, some (ES5)
(a+"").search(e)                     // 16 bytes, buggy
a.filter(t=>t==e).length             // 24 bytes, no reason to use this
for(i=c=0;i<a.length;c+=e==a[i++]);  // 35 bytes, super-traditional

Elemento eque não existe em ordem a:

!a.includes(e)
!~a.indexOf(e)
a.every(t=>t!=e)
!a.filter(t=>t==e).length
for(i=0,c=1;i<a.length;c*=e!=a[i++]);

Eu geralmente uso &&e ||como x?y:xe x?x:y, respectivamente. Mas posso ver como isso seria útil em mais programas baseados em lógica. O único problema +seria que, por exemplo, 3e -3ambos são verdadeiros, mas 3+-3não são.
ETHproductions

@ETHproductions Ah, você está certo; esse é um caso extremo. -também poderia funcionar, se s != t.
Conor O'Brien

a.filter(t=>t==e).length==a.lengthestá incorreto. Deveria ser!a.filter(t=>t==e).length
ETHproductions

@ETHproductions você está certo!
Conor O'Brien

3

Encurte chamadas de função repetidas

Se você repetiu chamadas para uma função com um nome longo, como manipulação de tela:

c.lineTo(0,100);c.lineTo(100,100);c.lineTo(100,0);c.lineto(0,0);c.stroke()

A maneira tradicional de encurtar isso seria usar o apelido de nome da função:

c[l='lineTo'](0,100);c[l](100,100);c[l](100,0);c[l](0,0);c.stroke()

Se você tiver chamadas suficientes, a melhor maneira é criar uma função que faça o trabalho para você:

l=(x,y)=>c.lineTo(x,y);l(0,100);l(100,100);l(100,0);l(0,0);c.stroke()

Se a maioria das chamadas de função estiver encadeada, você poderá fazer com que a função retorne por si mesma, permitindo cortar dois bytes de cada chamada sucessiva:

l=(x,y)=>c.lineTo(x,y)||l;l(0,100)(100,100)(100,0)(0,0);c.stroke()

Exemplo de uso: 1 , 2


1
você pode encurtar com o operador de ligação :(l=::c.lineTo)(0,100)(100,100)(100,0)(0,0);c.stroke()
Downgoat

@Downgoat Obrigado, que navegadores suportam isso? (Além disso, pelo que tenho visto que irá erro na segunda chamada, desde que c.lineTonão em si retornar naturalmente)
ETHproductions

você tem que esfregá-lo através de babel, já que é uma característica ES7
Downgoat

3

Operador de ligação ::

O operador de ligação pode ser usado para ajudar a reduzir bytes em funções repetidas:

(x='abc'.search(a))+x.search(b) // Before
(x=::'abc'.search)(a)+x(b)      // 5 bytes saved

Além disso, se você quiser usar a função com um thisexemplo diferente :

s[r='replace'](/a/g,'b')+s[r](/c/g,'d') // Before
(r=s.replace)(/a/g,'b')+s::r(/c/g,'d')  // 1 byte saved

3

Evitando vírgulas ao armazenar muitos dados

Se você possui muitos dados (por exemplo, índices, caracteres, ...) que precisa armazenar em uma matriz, é melhor deixar todas as vírgulas afastadas. Isso funciona melhor se todos os dados tiverem o mesmo comprimento de string, 1 obviamente sendo ideal.

43 bytes (linha de base)

a=[[3,7,6,1,8,9,4,5,2],[5,4,3,2,7,6,5,4,3]]

34 bytes (sem vírgulas)

a=[[..."376189452"],[..."543276543"]]

Se você estiver disposto a alterar o acesso à sua matriz , poderá reduzir ainda mais isso, armazenando os mesmos valores da seguinte forma:

27 bytes (mesmos dados, apenas altera o acesso ao array)

a=[..."376189452543276543"]

Por que apenas o último bloco é destacado?
CalculatorFeline

@CalculatorFeline Obrigado, corrigido.
Chiru
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.