楽しいJavaScript:雪の日

画像







JavaScriptでの異常なプログラミングのための別の不自然なタスク。 今度は2019年の新年の機会に。 私が思い付くのがどれほど面白かったのかを判断することも同じくらい面白いと思います。 猫の下に興味があります。 すべてのシャンパンとすべての幸せ!







前のタスク:









文言



過去1年間、サンタクロースは通常の開発者の名前の適切なリストを作成し、現在はお祝いのプログラムを作成する予定です。 形式は次のとおりです。 happy new year, ${username}!



。 しかし、残念なことに、キーボードがクラッシュし、多くのラテン文字を入力できなくなります。 欠陥を調べた後、エルフは、他の機能から何がSnowing day



かという興味深い観察をSnowing day



ました。 出力ソースは、あなたの裁量で選択できます。

したがって、入力時-空でない文字列の配列(名前は空にできません)。 ラテン文字S



n



o



w



i



g



d



a



y



のみを使用してプログラムを作成する必要があります(合計9文字、そのうちの1つは大文字)。 プログラムは、渡された配列を迂回して、各名前のフレーズ「 happy new year, ${username}!



」を出力する必要がありますhappy new year, ${username}!



任意の出力ソースを使用します: alertconsole.logまたは何でも思い浮かびます。 まあ、グローバルコンテキストを汚染しないのはいいことです。







習慣的な解決策



何も発明しない場合、すべてが非常に簡単です:







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





またはそれ以上:







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





私たちは配列で使用し、それをユーザーにします







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





しかし、ここでは、ラテン語の実装で許可された文字のみを使用するという、非常にフェッチングされています。 最初に自分で対処してから、ディスカッションに参加してください。







面白い決定



せっかちな人は、現在JSFiddleで以下の解決策を見ることができます。







この問題を解決するには、次の余分なラテン語を取り除く必要があります。







  1. 関数宣言内のキーワード関数
  2. 変数を宣言するためのlet (またはvar )キーワード。
  3. 渡された配列を反復処理するループを編成するときのキーワード。
  4. メッセージのテキストの形成。
  5. 結果を出力する関数を呼び出します。


矢印関数は、最初の問題を簡単に解決します。







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





最後に変数名を簡単に変更できるため、今は変数名に注意を払いません。







関数が必要な場合、またはその結果を即座にIIFEで「矢印」を使用します。 さらに、関数を使用すると、次の2つの方法でletおよびvarディレクティブを削除できます。







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





どちらの場合も、関数パラメーターで変数を宣言します。 最初の場合のみ、関数が呼び出されたときに値を渡し、2番目の場合は、デフォルトの関数パラメーターを使用します。







実際、問題は3番目の点から始まります。 古典的なfordowhileループ、 for..inおよびfor..ofを介した走査オプション、配列メソッドforEachmapfilter (コールバックを渡すことができる)に十分な文字がありません。 ただし、配列に対して反復関数を実装できます。







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





現在の状態の検証が終了するまで、要素を再帰的に走査します。 ここで論理変換に頼ることができるのはなぜですか? 配列の要素は空の文字列ではないため( falseのみになります )、インデックスの増分で配列を終了すると、 未定義になりますfalseにキャストされます )。







矢印式を使用して関数を書き直します。







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





ただし、文字f



がないため、ifステートメントは使用できません。 関数が条件を満たすためには、それを取り除く必要があります。







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





三項演算子と、 コンマ演算子を介して2つの式を1つに結合する機能は、この点で役立ちました。 この関数は、ソリューションのレイアウトで後ほど使用します。







4番目の問題は、いずれの場合でも、欠落文字のある文字列を取得する必要があるという事実に関連しています。 明らかに、数字を使用して文字を表します。 いくつかのオプションがあります:









最初の3つのオプションの方向に向かって自分自身を掘り下げて、おそらくこのタスクでおそらく最も簡単なNumber.prototype.toStringを検討してください基数パラメーターの最大値は36(10桁+小文字のラテン文字26)です。







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





したがって、1から始まるアルファベットの数字でラテン文字を取得できます。唯一の制限は、すべての文字が小文字であることです。 はい、メッセージにテキストを表示するにはこれで十分ですが、いくつかのメソッドと関数を追加することはできません(同じforEach )。







しかし、喜ぶには時期尚早です。まず、関数エントリのtoStringを削除する必要があります。 最初に、次の方法を使用します。







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





よく見ると、文字列toString



t



r



