JavaScript entretenido: Snow Day

Imagen


Otra tarea artificial para la programación anormal en JavaScript . Esta vez con motivo del próximo año nuevo 2019. Espero que sea igual de interesante decidir qué tan interesante fue para mí pensar. Pregunto curioso debajo de gato. Todo champaña y todo feliz!


Tareas anteriores:



Redacción


Durante el año pasado, Santa Claus ha recopilado una lista decente de nombres de desarrolladores normales y ahora planea escribir un programa para felicitarlo. El formato es: ¡ happy new year, ${username}! . Pero aquí está la mala suerte: el teclado se bloquea y no le permite ingresar muchos caracteres latinos. Después de examinar el defecto, los elfos hicieron una observación interesante de que, de lo que funciona, puedes sumar un Snowing day . La fuente de salida se puede seleccionar a su discreción.

Entonces, en la entrada, una serie de cadenas no vacías (el nombre no puede estar vacío). Se requiere escribir un programa usando solo caracteres latinos: S , n , o , w , i , g , d , a , y (un total de 9 caracteres, uno de los cuales es mayúscula). ¡El programa debe ir alrededor de la matriz aprobada y generar la frase happy new year, ${username}! para cada nombre happy new year, ${username}! usando algún tipo de fuente de salida: alert , console.log o lo que se te ocurra. Bueno, sería bueno no contaminar el contexto global.


Solución habitual


Si no inventas nada, entonces todo es muy simple:


 function happy(users) { for (let i = 0; i !== users.length; i += 1) { console.log(`happy new year, ${users[i]}!`); } } 

o mejor así:


 function happy(users) { users.forEach(user => console.log(`happy new year, ${user}!`)); } 

Usamos con nuestra matriz, deja que sean usuarios :


 let users = ['John', 'Jack', 'James']; happy(users); // happy new year, John! // happy new year, Jack! // happy new year, James! 

Pero aquí es exagerado: usar solo caracteres permitidos en la implementación latina. Primero trate de sobrellevar su propio problema y luego únase a la discusión.


Decisión entretenida


Impaciente puede ver la solución a continuación en JSFiddle en este momento.


Para resolver el problema, debe deshacerse del exceso de latín en lo siguiente:


  1. La función de palabra clave en una declaración de función .
  2. La palabra clave let (o var ) para declarar variables.
  3. Palabras clave al organizar un ciclo para iterar sobre una matriz pasada.
  4. La formación del texto del mensaje.
  5. Llama a alguna función para generar el resultado.

Las funciones de flecha nos ayudarán fácilmente con el primer problema:


 (arr => el.forEach(/* ... */))(users); 

No prestaremos atención a los nombres de las variables en este momento, ya que podemos cambiarles el nombre fácilmente al final.


Usamos "flechas" con IIFE siempre que se necesite una función o su resultado inmediatamente. Además, las funciones le permiten deshacerse de las directivas let y var de dos maneras:


 (param => /* ... */)(value); ((param = value) => /* ... */)(); 

En ambos casos, declaramos una variable en los parámetros de la función. Solo en el primer caso pasamos el valor cuando se llama a la función, y en el segundo, usamos el parámetro de función predeterminado.


De hecho, los problemas comienzan en el tercer punto. No tenemos suficientes caracteres para el clásico para , do , bucles while, ni para las opciones de recorrido a través de for ... in y for..of , ni para los métodos de matriz para cada , mapa , filtro (donde puede pasar la devolución de llamada). Pero podemos implementar nuestra función de iteración sobre una matriz:


 function iterate(arr, consume) { function iter(i) { if (arr[i]) { consume(arr[i]); iter(++i); } } iter(0); } 

Recursivamente recorremos los elementos hasta que la verificación de la condición actual se caiga. ¿Por qué podemos confiar en la transformación lógica aquí? porque el elemento de nuestra matriz no es una cadena vacía (solo se envuelve falso ), pero cuando salimos de la matriz a través del incremento del índice, quedamos indefinidos (convertidos en falsos ).


Reescribimos la función usando las expresiones de flecha:


 let iterate = (arr, consume) => ( (iter = i => { if (arr[i]) { consume(arr[i]); iter(++i); } }) => iter(0) )(); 

