Menghibur JavaScript: Hari Salju

Gambar


Tugas lain yang dibuat untuk pemrograman abnormal dalam JavaScript . Kali ini pada kesempatan Tahun Baru 2019 mendatang. Saya harap akan sama menariknya untuk memutuskan betapa menariknya bagi saya untuk memunculkannya. Saya bertanya ingin tahu di bawah kucing. Semua sampanye dan semua Selamat!


Tugas sebelumnya:



Kata-kata


Selama setahun terakhir, Santa Claus telah mengumpulkan daftar nama pengembang normal dan sekarang berencana untuk menulis sebuah program untuk selamat. Formatnya adalah: happy new year, ${username}! . Tapi ini nasib buruk: keyboard macet dan tidak memungkinkan Anda memasukkan banyak karakter Latin. Setelah memeriksa cacat itu, para elf membuat pengamatan yang menarik bahwa dari apa lagi yang berhasil, Anda dapat menambahkan Snowing day . Sumber output dapat dipilih sesuai kebijaksanaan Anda.

Jadi, pada input - beberapa larik string yang tidak kosong (nama tidak boleh kosong). Diperlukan untuk menulis sebuah program hanya menggunakan karakter Latin: S , n , o , w , i , g , d , a , y (total 9 karakter, salah satunya adalah huruf besar). Program harus berkeliling array yang lulus dan output frase happy new year, ${username}! untuk setiap nama happy new year, ${username}! menggunakan beberapa jenis sumber output: alert , console.log atau apa pun yang terlintas dalam pikiran. Yah, alangkah baiknya tidak mencemari konteks global.


Solusi kebiasaan


Jika Anda tidak menemukan apa pun, maka semuanya sangat sederhana:


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

atau lebih baik:


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

Kami menggunakan dengan array kami, biarkan itu menjadi pengguna :


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

Tapi di sini tidak masuk akal: hanya menggunakan karakter yang diizinkan dalam implementasi Latin. Cobalah untuk mengatasi sendiri terlebih dahulu, dan kemudian bergabung dengan diskusi.


Keputusan yang menghibur


Tidak sabar dapat melihat solusi di bawah ini di JSFiddle sekarang.


Untuk mengatasi masalah, Anda harus menyingkirkan kelebihan Latin dalam hal berikut:


  1. Fungsi kata kunci dalam deklarasi fungsi .
  2. Kata kunci let (atau var ) untuk mendeklarasikan variabel.
  3. Kata kunci saat mengatur loop untuk beralih pada array yang diteruskan.
  4. Pembentukan teks pesan.
  5. Panggil beberapa fungsi untuk menampilkan hasilnya.

Fungsi panah akan dengan mudah membantu kami dengan masalah pertama:


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

Kami tidak akan memperhatikan nama variabel sekarang, karena kami dapat dengan mudah mengganti nama mereka di bagian paling akhir.


Kami menggunakan "panah" dengan IIFE di mana pun suatu fungsi dibutuhkan atau hasilnya segera. Selain itu, fungsi memungkinkan Anda untuk menyingkirkan arahan let dan var dalam dua cara:


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

Dalam kedua kasus, kami mendeklarasikan variabel dalam parameter fungsi. Hanya dalam kasus pertama kita memberikan nilai ketika fungsi dipanggil, dan dalam kasus kedua - kita menggunakan parameter fungsi default.


Memang, masalah dimulai pada poin ketiga . Kami tidak memiliki cukup karakter untuk klasik untuk , lakukan , saat loop, atau untuk opsi traversal melalui untuk..dalam dan untuk..dari , tidak juga untuk metode array untukMasing-masing , peta , filter (di mana Anda dapat melewati panggilan balik). Tapi kita bisa mengimplementasikan fungsi iterasi kita di atas sebuah array:


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

