
Eine weitere erfundene Aufgabe für abnormale Programmierung in JavaScript . Diesmal anlässlich des kommenden Neujahrs 2019. Ich hoffe, es wird genauso interessant sein zu entscheiden, wie interessant es für mich war, es zu entwickeln. Ich frage neugierig unter Katze. Alles Champagner und alles ein Happy!
Bisherige Aufgaben:
Im vergangenen Jahr hat der Weihnachtsmann eine anständige Liste mit Namen normaler Entwickler zusammengestellt und plant nun, ein Glückwunschprogramm zu schreiben. Das Format lautet: happy new year, ${username}!
. Aber hier ist das Pech: Die Tastatur stürzt ab und Sie können nicht viele lateinische Zeichen eingeben. Nach der Untersuchung des Defekts machten die Elfen eine interessante Beobachtung, dass man aus dem, was sonst noch funktioniert, einen Snowing day
addieren kann. Die Ausgabequelle kann nach eigenem Ermessen ausgewählt werden.
Also, am Eingang - ein Array von nicht leeren Zeichenfolgen (der Name darf nicht leer sein). Es ist erforderlich, ein Programm nur mit lateinischen Zeichen zu schreiben: S
, n
, o
, w
, i
, g
, d
, a
, y
(insgesamt 9 Zeichen, von denen eines in Großbuchstaben geschrieben ist). Das Programm sollte das übergebene Array umgehen und die Phrase happy new year, ${username}!
ausgeben happy new year, ${username}!
für jeden Namen happy new year, ${username}!
Verwenden einer Ausgabequelle: alert , console.log oder was auch immer in den Sinn kommt. Nun, es wäre schön, den globalen Kontext nicht zu verschmutzen.
Gewohnheitsmäßige Lösung
Wenn Sie nichts erfinden, ist alles sehr einfach:
function happy(users) { for (let i = 0; i !== users.length; i += 1) { console.log(`happy new year, ${users[i]}!`); } }
oder besser so:
function happy(users) { users.forEach(user => console.log(`happy new year, ${user}!`)); }
Wir verwenden mit unserem Array, lassen Sie es Benutzer sein :
let users = ['John', 'Jack', 'James']; happy(users);
Aber hier ist es weit hergeholt: nur erlaubte Zeichen in der lateinischen Implementierung zu verwenden. Versuchen Sie zuerst, selbst fertig zu werden, und beteiligen Sie sich dann an der Diskussion.
Unterhaltsame Entscheidung
Ungeduldig kann die unten stehende Lösung jetzt in JSFiddle sehen .
Um das Problem zu lösen, müssen Sie überschüssiges Latein im Folgenden entfernen:
- Die Schlüsselwortfunktion in einer Funktionsdeklaration .
- Das Schlüsselwort let (oder var ) zum Deklarieren von Variablen.
- Schlüsselwörter beim Organisieren einer Schleife zum Durchlaufen eines übergebenen Arrays.
- Die Bildung des Textes der Nachricht.
- Rufen Sie eine Funktion auf, um das Ergebnis auszugeben.
Pfeilfunktionen helfen uns leicht beim ersten Problem:
(arr => el.forEach())(users);
Wir werden Variablennamen im Moment nicht beachten, da wir sie ganz einfach ganz am Ende umbenennen können.
Wir verwenden "Pfeile" mit IIFE überall dort, wo eine Funktion benötigt wird oder deren Ergebnis sofort. Darüber hinaus können Sie mit Funktionen let- und var- Direktiven auf zwei Arten entfernen:
(param => )(value); ((param = value) => )();
In beiden Fällen deklarieren wir eine Variable in den Funktionsparametern. Nur im ersten Fall übergeben wir den Wert, wenn die Funktion aufgerufen wird, und im zweiten Fall verwenden wir den Standardfunktionsparameter.
In der Tat beginnen die Probleme mit dem dritten Punkt. Wir haben nicht genügend Zeichen für die klassischen for , do , while- Schleifen oder für die Durchquerungsoptionen für for..in und for..of sowie für die Array-Methoden forEach , map , filter (wo Sie den Rückruf übergeben können). Wir können unsere Iterationsfunktion jedoch über ein Array implementieren:
function iterate(arr, consume) { function iter(i) { if (arr[i]) { consume(arr[i]); iter(++i); } } iter(0); }
Wir durchlaufen die Elemente rekursiv, bis die Überprüfung des aktuellen Zustands abfällt. Warum können wir uns hier auf logische Transformation verlassen? Da das Element unseres Arrays keine leere Zeichenfolge ist (nur es wird false umbrochen), werden wir undefiniert (in false umgewandelt ), wenn wir das Array durch das Inkrement des Index verlassen.
Wir schreiben die Funktion mit den Pfeilausdrücken neu:
let iterate = (arr, consume) => ( (iter = i => { if (arr[i]) { consume(arr[i]); iter(++i); } }) => iter(0) )();
Wir können die if-Anweisung jedoch nicht verwenden, da wir das Zeichen f
nicht haben. Damit unsere Funktion die Bedingung erfüllt, müssen wir sie loswerden:
let iterate = (arr, consume) => ( (iter = i => arr[i] ? (consume(arr[i]), iter(++i)) : 0) => iter(0) )();
Der ternäre Operator und die Möglichkeit, zwei Ausdrücke durch den Komma-Operator zu einem zu kombinieren, haben uns dabei geholfen. Wir werden diese Funktion später im Layout der Lösung verwenden.
Das vierte Problem ist, dass wir auf jeden Fall eine Zeichenfolge mit fehlenden Zeichen benötigen. Natürlich werden wir Zahlen verwenden, um Zeichen darzustellen. Es gibt mehrere Möglichkeiten:
- Eine String.fromCharCode- Funktion, die erwartet, dass die Ganzzahl zurückgegeben wird, und eine Zeichenfolge zurückgibt, die aus der angegebenen Unicode-Sequenz erstellt wurde.
- Mit der
\uhhhh
Sequenz \uhhhh
können \uhhhh
jedes Unicode-Zeichen mit dem angegebenen Hexadezimalcode ausgeben. - Format
&#dddd;
Mit HTML-Zeichen können Sie ein Symbol im Seitendokument mit dem angegebenen Dezimalcode anzeigen. - Die toString- Funktion des Number- Prototypobjekts verfügt über einen zusätzlichen Radix- Parameter - die Basis des Zahlensystems.
- Vielleicht gibt es noch etwas ...
Sie können sich in Richtung der ersten drei Optionen graben und jetzt die wahrscheinlich einfachste für diese Aufgabe in Betracht ziehen : Number.prototype.toString . Der Maximalwert des Radix- Parameters beträgt 36 (10 Ziffern + 26 lateinische Kleinbuchstaben):
let symb = sn => (sn + 9).toString(36);
Somit können wir jedes lateinische Zeichen anhand der Zahl im Alphabet erhalten, beginnend mit 1. Die einzige Einschränkung besteht darin, dass alle Zeichen in Kleinbuchstaben geschrieben sind. Ja, dies reicht aus, um den Text in der Nachricht anzuzeigen, aber wir können einige Methoden und Funktionen nicht hinzufügen (dasselbe gilt für jede ).
Aber es ist zu früh, um sich zu freuen. Zuerst müssen Sie toString im Funktionseintrag entfernen. Zunächst wenden wir uns der Methode wie folgt zu:
let symb = sn => (sn + 9)['toString'](36);
Wenn Sie genau hinschauen, benötigen wir für die Zeichenfolge toString
nur zwei Zeichen: t
und r
: Der Rest steht im Wort Snowing
. Sie zu bekommen ist ziemlich einfach, da ihre Reihenfolge bereits auf true
hinweist. Mit impliziten Typkonvertierungen können wir diese Zeichenfolge und die benötigten Zeichen wie folgt abrufen:
!0+'';
Wir erreichen die Funktion, einen lateinischen Buchstaben zu erhalten:
let symb = sn => (sn + 9)[(!0+'')[0] + 'oS' + (!0+'')[0] + (!0+'')[1] + 'ing'](36);
Um mit symb Wörter aus einem Array von Buchstabenfolgenummern abzurufen , verwenden wir die Standardfunktion Array.prototype.reduce :
[1,2,3].reduce((res, sn) => res += symb(sn), '');
Ja, das passt nicht zu uns. In unserer Lösung können wir jedoch mit der Iterationsfunktion etwas Ähnliches tun:
let word = chars => (res => (iterate(chars, ch => res += symb(ch)), res))(''); word([1,2,3]);
Aufmerksame Leute werden feststellen, dass wir die Iterationsfunktion für ein Array von Zeichenfolgen entwickelt haben, aber wir verwenden sie hier mit Zahlen. Aus diesem Grund ist der Anfangsindex unseres Alphabets 1 und nicht 0. Andernfalls würde ein spontaner Zyklus enden, wenn er 0 (Buchstabe a
) erreicht.
Um die Zuordnung von Zeichen zu ihren Seriennummern zu vereinfachen, können Sie ein Wörterbuch herunterladen:
[...Array(26).keys()].reduce((map, i) => (map[symb(i + 1)] = i + 1, map), {});
Es ist jedoch klüger, noch einfacher zu arbeiten und die Funktion der inversen Transformation von Wörtern als Ganzes zu schreiben:
let reword = str => str.split('').map(s => parseInt(s, 36) - 9); reword('happy');
Wir vervollständigen die Funktion der Bildung der Nachricht selbst:
let message = name => word([8,1,16,16,25]) + ' ' + word([14,5,23]) + ' ' + word([25,5,1,18]) + ', ' + name + '!';
Es gibt nur noch sehr wenig zu tun, um das fünfte Problem zu lösen. Konsole , Alarm , Bestätigung , Eingabeaufforderung , innerHTML , document.write kommen in den Sinn. Keine der aufgeführten Optionen kann jedoch direkt erreicht werden.
Wir hatten auch die Möglichkeit, jedes Wort mit der Wortfunktion zu erhalten . Dies bedeutet, dass wir viele Funktionen von Objekten aufrufen können, indem wir über eckige Klammern auf sie zugreifen, wie dies bei toString der Fall war.
Da wir Pfeilfunktionen verwenden, bleibt dieser Kontext global (und es besteht keine Notwendigkeit, ihn weiterzuleiten). An jedem Ort können wir über eine Leitung auf viele seiner Funktionen zugreifen:
this[word([1,12,5,18,20])]('hello');
Aber für das "Zeichnen" fehlen uns wieder Charaktere. Wir können es durch Window.self ersetzen, aber damit ist es in Bezug auf das verfügbare Alphabet noch schlimmer. Es lohnt sich jedoch, auf das Fensterobjekt selbst zu achten, dessen "Umriss" für uns durchaus zufriedenstellend ist, selbst wenn es eine Ziege gewesen wäre und viel länger ist!
Übrigens war in der ersten Version der Aufgabe die Schlüsselphrase nur das Wort Snowing
, und das window
konnte nicht gefaltet werden (aufgrund des Fehlens des Zeichens d
). Der Zugriff auf den Kontext basierte auf einem der jsfuck- Tricks:
(_ => 0)['constructor']('return this')()['alert']('hello');
Oder Sie können auch direkt auf etwas in einem globalen Kontext zugreifen:
(_ => 0)['constructor']('return alert')()('hello');
Wie Sie sehen können, ist in den Beispielen das gesamte Latein in Zeilen. Hier erstellen wir eine Funktion aus einer Zeichenfolge und erhalten über die Funktion für verschwendete Pfeile Zugriff auf die Funktion (Konstruktor). Aber das ist irgendwie zu viel! Vielleicht weiß jemand anderes, wie wir unter unseren Bedingungen auf den Kontext zugreifen können?
Endlich haben wir alles zusammengestellt! Der Hauptteil unserer "Haupt" -Funktion ruft Iterate für das übergebene Array auf, und der Consumer gibt das Ergebnis der bereits integrierten Nachrichtenkompilierungsfunktion aus. Für den Text der Nachricht und der Befehle wird eine Wortfunktion verwendet, die ebenfalls iteriert werden muss , und wir werden sie als nächstes in den Standardparametern bestimmen . So:
(users => ( (( // firstly we need the iterating function iterate = (array, consume) => ((iter = i => array[i] ? (consume(array[i]), iter(++i)) : 0) => iter(0))(),
Benennen Sie die Variablen mit dem zulässigen Alphabet um:
(_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);
Variable | Beschreibung |
---|
_123 {function} | Die Iterationsfunktion zum Iterieren über die Elemente eines Arrays. |
_12 {function} | Die lokale Iterationsfunktion, die Aufrufe rekursiv iteriert . |
Schnee {function} | Funktion als Rückruf für Iteration verwenden . |
ann {Array<Any>} | Array-Parameter. |
ein {Any} | Array-Elementparameter. |
wo {function} | Die Wortfunktion zur Bildung von Wörtern. |
w {string} | Lokale Variable zum Akkumulieren einer Zeichenfolge in Word . |
_10 {Array<string>} | Das ursprüngliche Array von Benutzern. |
_1 {string} | Der Benutzer aus dem Quell-Array, sein Name. |
Das ist alles. Schreiben Sie Ihre Ideen und Gedanken dazu auf, da es viele Möglichkeiten gibt, etwas anders oder gar nicht zu tun.
Fazit
Interessanterweise erwies es sich als echter Test, ein Wort oder eine Phrase für die Bedingungen des Problems zu finden. Ich wollte, dass sie klein und nicht sehr suggestiv ist und sich für eine mehr oder weniger prägnante Lösung eignet.
Die Inspiration für diese Aufgabe war die Funktionalität von JavaScript und die Isoterik von 6 Zeichen , die vielen bekannt sind. Wie bereits erläutert, kann dies verschiedene Variationen des Themas aufweisen und ist nicht die einzige Lösung. Es reicht aus, einen einfachen Wortlaut und eine Schlüsselphrase zu finden. Wir sehen uns im neuen Jahr!