Pero no podemos usar la declaración if , ya que no tenemos el carácter f . Para que nuestra función satisfaga la condición, necesitamos deshacernos de ella:


 let iterate = (arr, consume) => ( (iter = i => arr[i] ? (consume(arr[i]), iter(++i)) : 0) => iter(0) )(); 

El operador ternario y la capacidad de combinar dos expresiones en una a través del operador de coma nos ayudaron en esto. Usaremos esta función más adelante en el diseño de la solución.


El cuarto problema es que, en cualquier caso, necesitamos obtener una cadena con caracteres faltantes. Obviamente, utilizaremos números para representar caracteres. Hay varias opciones:


  • Una función String.fromCharCode que espera que devuelva un número entero y devuelve una cadena creada a partir de la secuencia Unicode especificada.
  • La secuencia de \uhhhh permite generar cualquier carácter Unicode utilizando el código hexadecimal especificado.
  • Formato &#dddd; for html-characters le permite mostrar un símbolo en el documento de la página utilizando el código decimal especificado.
  • La función toString del objeto prototipo Number tiene un parámetro radix adicional: la base del sistema numérico.
  • Tal vez hay algo más ...

Puede excavar en la dirección de las tres primeras opciones, y ahora considere la que probablemente sea la más fácil para esta tarea: Number.prototype.toString . El valor máximo del parámetro radix es 36 (10 dígitos + 26 caracteres latinos en minúsculas):


 let symb = sn => (sn + 9).toString(36); 

Por lo tanto, podemos obtener cualquier carácter latino por el número en el alfabeto, comenzando con 1. La única limitación es que todos los caracteres están en minúsculas. Sí, esto es suficiente para que podamos mostrar el texto en el mensaje, pero no podemos agregar algunos métodos y funciones (lo mismo para cada uno ).


Pero es demasiado temprano para alegrarse, primero debe deshacerse de toString en la entrada de la función. Primero, pasamos al método de la siguiente manera:


 let symb = sn => (sn + 9)['toString'](36); 

Si observa detenidamente, para la cadena toString solo necesitamos dos caracteres: t r : todo lo demás está en la palabra Snowing . Conseguirlos es bastante simple, ya que su orden ya insinúa que es true . Usando conversiones de tipo implícito, podemos obtener esta cadena y los caracteres que necesitamos de la siguiente manera:


 !0+''; // 'true' (!0+'')[0]; // 't' (!0+'')[1]; // 'r' 

Logramos la función de obtener cualquier letra latina:


 let symb = sn => (sn + 9)[(!0+'')[0] + 'oS' + (!0+'')[0] + (!0+'')[1] + 'ing'](36); 

Para obtener palabras de una matriz de números de secuencia de letras usando symb , usamos la función estándar Array.prototype.reduce :


 [1,2,3].reduce((res, sn) => res += symb(sn), ''); // 'abc' 

Sí, eso no nos conviene. Pero en nuestra solución, podemos hacer algo similar con la función iterar :


 let word = chars => (res => (iterate(chars, ch => res += symb(ch)), res))(''); word([1,2,3]); // 'abc' 

Las personas atentas notarán que desarrollamos la función iterar para una serie de cadenas, pero la usamos aquí con números. Es por eso que el índice inicial de nuestro alfabeto es 1, no 0. De lo contrario, un ciclo improvisado terminaría cuando se cumpliera 0 (letra a ).


Para facilitar la asignación de caracteres a sus números de serie, puede obtener un diccionario:


 [...Array(26).keys()].reduce((map, i) => (map[symb(i + 1)] = i + 1, map), {}); // {a: 1, b: 2, c: 3, d: 4, e: 5, …} 

Pero es más sabio hacer aún más simple y escribir la función de la transformación de la palabra inversa en su conjunto:


 let reword = str => str.split('').map(s => parseInt(s, 36) - 9); reword('happy'); // [8,1,16,16,25] reword('new'); // [14,5,23] reword('year'); // [25,5,1,18] 

Completamos la función de formar el mensaje en sí:


 let message = name => word([8,1,16,16,25]) + ' ' + word([14,5,23]) + ' ' + word([25,5,1,18]) + ', ' + name + '!'; 