Kami melintasi elemen secara rekursif sampai verifikasi kondisi saat ini jatuh. Mengapa kita dapat mengandalkan transformasi logis di sini? karena elemen array kita bukanlah string kosong (hanya itu membungkus false ), tetapi ketika kita keluar dari array melalui kenaikan indeks, kita mendapatkan undefined (dilemparkan ke false ).


Kami menulis ulang fungsi menggunakan ekspresi panah:


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

Tetapi kita tidak dapat menggunakan pernyataan if , karena kita tidak memiliki karakter f . Agar fungsi kita memenuhi syarat, kita harus menyingkirkannya:


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

Operator ternary dan kemampuan untuk menggabungkan dua ekspresi menjadi satu melalui operator koma membantu kami dalam hal ini. Kami akan menggunakan fungsi ini nanti dalam tata letak solusi.


Masalah keempat adalah bahwa dalam hal apa pun kita perlu mendapatkan string dengan karakter yang hilang. Jelas, kami akan menggunakan angka untuk mewakili karakter. Ada beberapa opsi:


  • Fungsi String.fromCharCode yang mengharapkan bilangan bulat untuk mengembalikan dan mengembalikan string yang dibuat dari urutan Unicode yang ditentukan.
  • Urutan \uhhhh memungkinkan \uhhhh untuk mengeluarkan setiap karakter Unicode menggunakan kode heksadesimal yang ditentukan.
  • Format &#dddd; untuk html-karakter memungkinkan Anda untuk menampilkan simbol dalam dokumen halaman menggunakan kode desimal yang ditentukan.
  • Fungsi toString dari objek prototipe Number memiliki parameter radix tambahan - dasar sistem bilangan.
  • Mungkin ada sesuatu yang lain ...

Anda dapat menggali diri Anda ke arah tiga opsi pertama, dan sekarang pertimbangkan yang paling mudah untuk tugas ini: Number.prototype.toString . Nilai maksimum dari parameter radix adalah 36 (10 digit + 26 huruf Latin kecil):


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

Jadi, kita bisa mendapatkan karakter Latin apa pun dengan angka dalam alfabet, dimulai dengan 1. Satu-satunya batasan adalah semua karakter huruf kecil. Ya, ini cukup bagi kami untuk menampilkan teks dalam pesan, tetapi kami tidak dapat menambahkan beberapa metode dan fungsi ( forEach yang sama).


Tapi terlalu dini untuk bersukacita, pertama Anda harus menyingkirkan toString di entri fungsi. Pertama, kita beralih ke metode sebagai berikut:


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

Jika Anda perhatikan dengan teliti, maka untuk string toString kita hanya perlu dua karakter: t dan r : semua sisanya ada dalam kata Snowing . Mendapatkan mereka cukup sederhana, karena pesanan mereka sudah mengisyaratkan true . Menggunakan konversi tipe implisit, kita bisa mendapatkan string ini dan karakter yang kita butuhkan sebagai berikut:


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

Kami mencapai fungsi memperoleh huruf Latin:


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

Untuk mendapatkan kata-kata dari array nomor urut huruf menggunakan symb , kami menggunakan fungsi standar Array.prototype.reduce :


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

Ya, itu tidak cocok untuk kita. Tetapi dalam solusi kami, kami dapat melakukan sesuatu yang mirip dengan fungsi iterate :


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

Orang yang penuh perhatian akan mencatat bahwa kami mengembangkan fungsi iterate untuk serangkaian string, tetapi kami menggunakannya di sini dengan angka. Itulah sebabnya indeks awal alfabet kami adalah 1, bukan 0. Jika tidak, siklus dadakan akan berakhir ketika bertemu 0 (huruf a ).


Untuk memudahkan pemetaan karakter ke nomor seri mereka, Anda bisa mendapatkan kamus:


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

Tetapi lebih bijaksana untuk melakukan bahkan lebih sederhana dan menulis fungsi transformasi kata terbalik secara keseluruhan:


 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] 