2つの文字だけが必要です。残りはすべてSnowing



という単語に含まれています。 それらの順序はすでにtrue



暗示しているため、それらを取得するのは非常に簡単true



。 暗黙的な型変換を使用すると、この文字列と必要な文字を次のように取得できます。







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





ラテン文字を取得する機能を実現します。







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





symbを使用して文字シーケンス番号の配列から単語を取得するには、標準のArray.prototype.reduce関数を使用します。







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





はい、それは私たちには合いません。 しかし、このソリューションでは、 反復関数を使用して同様のことができます。







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





気配りのある人は、文字列の配列に対して反復関数を開発したことに気付くでしょうが、ここでは数値で使用しています。 それが、アルファベットの初期インデックスが0ではなく1である理由です。それ以外の場合、即興サイクルは0(文字a



)に達すると終了します。







文字をシリアル番号に簡単にマッピングするために、辞書を取得できます。







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





ただし、さらに簡単に実行して、逆ワード変換の機能全体を記述する方が賢明です。







 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]
      
      





メッセージ自体を形成する機能を完了します。







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





5番目の問題の結論に対処するために残っていることはほとんどありません。 ConsolealertconfirmpromptinnerHTMLdocument.writeが思い浮かびます。 ただし、リストされているオプションに直接アクセスすることはできません。







また、 word関数を使用して任意の単語を取得する機会を得ました。 これは、 toStringの場合のように、角括弧を介してアクセスすることにより、オブジェクトから多くの関数を呼び出すことができることを意味します。







矢印関数を使用する場合、 thisコンテキストはグローバルなままです(転送する必要はありません)。 どこからでも、次の行を介して多くの機能にアクセスできます。







 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');
      
      





しかし、これを「描く」ために、再びキャラクターが不足しています。 これをWindow.selfに置き換えることができますが、使用可能なアルファベットに関してはさらに悪いです。 ただし、 ウィンドウオブジェクト自体に注意を払う価値があります。ウィンドウオブジェクト自体の「概要」は、それがヤギであり、桁違いに長いものであったとしても、非常に満足のいくものです。







ちなみに、タスクの最初のバージョンでは、キーフレーズはSnowing



という単語のみで、 window



は( d



文字がないため)折りたたむことができませんでした。 コンテキストへのアクセスは、 jsfuckトリックの1つに基づいていました。







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





または、グローバルコンテキストの何かに直接アクセスすることもできます。







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





ご覧のとおり、例ではラテン語全体が行になっています。 ここでは、文字列から関数を作成し、無駄な矢印関数から関数 (コンストラクター)にアクセスします。 しかし、これはどういうわけか多すぎる! たぶん他の誰かが私たちの条件でコンテキストにアクセスする方法を知っていますか?







最後に、すべてをまとめました! 「main」関数の本体は、渡された配列に対して繰り返しを呼び出し、コンシューマーは、既に組み込まれているメッセージコンパイル関数の結果を出力します。 メッセージとコマンドのテキストには、1つの単語関数が使用されますが、これもiterateが必要であり、次にデフォルトパラメータで決定します 。 このように:







 (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);
      
      





許可されているアルファベットを使用して変数の名前を変更します。







 (_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);
      
      





可変 説明
_123 {function}



配列の要素を反復処理する反復関数。
_12 {function}



反復するローカルiter関数は再帰的に呼び出します。
{function}



iterateのコールバックとして関数を使用します。
アン {Array<Any>}



配列パラメーター。
{Any}



配列要素パラメーター。
{function}



単語を形成する単語関数。
w {string}



wordの文字列を蓄積するローカル変数。
_10 {Array<string>}



ユーザーの元の配列。
_1 {string}



ソース配列からのユーザー、彼の名前。


以上です。 これについてあなたの考えや考えを書いてください。何か違うことをするか、まったくしないかの選択肢がたくさんあります。







おわりに



問題の状態を表す単語やフレーズを思い付くことが実際のテストであることが判明したのは興味深いことです。 私は彼女が短く、あまり示唆的ではなく、多かれ少なかれ簡潔なソリューションに適していることを望んでいました。







このタスクのインスピレーションは、JavaScriptの機能と多くの人に知られている6文字アイソテリックによって提供されました。 前に説明したタスクのように、これにはトピックに関するいくつかのバリエーションがあり、唯一のソリューションではありません。 簡単な言葉遣いとキーフレーズを考え出すだけで十分です。 新年お会いしましょう!








All Articles