Queda muy poco para tratar la conclusión del quinto problema. Consola , alerta , confirmación , aviso , innerHTML , document.write vienen a la mente. Pero ninguna de las opciones enumeradas puede ser contactada directamente.


También tuvimos la oportunidad de obtener cualquier palabra usando la función de palabra . Esto significa que podemos llamar a muchas funciones desde objetos accediendo a ellas entre corchetes, como fue el caso con toString .


Dado que utilizamos funciones de flecha, este contexto sigue siendo global (y no hay necesidad de reenviarlo). En cualquier lugar, podemos acceder a muchas de sus funciones a través de una línea:


 this[word([1,12,5,18,20])]('hello'); // alert('hello'); this[word([3,15,14,19,15,12,5])][word([12,15,7])]('hello'); // console.log('hello'); 

Pero para el "dibujo" de this nuevamente nos faltan personajes. Podemos reemplazarlo con Window.self , pero con él es aún peor en términos del alfabeto disponible. Sin embargo, vale la pena prestar atención al objeto de la ventana en sí, cuyo "esquema" es bastante satisfactorio para nosotros, incluso si hubiera sido de cabra, ¡y es mucho más largo!


Por cierto, en la primera versión de la tarea, la frase clave era solo la palabra Snowing , y la window no se podía plegar (debido a la ausencia del carácter d ). El acceso al contexto se basó en uno de los trucos de jsfuck :


 (_ => 0)['constructor']('return this')()['alert']('hello'); 

O también puede acceder a algo en un contexto global directamente:


 (_ => 0)['constructor']('return alert')()('hello'); 

Como puede ver, en los ejemplos todo el latín está en líneas. Aquí creamos una función a partir de una cadena, y tenemos acceso a la Función (constructor) desde la función de flecha desperdiciada. ¡Pero esto es de alguna manera demasiado! ¿Quizás alguien más sabe cómo acceder al contexto en nuestras condiciones?


¡Finalmente, lo juntamos todo! El cuerpo de nuestra función "principal" llamará a iterar para la matriz pasada, y el consumidor generará el resultado de la función de compilación de mensajes ya incorporada. Para el texto del mensaje y los comandos, se utiliza una función de una palabra , que también necesita iterar , y la determinaremos a continuación en los parámetros predeterminados . Así:


 (users => ( (( // firstly we need the iterating function iterate = (array, consume) => ((iter = i => array[i] ? (consume(array[i]), iter(++i)) : 0) => iter(0))(), // then we determine the word-creating function word = chars => (res => (iterate(chars, ch => res += (ch + 9)[(!0+'')[0] + 'oS' + (!0+'')[0] + (!0+'')[1] + 'ing'](36) ), res) )('') ) => iterate(users, name => // using console.log in window for printing out window[word([3,15,14,19,15,12,5])][word([12,15,7])]( word([8,1,16,16,25]) + ' ' + word([14,5,23]) + ' ' + word([25,5,1,18]) + ', ' + name + '!' ) ))() ))(users); 

Cambie el nombre de las variables usando el 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); 

VariableDescripción
_123 {function}La función iterar para iterar sobre los elementos de una matriz.
_12 {function}La función iter local que itera llamadas recursivamente.
nieve {function}Consume la función como devolución de llamada para iterar .
ann {Array<Any>}Parámetro de matriz.
un {Any}Parámetro del elemento de matriz.
wo {function}La palabra funciona para formar palabras.
w {string}Variable local para acumular una cadena en palabra .
_10 {Array<string>}El conjunto original de usuarios.
_1 {string}El usuario de la matriz fuente, su nombre.

Eso es todo Escriba sus ideas y pensamientos sobre esto, ya que hay muchas opciones para hacer algo diferente o no hacer nada.


Conclusión


Es interesante que encontrar una palabra o frase para las condiciones del problema haya resultado ser una prueba real. Quería que fuera baja y no muy sugestiva, y adecuada para una solución más o menos concisa.


La inspiración para esta tarea fue proporcionada por la funcionalidad de JavaScript y el isotérico de 6 caracteres conocidos por muchos. Como se discutió anteriormente en las tareas, esto puede tener varias variaciones sobre el tema, y ​​no es la única solución. Es suficiente con una redacción simple y una frase clave. ¡Nos vemos en el año nuevo!

Source: https://habr.com/ru/post/es433112/


All Articles