Kami melengkapi fungsi membentuk pesan itu sendiri:


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

Hanya ada sedikit yang tersisa untuk berurusan dengan kesimpulan dari masalah kelima . Konsol , waspada , konfirmasi , prompt , innerHTML , document.write datang ke pikiran. Tetapi tidak ada opsi yang terdaftar yang dapat dijangkau secara langsung.


Kami juga mendapat kesempatan untuk mendapatkan kata apa saja menggunakan fungsi kata . Ini berarti bahwa kita dapat memanggil banyak fungsi dari objek dengan mengaksesnya melalui tanda kurung siku, seperti halnya dengan toString .


Karena kami menggunakan fungsi panah, konteks ini tetap bersifat global (dan tidak perlu meneruskannya). Di mana saja, kita dapat mengakses banyak fungsinya melalui jalur:


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

Tetapi untuk "gambar" this kita kembali kekurangan karakter. Kita bisa menggantinya dengan Window.self , tetapi dengan itu bahkan lebih buruk dalam hal alfabet yang tersedia. Namun, perlu memperhatikan objek jendela itu sendiri, "garis besar" yang cukup memuaskan bagi kita, bahkan jika itu adalah kambing, dan jauh lebih lama!


Ngomong-ngomong, dalam versi pertama tugas, frasa kunci hanya kata Snowing , dan window tidak dapat dilipat (karena tidak adanya karakter d ). Akses ke konteks didasarkan pada salah satu trik jsfuck :


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

Atau Anda juga dapat mengakses sesuatu dalam konteks global secara langsung:


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

Seperti yang Anda lihat, dalam contoh-contoh seluruh bahasa Latin ada dalam barisan. Di sini kita membuat fungsi dari string, dan kita mendapatkan akses ke Function (constructor) dari fungsi panah yang terbuang. Tapi ini entah bagaimana terlalu banyak! Mungkin orang lain tahu cara mengakses konteks dalam kondisi kita?


Akhirnya, kami mengumpulkan semuanya! Badan fungsi "utama" kami akan memanggil iterate untuk array yang diteruskan, dan konsumen akan menampilkan hasil dari fungsi kompilasi pesan yang sudah terpasang. Untuk teks pesan dan perintah, satu fungsi kata digunakan, yang juga membutuhkan iterate , dan kami akan menentukannya berikutnya dalam parameter default . Seperti ini:


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

Ganti nama variabel menggunakan alfabet yang diizinkan:


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

VariabelDeskripsi
_123 {function}Fungsi iterate untuk beralih pada elemen-elemen array.
_12 {function}Fungsi iter lokal yang mengulang panggilan secara rekursif.
salju {function}Mengkonsumsi fungsi sebagai panggilan balik untuk iterate .
ann {Array<Any>}Parameter array.
sebuah {Any}Parameter elemen array.
wo {function}Kata tersebut berfungsi untuk membentuk kata-kata.
w {string}Variabel lokal untuk mengakumulasi string dalam kata .
_10 {Array<string>}Array asli pengguna.
_1 {string}Pengguna dari array sumber, namanya.

Itu saja. Tulis ide dan pemikiran Anda tentang ini, karena ada banyak pilihan untuk melakukan sesuatu yang berbeda atau tidak sama sekali.


Kesimpulan


Menariknya, untuk menghasilkan kata atau frasa untuk kondisi masalah ternyata menjadi ujian nyata. Saya ingin dia pendek dan tidak terlalu sugestif, dan cocok untuk solusi yang kurang lebih ringkas.


Inspirasi untuk tugas ini disediakan oleh fungsionalitas JavaScript dan isoterik 6 karakter yang dikenal banyak orang. Seperti tugas yang dibahas sebelumnya, ini mungkin memiliki beberapa variasi pada topik, dan bukan satu-satunya solusi. Cukup dengan kata-kata sederhana dan frase kunci. Sampai jumpa di tahun baru!

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


All Articles