
Pada konferensi reguler untuk
para pengembang JavaScript
HolyJS yang diadakan pada 24-25 Mei di St. Petersburg, stan perusahaan kami menawarkan kepada setiap orang tugas-tugas baru. Kali ini ada 3 dari mereka! Tugas diberikan pada gilirannya, dan untuk solusi masing-masing berikutnya lambang (JS Brave> JS Adept> JS Master) diandalkan, yang berfungsi sebagai motivasi yang baik untuk tidak berhenti. Secara total, kami telah mengumpulkan sekitar
900 jawaban dan sedang terburu-buru untuk membagikan analisis solusi yang paling populer dan unik.
Tes yang diusulkan diharapkan dari keberanian dan pemahaman tentang "fitur" dasar bahasa, dan kesadaran akan fitur baru ECMAScript 2019 (pada kenyataannya, yang terakhir tidak diperlukan). Penting bahwa tugas-tugas ini bukan untuk wawancara, tidak praktis, dan dipikirkan hanya untuk bersenang-senang.
Tugas 1 ~ Ekspresi Hitung Mundur
Apa yang akan mengembalikan ekspresi? Susun ulang setiap karakter tunggal menjadi
- ekspresi kembali 2
- ekspresi kembali 1
+(_ => [,,~1])().length
Selain itu : apakah mungkin untuk mendapatkan 0 dengan permutasi?
Apa yang akan mengembalikan ekspresi?
Tidak lama untuk berpikir di sini: ekspresi akan kembali
3 . Kami memiliki fungsi anonim yang hanya mengembalikan array dari tiga elemen, dua yang pertama kosong. Panggilan fungsi memberi kita array ini, kita mengambil panjang dari itu, dan operator unary plus tidak menyelesaikan apa pun.
Ekspresi mengembalikan 2
Perbaikan cepat tampaknya mengurangi jumlah elemen dalam array yang dikembalikan. Untuk melakukan ini, cukup buang satu koma:
[,~1].length
Tidak mungkin untuk membuang simbol begitu saja: itu perlu disusun ulang ke tempat lain dalam ekspresi asli. Tapi kita tahu bahwa properti
panjang array memperhitungkan elemen kosong yang tercantum dalam literal array, kecuali untuk
satu kasus :
Jika suatu elemen dielusi pada akhir array, elemen itu tidak berkontribusi pada panjang Array.
Yaitu, jika elemen kosong di akhir array, maka diabaikan:
[,10,]
Dengan demikian, ekspresi yang dikoreksi terlihat seperti ini:
+(_ => [,~1,])().length
Apakah ada opsi lain untuk menghilangkan koma ini? Atau melaju.
Ekspresi mengembalikan 1
Tidak mungkin lagi mendapatkannya dengan mengurangi ukuran array, karena Anda harus melakukan setidaknya dua permutasi. Harus mencari opsi lain.
Fungsi itu sendiri mengisyaratkan keputusan. Ingatlah bahwa fungsi dalam JavaScript adalah objek yang memiliki properti dan metode sendiri. Dan salah satu properti
juga panjang , yang menentukan jumlah argumen yang diharapkan oleh fungsi. Dalam kasus kami, fungsi memiliki satu argumen tunggal (
garis bawah ) - apa yang Anda butuhkan!
Agar
panjang dapat diambil dari suatu fungsi, bukan dari array, Anda harus berhenti memanggilnya melalui tanda kurung. Menjadi jelas bahwa salah satu dari tanda kurung ini adalah pesaing untuk permutasi. Dan beberapa pilihan muncul di pikiran:
+((_ => [,,~1])).length
Mungkin ada hal lain? Atau level selanjutnya.
Ekspresi mengembalikan 0
Dalam tugas tambahan, jumlah permutasi tidak terbatas. Tapi kita bisa berusaha memenuhinya dengan membuat gerakan minimal.
Mengembangkan pengalaman sebelumnya dengan
panjang dari objek fungsi, Anda dapat dengan cepat menemukan solusi:
+(() => [,_,~1]).length
Ada beberapa turunan di sini, tetapi intinya adalah kita mengurangi jumlah argumen fungsi menjadi nol. Untuk melakukan ini, kami membutuhkan sebanyak
tiga permutasi : dua tanda kurung dan karakter
garis bawah , yang menjadi elemen dari array.
Ok, tapi mungkin sudah waktunya untuk berhenti mengabaikan penambahan (+) dan bitwise TIDAK (~) operator dalam alasan kami? Tampaknya aritmatika dalam masalah ini dapat bermain di tangan kita. Agar tidak beralih ke awal, berikut adalah ekspresi aslinya:
+(_ => [,,~1])().length
Untuk mulai dengan, kami menghitung
~ 1 .
Bitwise BUKAN dari
x akan kembali
- (x + 1) . Yaitu,
~ 1 = -2 . Dan kami juga memiliki operator tambahan dalam ekspresi, yang, seolah-olah, mengisyaratkan bahwa di tempat lain Anda perlu menemukan 2 lagi dan semuanya akan berhasil.
Baru-baru ini, kami mengingat "non-efek" dari elemen kosong terakhir dalam array literal pada ukurannya, yang berarti deuce kami ada di suatu tempat di sini:
[,,].length
Dan semuanya bertambah sangat berhasil: kita mendapatkan elemen
~ 1 dari array, mengurangi panjangnya menjadi 2, dan menambahkannya sebagai operan pertama untuk penambahan kita ke awal ekspresi:
~1+(_ => [,,])().length
Dengan demikian, kami telah mencapai tujuan dalam
dua permutasi !
Tetapi bagaimana jika ini bukan satu-satunya pilihan? Sebuah drum kecil ...
+(_ => [,,~1.])(),length
Ini juga memerlukan dua permutasi: satu titik setelah satu (ini dimungkinkan, karena tipe numerik hanya
angka ) dan koma sebelum
panjang . Tampaknya tidak masuk akal, tetapi "kadang-kadang" berhasil. Kenapa terkadang?
Dalam hal ini, ekspresi melalui
operator koma dibagi menjadi dua ekspresi dan hasilnya akan menjadi nilai yang dihitung dari yang kedua. Tapi ekspresi kedua hanya
panjang ! Faktanya adalah bahwa di sini kita mengakses nilai variabel dalam konteks global. Jika runtime adalah browser, maka
window.length . Dan objek
jendela memang memiliki
properti panjang yang mengembalikan jumlah bingkai pada halaman. Jika dokumen kita kosong, maka
panjangnya akan kembali 0. Ya, opsi dengan asumsi ... karena itu, mari kita memikirkan yang sebelumnya.
Dan berikut adalah beberapa opsi yang lebih menarik yang ditemukan (sudah untuk sejumlah permutasi yang berbeda):
(_ => [,,].length+~1)()
Tidak ada komentar. Adakah yang menemukan sesuatu yang lebih menyenangkan?
Motivasi
Tugas kuno yang bagus tentang “mengatur ulang satu atau dua pertandingan untuk membuat kotak”. Teka-teki terkenal untuk pengembangan logika dan pemikiran kreatif berubah menjadi obskurantisme dalam variasi JavaScript semacam itu. Apakah ini berguna? Lebih mungkin tidak daripada ya. Kami menyentuh banyak fitur bahasa, beberapa bahkan cukup konseptual, untuk sampai ke bagian bawah solusi. Namun, proyek nyata bukan tentang
panjang dari fungsi dan ekspresi menjadi steroid. Tugas ini diusulkan di konferensi sebagai semacam latihan adiktif untuk mengingat seperti apa JavaScript itu.
Combinatorics yang benar
Tidak semua kemungkinan jawaban dipertimbangkan, tetapi agar tidak ketinggalan apa pun, kami beralih ke
JavaScript yang sebenarnya ! Jika menarik untuk mencoba menemukannya sendiri, maka ada cukup tips di atas, dan lebih baik tidak membaca lebih lanjut.

