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..N−1]

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..N−1]

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.