
Outra tarefa artificial para programação anormal em JavaScript . Desta vez, por ocasião do próximo ano novo de 2019. Espero que seja igualmente interessante decidir o quão interessante foi para mim. Eu pergunto curioso sob gato. Todo champanhe e todo feliz!
Tarefas anteriores:
No ano passado, o Papai Noel coletou uma lista decente de nomes de desenvolvedores normais e agora planeja escrever um programa de parabéns. O formato é: happy new year, ${username}!
. Mas aqui está a má sorte: o teclado trava e não permite que você digite muitos caracteres latinos. Depois de examinar o defeito, os elfos fizeram uma observação interessante de que, do que mais funciona, você pode adicionar Snowing day
um Snowing day
. A fonte de saída pode ser selecionada a seu critério.
Portanto, na entrada - alguma matriz de cadeias não vazias (o nome não pode estar vazio). É necessário escrever um programa usando apenas caracteres latinos: S
, n
, o
, w
, i
, g
, d
, a
, y
(um total de 9 caracteres, um dos quais está em maiúsculas). O programa deve contornar a matriz passada e gerar a frase happy new year, ${username}!
para cada nome happy new year, ${username}!
usando algum tipo de fonte de saída: alert , console.log ou o que vier à mente. Bem, seria bom não poluir o contexto global.
Solução habitual
Se você não inventa nada, tudo é muito simples:
function happy(users) { for (let i = 0; i !== users.length; i += 1) { console.log(`happy new year, ${users[i]}!`); } }
ou melhor:
function happy(users) { users.forEach(user => console.log(`happy new year, ${user}!`)); }
Usamos com nossa matriz, sejam usuários :
let users = ['John', 'Jack', 'James']; happy(users);
Mas aqui está exagerado: usar apenas caracteres permitidos na implementação em latim. Tente lidar por conta própria primeiro e depois participe da discussão.
Decisão divertida
O paciente pode ver a solução abaixo no JSFiddle agora.
Para resolver o problema, você precisa se livrar do excesso de latim da seguinte maneira:
- A função de palavra-chave em uma declaração de função .
- A palavra-chave let (ou var ) para declarar variáveis.
- Palavras-chave ao organizar um loop para iterar sobre uma matriz passada.
- A formação do texto da mensagem.
- Chame alguma função para gerar o resultado.
As funções de seta nos ajudarão facilmente com o primeiro problema:
(arr => el.forEach())(users);
No momento, não prestaremos atenção aos nomes de variáveis, pois podemos renomeá-los facilmente no final.
Usamos “setas” com IIFE sempre que uma função é necessária ou seu resultado imediatamente. Além disso, as funções permitem livrar-se das diretivas let e var de duas maneiras:
(param => )(value); ((param = value) => )();
Nos dois casos, declaramos uma variável nos parâmetros da função. Somente no primeiro caso, passamos o valor quando a função é chamada, e no segundo - usamos o parâmetro da função padrão.
De fato, os problemas começam no terceiro ponto. Não temos caracteres suficientes para os loops clássicos de for , do , while , nem para as opções de travessia de for..in e for..of , nem para os métodos de array paraCada , mapa , filtro (onde você pode passar o retorno de chamada). Mas podemos implementar nossa função de iteração sobre uma matriz:
function iterate(arr, consume) { function iter(i) { if (arr[i]) { consume(arr[i]); iter(++i); } } iter(0); }
Atravessamos recursivamente os elementos até que a verificação da condição atual caia. Por que podemos confiar na transformação lógica aqui? porque o elemento da nossa matriz não é uma string vazia (apenas envolve falso ), mas quando saímos da matriz por meio de um incremento de índice, somos indefinidos (convertidos para false ).
Reescrevemos a função usando as expressões de seta:
let iterate = (arr, consume) => ( (iter = i => { if (arr[i]) { consume(arr[i]); iter(++i); } }) => iter(0) )();
Mas não podemos usar a declaração if , pois não temos o caractere f
. Para que nossa função satisfaça a condição, precisamos nos livrar dela:
let iterate = (arr, consume) => ( (iter = i => arr[i] ? (consume(arr[i]), iter(++i)) : 0) => iter(0) )();
O operador ternário e a capacidade de combinar duas expressões em uma através do operador vírgula nos ajudaram nisso. Usaremos essa função posteriormente no layout da solução.
O quarto problema é que, em qualquer caso, precisamos obter uma string com caracteres ausentes. Obviamente, usaremos números para representar caracteres. Existem várias opções:
- Uma função String.fromCharCode que espera que o número inteiro retorne e retorne uma sequência criada a partir da sequência Unicode especificada.
- A sequência de
\uhhhh
permite a saída de qualquer caractere Unicode usando o código hexadecimal especificado. - Formato
&#dddd;
para caracteres html permite exibir um símbolo no documento da página usando o código decimal especificado. - A função toString do objeto de protótipo Number possui um parâmetro radix adicional - a base do sistema numérico.
- Talvez haja algo mais ...
Você pode ir na direção das três primeiras opções e agora considerar a provavelmente a mais fácil para esta tarefa: Number.prototype.toString . O valor máximo do parâmetro radix é 36 (10 dígitos + 26 caracteres latinos em minúsculas):
let symb = sn => (sn + 9).toString(36);
Assim, podemos obter qualquer caractere latino pelo número do alfabeto, começando com 1. A única limitação é que todos os caracteres são minúsculos. Sim, basta exibir o texto na mensagem, mas não podemos adicionar alguns métodos e funções (o mesmo para cada ).
Mas é muito cedo para se alegrar, primeiro você precisa se livrar do toString na entrada da função. Primeiro, passamos ao método da seguinte maneira:
let symb = sn => (sn + 9)['toString'](36);
Se você olhar atentamente, para a string toString
, precisamos apenas de dois caracteres: t
e r
: todo o resto está na palavra Snowing
. Obtê-los é bem simples, pois a ordem deles já indica a true
. Usando conversões implícitas de tipo, podemos obter essa sequência e os caracteres necessários da seguinte maneira:
!0+'';
Atingimos a função de obter qualquer letra latina:
let symb = sn => (sn + 9)[(!0+'')[0] + 'oS' + (!0+'')[0] + (!0+'')[1] + 'ing'](36);
Para obter palavras de uma matriz de números de sequência de letras usando symb , usamos a função Array.prototype.reduce padrão:
[1,2,3].reduce((res, sn) => res += symb(sn), '');
Sim, isso não nos convém. Mas em nossa solução, podemos fazer algo semelhante com a função iterate :
let word = chars => (res => (iterate(chars, ch => res += symb(ch)), res))(''); word([1,2,3]);
Pessoas atentas notarão que desenvolvemos a função iterada para uma série de strings, mas a usamos aqui com números. É por isso que o índice inicial do alfabeto é 1, e não 0. Caso contrário, um ciclo improvisado terminaria quando atingisse 0 (letra a
).
Para facilitar o mapeamento de caracteres para seus números de série, você pode obter um dicionário:
[...Array(26).keys()].reduce((map, i) => (map[symb(i + 1)] = i + 1, map), {});
Mas é mais sensato simplificar ainda mais e escrever a função da transformação inversa das palavras como um todo:
let reword = str => str.split('').map(s => parseInt(s, 36) - 9); reword('happy');
Concluímos a função de formar a própria mensagem:
let message = name => word([8,1,16,16,25]) + ' ' + word([14,5,23]) + ' ' + word([25,5,1,18]) + ', ' + name + '!';
Ainda resta muito pouco para lidar com a conclusão do quinto problema. Console , alerta , confirmação , prompt , innerHTML , document.write vêm à mente. Mas nenhuma das opções listadas pode ser acessada diretamente.
Também tivemos a oportunidade de obter qualquer palavra usando a função de palavra . Isso significa que podemos chamar muitas funções de objetos, acessando-as entre colchetes, como foi o caso de toString .
Como usamos as funções de seta, esse contexto permanece global (e não há necessidade de encaminhá-lo). Em qualquer lugar, podemos acessar muitas de suas funções através de uma linha:
this[word([1,12,5,18,20])]('hello');
Mas, para o "desenho" this
novamente nos falta caracteres. Podemos substituí-lo por Window.self , mas é ainda pior em termos do alfabeto disponível. No entanto, vale a pena prestar atenção no próprio objeto da janela , cujo "contorno" é bastante satisfatório para nós, mesmo que fosse cabra, e é muito mais longo!
A propósito, na primeira versão da tarefa, a frase-chave era apenas a palavra Snowing
e a window
não podia ser dobrada (devido à ausência do caractere d
). O acesso ao contexto foi baseado em um dos truques do jsfuck :
(_ => 0)['constructor']('return this')()['alert']('hello');
Ou você também pode acessar diretamente algo em um contexto global:
(_ => 0)['constructor']('return alert')()('hello');
Como você pode ver, nos exemplos, todo o latim está em linhas. Aqui, criamos uma função a partir de uma string e obtemos acesso à Function (construtor) a partir da função de seta desperdiçada. Mas isso é de alguma forma demais! Talvez alguém saiba como acessar o contexto em nossas condições?
Finalmente, juntamos tudo! O corpo da nossa função "principal" chamará iterate para a matriz passada e o consumidor produzirá o resultado da função de compilação de mensagens já incorporada. Para o texto da mensagem e comandos, é usada uma função de palavra , que também precisa ser repetida , e a determinaremos a seguir nos parâmetros padrão . Assim:
(users => ( (( // firstly we need the iterating function iterate = (array, consume) => ((iter = i => array[i] ? (consume(array[i]), iter(++i)) : 0) => iter(0))(),
Renomeie as variáveis usando o alfabeto permitido:
(_10 => ( (( _123 = (ann, snow) => ((_12 = i => ann[i] ? (snow(ann[i]), _12(++i)) : 0) => _12(0))(), wo = ann => (w => (_123(ann, an => w += (an + 9)[(!0+'')[0] + 'oS' + (!0+'')[0] + (!0+'')[1] + 'ing'](36) ), w) )('') ) => _123(_10, _1 => window[wo([3,15,14,19,15,12,5])][wo([12,15,7])]( wo([8,1,16,16,25]) + ' ' + wo([14,5,23]) + ' ' + wo([25,5,1,18]) + ', ' + _1 + '!' ) ))() ))(users);
Variável | Descrição do produto |
---|
_123 {function} | A função de iteração para iterar sobre os elementos de uma matriz. |
_12 {function} | A função iter local que itera as chamadas recursivamente. |
neve {function} | Consuma a função como um retorno de chamada para iterar . |
ann {Array<Any>} | Parâmetro de matriz. |
um {Any} | Parâmetro do elemento de matriz. |
wo {function} | A palavra funciona para formar palavras. |
w {string} | Variável local para acumular uma sequência de caracteres no word . |
_10 {Array<string>} | A matriz original de usuários. |
_1 {string} | O usuário da matriz de origem, seu nome. |
Isso é tudo. Escreva suas idéias e pensamentos sobre isso, pois há muitas opções para fazer algo diferente ou nada.
Conclusão
É interessante que inventar uma palavra ou frase para as condições do problema seja um teste real. Queria que ela fosse baixa, pouco sugestiva e adequada para uma solução mais ou menos concisa.
A inspiração para esta tarefa foi fornecida pela funcionalidade do JavaScript e pelo isotérico de 6 caracteres conhecidos por muitos. Como tarefas discutidas anteriormente, isso pode ter várias variações sobre o tópico, e não a única solução. Basta chegar a uma redação simples e uma frase-chave. Vejo você no ano novo!