Jadi, kita memiliki ekspresi 23 karakter, dalam catatan string yang akan kita permutasi. Secara total, kita perlu melakukan
n * (n - 1) = 506 permutasi dalam rekaman asli ekspresi untuk mendapatkan semua varian dengan satu simbol permutasi (seperti yang dipersyaratkan oleh kondisi masalah 1 dan 2).
Kami mendefinisikan fungsi
menggabungkan yang mengambil ekspresi input sebagai string dan predikat untuk menguji kesesuaian nilai yang diperoleh dengan mengeksekusi ekspresi ini. Fungsi ini akan memaksa semua opsi yang mungkin dan mengevaluasi ekspresi melalui
eval , menyimpan hasilnya dalam objek:
kunci - nilai yang diterima,
nilai - daftar mutasi ekspresi kami untuk nilai ini. Sesuatu seperti ini terjadi:
const combine = (expr, cond) => { let res = {}; let indices = [...Array(expr.length).keys()]; indices.forEach(i => indices.forEach(j => { if (i !== j) { let perm = replace(expr, i, j); try { let val = eval(perm); if (cond(val)) { (res[val] = res[val] || []).push(perm); } } catch (e) { } } })); return res; }
Di mana fungsi
ganti dari string yang dilewatkan dari ekspresi mengembalikan string baru dengan karakter disusun ulang dari posisi
i ke posisi
j . Dan sekarang, tanpa banyak rasa takut, kita akan melakukan:
console.dir(combine('+(_ => [,,~1])().length', val => typeof val === 'number' && !isNaN(val)));
Sebagai hasilnya, kami mendapat serangkaian solusi:
{ "1": [ "+(_ => [,,~1]()).length", "+((_ => [,,~1])).length", "+(_ =>( [,,~1])).length", "+(_ => ([,,~1])).length" ], "2": [ "+(_ => [,~1,])().length" ] "3": [/* ... */] "-4": [/* ... */] }
Solusi untuk 3 dan -4 tidak menarik bagi kami, untuk dua kami menemukan satu-satunya solusi, dan untuk unit ada kasus baru yang menarik dengan
[,, ~ 1] () . Mengapa tidak
TypeError: bla-bla bukan fungsi ? Dan semuanya sederhana: ungkapan ini tidak memiliki kesalahan untuk parser sintaksis, dan dalam runtime itu tidak dijalankan, karena fungsinya tidak dipanggil.
Seperti yang Anda lihat, dalam satu permutasi tidak mungkin untuk menyelesaikan masalah dengan nol. Bisakah kita mencoba dua? Memecahkan masalah dengan pencarian yang melelahkan, dalam hal ini kita akan memiliki kompleksitas
O (n ^ 4) dari panjang string dan "mengevaluasi" berkali-kali dikejar dan dihukum, tetapi rasa ingin tahu menang. Tidak sulit untuk
secara independen memperbaiki fungsi
kombinasi yang diberikan atau menulis penghitungan yang lebih baik yang mempertimbangkan fitur ekspresi tertentu.
console.dir(combine2('+(_ => [,,~1])().length', val => val === 0));
Pada akhirnya, set solusi untuk nol adalah:
{ "0": [ "+(_ => [,~.1])(),length", "+(_ => [,~1.])(),length", "~1+(_ => [,,])().length" ] }
Sangat mengherankan bahwa dalam alasan kami mengatur ulang titik setelah unit, tetapi lupa bahwa titik di depan unit juga mungkin: catat
0,1 dengan nol dihilangkan.
Jika Anda melakukan semua permutasi yang memungkinkan dari dua karakter masing-masing, Anda dapat menemukan bahwa ada cukup banyak jawaban untuk nilai dalam rentang dari 3 hingga -4:
{ "3": 198, "2": 35, "1": 150, "0": 3, "-1": 129, "-2": 118, "-3": 15, "-4": 64 }
Dengan demikian, jalur solusi
Countdown Expression pada dua permutasi bisa lebih lama dari jalur yang diusulkan dari 3 ke 0 pada satu permutasi.
Ini adalah bagian pertama dari analisis tugas kami di HolyJS 2019, dan segera yang kedua akan muncul, di mana kami akan mempertimbangkan solusi dari tes kedua dan ketiga. Kami akan menghubungi Anda!